LoginSignup
3
1

More than 5 years have passed since last update.

Groongaで一時カラムと多段ドリルダウンを使って名寄せ集計する方法

Posted at

はじめに

全文検索エンジンのGroongaは全文検索エンジンの機能だけでなく、カラム指向型のデータストアとしての機能も備えています。

昨年は、表記揺れを直してグループ集計するために
編集距離などで文字列の類似度から代表の単語と表記ゆれした単語を事前に抽出しデータ投入時にデータを置き換えるというアプローチをとりました。(Groongaでのタグ検索と表記揺れとの戦い at Groonga Meatup 2015

今年は、Groongaのドリルダウンの結果を使って多段のドリルダウンをする機能や、検索結果を所定の演算を施した結果で一時的なオンメモリのカラムを作り、さらにその一時カラムに対してフィルター、ソート集計する機能など、様々なドリルダウンのための機能が追加・改良されました。

そこで、これらの機能を使って、グループ集計時に表記揺れや別名など、複数の単語を1つの単語に名寄せして集計する方法を紹介します。

通常のドリルダウン例

以下は通常のドリルダウンの例です。booksというテーブルに自動車および自転車というカテゴリーがあり、それぞれ1件であるという集計結果が得られています。

table_create categories TABLE_HASH_KEY ShortText
[[0,0.0,0.0],true]
table_create books TABLE_NO_KEY
[[0,0.0,0.0],true]
column_create books title COLUMN_SCALAR ShortText
[[0,0.0,0.0],true]
column_create books categories COLUMN_VECTOR categories
[[0,0.0,0.0],true]
load --table books
[
{"title": "自動車教習本", "categories": ["自動車"]},
{"title": "働く車", "categories": ["車"]},
{"title": "自転車の乗り方", "categories": ["自転車"]}
]
[[0,0.0,0.0],3]
select books \
  --output_columns title,categories \
  --drilldown categories
[
  [
    0,
    0.0,
    0.0
  ],
  [
    [
      [
        3
      ],
      [
        [
          "title",
          "ShortText"
        ],
        [
          "categories",
          "categories"
        ]
      ],
      [
        "自動車教習本",
        [
          "自動車"
        ]
      ],
      [
        "働く車",
        [
          "車"
        ]
      ],
      [
        "自転車の乗り方",
        [
          "自転車"
        ]
      ]
    ],
    [
      [
        3
      ],
      [
        [
          "_key",
          "ShortText"
        ],
        [
          "_nsubrecs",
          "Int32"
        ]
      ],
      [
        "自動車",
        1
      ],
      [
        "車",
        1
      ],
      [
        "自転車",
        1
      ]
    ]
  ]
]

ドリルダウン結果での一時カラムの作成

Groongaで一時カラム・動的カラムを使って計算結果でソートや集計する方法で紹介したGroongaの一時カラムはドリルダウン結果に対してもサポートされています。この一時カラムを使ってcategoriesテーブルのsynonymカラムに設定された別名に置き換えてみます。

column_create categories synonym COLUMN_SCALAR ShortText
[[0,0.0,0.0],true]
load --table categories
[
{"_key": "車", "synonym": "自動車"}
]
[[0,0.0,0.0],1]
select books \
  --drilldowns[category].keys categories \
  --drilldowns[category].columns[aggregated_category].stage initial \
  --drilldowns[category].columns[aggregated_category].type ShortText \
  --drilldowns[category].columns[aggregated_category].flags COLUMN_SCALAR \
  --drilldowns[category].columns[aggregated_category].value 'synonym || _key' \
  --drilldowns[category].output_columns _key,aggregated_category,_nsubrecs
[
  [
    0,
    0.0,
    0.0
  ],
  [
    [
      [
        3
      ],
      [
        [
          "_id",
          "UInt32"
        ],
        [
          "categories",
          "categories"
        ],
        [
          "title",
          "ShortText"
        ]
      ],
      [
        1,
        [
          "自動車"
        ],
        "自動車教習本"
      ],
      [
        2,
        [
          "車"
        ],
        "働く車"
      ],
      [
        3,
        [
          "自転車"
        ],
        "自転車の乗り方"
      ]
    ],
    {
      "category": [
        [
          3
        ],
        [
          [
            "_key",
            "ShortText"
          ],
          [
            "aggregated_category",
            "ShortText"
          ],
          [
            "_nsubrecs",
            "Int32"
          ]
        ],
        [
          "自動車",
          "自動車",
          1
        ],
        [
          "車",
          "自動車",
          1
        ],
        [
          "自転車",
          "自転車",
          1
        ]
      ]
    }
  ]
]

--drilldowns[category].columns[aggregated_category].value 'synonym || _key'とすることで、categoriesテーブルのsynonymカラムに値が設定されている場合はその値、設定されていない場合は元の_keyが一時カラムに代入されるようになります。これで、というカテゴリーは自動車というカテゴリーに設定されました。

なお、1レコードずつシーケンシャルに置き換えるため、グループ結果の種類数が莫大な場合は処理速度が厳しくなってくるでしょう。数万~数十万程度なら大丈夫かなという勘所です。

多段ドリルダウン

これでグループ結果のテーブルに別名に置き換えた一時カラムがメモリ上に設定されたので、この一時カラムを対象に再集計させます。
--drilldowns[label_name]とすることでドリルダウン結果のテーブルに名前を付けることができ、--drilldowns[label_name].tableでそのテーブル名を指定することができます。

select books \
  --drilldowns[category].keys categories \
  --drilldowns[category].columns[aggregated_category].stage initial \
  --drilldowns[category].columns[aggregated_category].type ShortText \
  --drilldowns[category].columns[aggregated_category].flags COLUMN_SCALAR \
  --drilldowns[category].columns[aggregated_category].value 'synonym || _key' \
  --drilldowns[category].output_columns _key,aggregated_category,_nsubrecs \
  --drilldowns[aggregated_category].table category \
  --drilldowns[aggregated_category].keys aggregated_category \
  --drilldowns[aggregated_category].calc_types SUM \
  --drilldowns[aggregated_category].calc_target _nsubrecs \
  --drilldowns[aggregated_category].output_columns _key,_sum
[
  [
    0,
    0.0,
    0.0
  ],
  [
    [
      [
        3
      ],
      [
        [
          "_id",
          "UInt32"
        ],
        [
          "categories",
          "categories"
        ],
        [
          "title",
          "ShortText"
        ]
      ],
      [
        1,
        [
          "自動車"
        ],
        "自動車教習本"
      ],
      [
        2,
        [
          "車"
        ],
        "働く車"
      ],
      [
        3,
        [
          "自転車"
        ],
        "自転車の乗り方"
      ]
    ],
    {
      "category": [
        [
          3
        ],
        [
          [
            "_key",
            "ShortText"
          ],
          [
            "aggregated_category",
            "ShortText"
          ],
          [
            "_nsubrecs",
            "Int32"
          ]
        ],
        [
          "自動車",
          "自動車",
          1
        ],
        [
          "車",
          "自動車",
          1
        ],
        [
          "自転車",
          "自転車",
          1
        ]
      ],
      "aggregated_category": [
        [
          2
        ],
        [
          [
            "_key",
            "ShortText"
          ],
          [
            "_sum",
            "Int64"
          ]
        ],
        [
          "自動車",
          2
        ],
        [
          "自転車",
          1
        ]
      ]
    }
  ]
]

これで、というカテゴリーを自動車というカテゴリーで集約した結果で自動車が2件、自転車が1件というグループ件数が得られました。

なお、グループ結果前にdrilldownではないcolumnsfilteredステージで調整してからdrilldownしても同様の結果が得られるのですが(*)、
この場合、グループ集約前のレコードを全部置き換える必要があるので、演算処理の回数を減らすためにグループ集約後の一時カラムで値を置き換える方が良いでしょう。

* 上記の例だとvectorなので自作関数をかかないと無理かもしれません。

おわりに

以上、多段ドリルダウンと一時カラムの使用例を紹介しました。

一時カラムのところは、自作のユーザ定義関数を作って値を置き換えたりすることもできます。また、ウィンドウ関数の仕組みなども追加されており、グループ処理の自由度は格段にあがっています。

Groongaを全文検索だけでなく、グループ集計の用途としても検討してみてはいかがでしょうか。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1