BloGroonga

2013-03-29

今日は肉の日ですね。

groonga 3.0.2リリース

groonga 3.0.2 をリリースしました!

それぞれの環境毎のインストール方法: インストール

はじめに、利用事例の記事を書いてくれる人募集のお知らせです。(再度)

これまでも、groonga.orgにて 利用事例 を紹介してきましたが、それとは別に、 gihyo.jp にてgroonga関連の記事を書くという企画が進行しています。

まだgroongaを知らない人にもWeb記事を通じて知ってもらいたいというのが動機です。

詳細は groonga普及のための協力のお願い を参照してください。実際に使っているユーザーのみなさんに書いてもらえたらいいなぁと思っています!

すでに何人かの方に応募いただいていますが、まだまだ募集中です。

sub_filter() 関数のサポート

今回のリリースでは sub_filter() 関数のサポートを追加しました。

どういった場合にこの関数を使うと便利なのかを具体例で紹介します。

お題は「ブログエントリの特定のコメントを抽出する」です。

この例で使用するスキーマ定義は以下の通りです。

  table_create Comment TABLE_PAT_KEY UInt32
  column_create Comment name COLUMN_SCALAR ShortText
  column_create Comment content COLUMN_SCALAR ShortText

  table_create Blog TABLE_PAT_KEY ShortText
  column_create Blog title COLUMN_SCALAR ShortText
  column_create Blog content COLUMN_SCALAR ShortText
  column_create Blog comments COLUMN_VECTOR Comment

  column_create Comment blog_comment_index COLUMN_INDEX Blog comments

  table_create Lexicon TABLE_PAT_KEY ShortText --default_tokenizer TokenBigram
  column_create Lexicon comment_content COLUMN_INDEX|WITH_POSITION Comment content
  column_create Lexicon comment_name COLUMN_INDEX|WITH_POSITION Comment name
  column_create Lexicon blog_content COLUMN_INDEX|WITH_POSITION Blog content

BlogテーブルにはCommentテーブルを関連づけています。 また、全文検索用のLexiconテーブルを用意しています。

例で使用するサンプルデータは以下の通りです。

  load --table Comment
  [
  {"_key": 1, "name": "A", "content": "groonga"},
  {"_key": 2, "name": "B", "content": "groonga"},
  {"_key": 3, "name": "C", "content": "rroonga"},
  {"_key": 4, "name": "A", "content": "mroonga"},
  ]

  load --table Blog
  [
  {"_key": "groonga's blog", "content": "content of groonga's blog", comments: [1, 2, 3]},
  {"_key": "mroonga's blog", "content": "content of mroonga's blog", comments: [2, 3, 4]},
  {"_key": "rroonga's blog", "content": "content of rroonga's blog", comments: [3]},
  ]

例えば、Aさんがgroongaについてコメントしたブログエントリのタイトルを抽出したいとしましょう。 Aさんのコメントは以下のようにして抽出することができます。

  select Comment --output_columns _key,name,content --query "name:@"A""

  [
    [2],
    [
      ["_key","UInt32"],["name","ShortText"],["content","ShortText"]
    ],
    [1,"A","groonga"],
    [4,"A","mroonga"]
  ]

Aさんはgroongaとmroongaについてコメントしていることがわかります。 では、AさんがgroongaについてコメントしているエントリをBlogテーブルから探してみましょう。 まずは、AさんがコメントしているエントリをBlogテーブルから探してみます。

  select Blog --output_columns _key --filter "comments.name @ "A""

  [
    [
      [2],
      [["_key","ShortText"]],
      ["groonga's blog"],
      ["mroonga's blog"]
    ]
  ]

"groonga's blog"と"mroonga's blog"に対してコメントしていることがわかりました。

では、AさんがgroongaについてコメントしているエントリをBlogテーブルから探してみましょう。 コメント本文に対する条件を追加して検索してみます。

  select Blog --output_columns _key --filter "comments.name @ "A" && comments.content @ "groonga""

  [
    [
      [2],
      [["_key","ShortText"]],
      ["groonga's blog"],
      ["mroonga's blog"]
    ]
  ]

条件を追加しているのに、結果は同じになりました。 "mroonga's blog"にはAさんがコメントしてはいますが、"groonga"ではなく"mroonga"についてです。 これは意図した結果ではありません。

いったいどういうことなのでしょうか。

--filter "comment.name @ "A" && comment.content @ "groonga""

実は、上記の条件を処理するときに、groongaでは以下のようにして判定しています。

  • comment.name @ "A"なものがあるか? => CommentテーブルにAさんのコメントがあるのでマッチ
  • comment.content @ "groonga"なものがあるか? => Commentテーブルに"groonga"があるのでマッチ

それぞれどれかひとつでもマッチしているかどうか、を見ています。 その結果をふまえて、両方をみたしているかで判定しているので、Blogの"mroonga's blog"エントリが抽出されています。

やりたいのは Aさんがgroongaについてコメントしたエントリを抽出することでした。 これは、"groonga's blog"だけが抽出されるというのが期待する結果です。

具体的に言うとCommentテーブルの以下のコメントデータを含むBlogテーブルのエントリを抽出したいわけです。

  {"_key": 1, "name": "A", "content": "groonga"},

この場合にCommentテーブルのnameカラムとcontentカラムにおいて、どれかひとつでもマッチしているものがあるかどうかで評価されるのは都合が良くありません。

こういうときに使えるのが、 sub_filter() 関数です。 comments のコンテキストで絞り込むことができます。

sub_filter を使うと上記目的に合致するクエリを以下のように書くことができます。

  select Blog --output_columns _key 
    --filter 'sub_filter(comments, "name @ "A" && content @ "groonga"")'

実行結果は以下のようになります。

  [
    [
      [1],
      [["_key","ShortText"]],
      ["groonga's blog"],
    ]
  ]

これで、意図した通り、Aさんがgroongaについてコメントしているエントリのみを抽出することができました。

query() 関数でクエリ展開のサポート

今回のリリースでは、 query() 関数でのクエリ展開をサポートしました。

selectコマンドの --query ではクエリ展開と重みづけの指定ができます。 2.1.2のリリースでは複数の query() 関数を select コマンドでサポートしたことで、複数のクエリごとに異なる重みづけを指定することができるようになりました。

ただし、 query() 関数ではクエリ展開できなかったので、アプリケーション側でクエリ展開相当のことをやってから query() 関数に渡さないことには --query と同じようには複数の全文検索を行うことができませんでした。

今回のリリースでは、@query()@ 関数でのクエリ展開をサポートしたことで、 --query でできていたクエリ展開と重みづけを複数の全文検索でも利用できるようになりました。

今までは query() 関数の構文は以下のようにカラムとクエリを受けつけるものでした。

 query("MATCH_COLUMNS", "QUERY")

今回クエリ展開をサポートするために引数を追加し、 QueryExpanderTSV と一緒に使えるようになりました。(置換テーブルを指定することもできます。)

 query("MATCH_COLUMNS", "QUERY", "QueryExpanderTSV")

QueryExpanderTSVによるクエリ展開については QueryExpanderTSV を参照してください。

置換テーブルの例については クエリー展開用の引数 を参照してください。

これにより、クエリ展開と重みづけを同時に指定したいものは --query に書かないといけないという制限が緩和されます。

例えば、以下のように置換テーブルと同等のことを書かないといけなかったクエリは

  select Users --match_columns "memo * 100" --query "memo:@groonga" 
    --query_expander Synonyms.synonym 
    --filter 'query("name * 10", "((...) OR (...) OR (...))")'

こんな風に自前で展開せずにすむようになります。

  select Users --match_columns "memo * 100" --query "memo:@groonga" 
    --query_expander Synonyms.synonym 
    --filter 'query("name * 10", "...", "Synonyms.synonym")'

さいごに

3.0.1からの詳細な変更点は 3.0.2リリース 2013/03/29 を確認してください。

それでは、groongaでガンガン検索してください!