4.7. match_columnsパラメータ

4.7.1. 複数のカラムに対する全文検索

Groongaでは、複数のカラムを対象とした全文検索を行うことができます。例えば、ブログのテーブルで、タイトルと内容とがそれぞれ別のカラムに入ったものがあるとしましょう。「タイトルもしくは内容に特定の単語を含む」検索を行いたいとします。

この場合、2つのインデックス作成方式があります。1つは、それぞれのカラムに1つずつインデックスを付与する方式です。もう1つは、複数のカラムに対して1つのインデックスを付与する方式です。Groongaでは、どちらの形式のインデックスが存在している場合でも、同一の記法で全文検索を行うことができます。

4.7.1.1. カラムごとにインデックスを付与する場合

カラムごとにインデックスを作成する方法はこの通りです。

まず、 Blog1 テーブルを作成し、 title カラムと message カラムを追加します。 title カラムにブログのタイトルを保存し、 message カラムにブログの本文を保存します。

インデックス用の IndexBlog1 テーブルも作り、 title カラムのインデックス用に index_title カラム、 message カラムのインデックス用に index_message カラムと、それぞれ1カラムごとに1つずつ追加しています。

実行例:

table_create --name Blog1 --flags TABLE_HASH_KEY --key_type ShortText
# [[0, 1337566253.89858, 0.000355720520019531], true]
column_create --table Blog1 --name title --flags COLUMN_SCALAR --type ShortText
# [[0, 1337566253.89858, 0.000355720520019531], true]
column_create --table Blog1 --name message --flags COLUMN_SCALAR --type ShortText
# [[0, 1337566253.89858, 0.000355720520019531], true]
table_create --name IndexBlog1 --flags TABLE_PAT_KEY --key_type ShortText --default_tokenizer TokenBigram --normalizer NormalizerAuto
# [[0, 1337566253.89858, 0.000355720520019531], true]
column_create --table IndexBlog1 --name index_title --flags COLUMN_INDEX|WITH_POSITION --type Blog1 --source title
# [[0, 1337566253.89858, 0.000355720520019531], true]
column_create --table IndexBlog1 --name index_message --flags COLUMN_INDEX|WITH_POSITION --type Blog1 --source message
# [[0, 1337566253.89858, 0.000355720520019531], true]
load --table Blog1
[
{"_key":"grn1","title":"Groonga test","message":"Groonga message"},
{"_key":"grn2","title":"baseball result","message":"rakutan eggs 4 - 4 Groonga moritars"},
{"_key":"grn3","title":"Groonga message","message":"none"}
]
# [[0, 1337566253.89858, 0.000355720520019531], 3]

match_columns オプションで、検索対象のカラムを複数指定することが出来ます。検索する文字列は query オプションで指定します。これを使うことで、タイトルと本文を全文検索することができます。

実際にブログエントリを検索してみましょう。

実行例:

select --table Blog1 --match_columns title||message --query groonga
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         3
#       ],
#       [
#         [
#           "_id",
#           "UInt32"
#         ],
#         [
#           "_key",
#           "ShortText"
#         ],
#         [
#           "message",
#           "ShortText"
#         ],
#         [
#           "title",
#           "ShortText"
#         ]
#       ],
#       [
#         1,
#         "grn1",
#         "Groonga message",
#         "Groonga test"
#       ],
#       [
#         3,
#         "grn3",
#         "none",
#         "Groonga message"
#       ],
#       [
#         2,
#         "grn2",
#         "rakutan eggs 4 - 4 Groonga moritars",
#         "baseball result"
#       ]
#     ]
#   ]
# ]
select --table Blog1 --match_columns title||message --query message
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         2
#       ],
#       [
#         [
#           "_id",
#           "UInt32"
#         ],
#         [
#           "_key",
#           "ShortText"
#         ],
#         [
#           "message",
#           "ShortText"
#         ],
#         [
#           "title",
#           "ShortText"
#         ]
#       ],
#       [
#         3,
#         "grn3",
#         "none",
#         "Groonga message"
#       ],
#       [
#         1,
#         "grn1",
#         "Groonga message",
#         "Groonga test"
#       ]
#     ]
#   ]
# ]
select --table Blog1 --match_columns title --query message
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         1
#       ],
#       [
#         [
#           "_id",
#           "UInt32"
#         ],
#         [
#           "_key",
#           "ShortText"
#         ],
#         [
#           "message",
#           "ShortText"
#         ],
#         [
#           "title",
#           "ShortText"
#         ]
#       ],
#       [
#         3,
#         "grn3",
#         "none",
#         "Groonga message"
#       ]
#     ]
#   ]
# ]

4.7.1.2. 複数のカラムにまたがったインデックスを付与する場合

Groongaでは複数のカラムにまたがったインデックスもサポートしています。

インデックスカラムが1つしかないというのが違いです。 titlemessage の2つのカラムに対するインデックスが共通になっています。

共通のインデックスを用いても、 title カラムのみでの検索、 message カラムのみでの検索、 title もしくは message カラムでの検索、全ての検索を行うことができます。

実行例:

table_create --name Blog2 --flags TABLE_HASH_KEY --key_type ShortText
# [[0, 1337566253.89858, 0.000355720520019531], true]
column_create --table Blog2 --name title --flags COLUMN_SCALAR --type ShortText
# [[0, 1337566253.89858, 0.000355720520019531], true]
column_create --table Blog2 --name message --flags COLUMN_SCALAR --type ShortText
# [[0, 1337566253.89858, 0.000355720520019531], true]
table_create --name IndexBlog2 --flags TABLE_PAT_KEY --key_type ShortText --default_tokenizer TokenBigram --normalizer NormalizerAuto
# [[0, 1337566253.89858, 0.000355720520019531], true]
column_create --table IndexBlog2 --name index_blog --flags COLUMN_INDEX|WITH_POSITION|WITH_SECTION --type Blog2 --source title,message
# [[0, 1337566253.89858, 0.000355720520019531], true]
load --table Blog2
[
{"_key":"grn1","title":"Groonga test","message":"Groonga message"},
{"_key":"grn2","title":"baseball result","message":"rakutan eggs 4 - 4 Groonga moritars"},
{"_key":"grn3","title":"Groonga message","message":"none"}
]
# [[0, 1337566253.89858, 0.000355720520019531], 3]

実際に前と同じ例で検索してみましょう。結果は上の例と同じになります。

実行例:

select --table Blog2 --match_columns title||message --query groonga
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         3
#       ],
#       [
#         [
#           "_id",
#           "UInt32"
#         ],
#         [
#           "_key",
#           "ShortText"
#         ],
#         [
#           "message",
#           "ShortText"
#         ],
#         [
#           "title",
#           "ShortText"
#         ]
#       ],
#       [
#         1,
#         "grn1",
#         "Groonga message",
#         "Groonga test"
#       ],
#       [
#         2,
#         "grn2",
#         "rakutan eggs 4 - 4 Groonga moritars",
#         "baseball result"
#       ],
#       [
#         3,
#         "grn3",
#         "none",
#         "Groonga message"
#       ]
#     ]
#   ]
# ]
select --table Blog2 --match_columns title||message --query message
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         2
#       ],
#       [
#         [
#           "_id",
#           "UInt32"
#         ],
#         [
#           "_key",
#           "ShortText"
#         ],
#         [
#           "message",
#           "ShortText"
#         ],
#         [
#           "title",
#           "ShortText"
#         ]
#       ],
#       [
#         1,
#         "grn1",
#         "Groonga message",
#         "Groonga test"
#       ],
#       [
#         3,
#         "grn3",
#         "none",
#         "Groonga message"
#       ]
#     ]
#   ]
# ]
select --table Blog2 --match_columns title --query message
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         1
#       ],
#       [
#         [
#           "_id",
#           "UInt32"
#         ],
#         [
#           "_key",
#           "ShortText"
#         ],
#         [
#           "message",
#           "ShortText"
#         ],
#         [
#           "title",
#           "ShortText"
#         ]
#       ],
#       [
#         3,
#         "grn3",
#         "none",
#         "Groonga message"
#       ]
#     ]
#   ]
# ]

注釈

"インデックスはどちらがよい方法なのか"と疑問に思うかもしれません。それは場合によります。

  • カラムごとのインデックス - マルチカラムインデックスよりも更新性能が良い傾向があります。一方、ディスク使用効率はあまり良くありません。

  • マルチカラムインデックス - バッファを共有するためディスク使用効率が良いです。一方、更新性能があまり良くありません。

4.7.2. インデックス名を指定した全文検索

Groongaは、インデックス名を指定した全文検索もサポートしています。

このセクションでは、インデックス名を指定した全文検索の方法を学びます。

インデックス名の指定について具体的な例を示します。

実行例:

table_create Entries TABLE_HASH_KEY ShortText
# [[0, 1337566253.89858, 0.000355720520019531], true]
column_create Entries title COLUMN_SCALAR ShortText
# [[0, 1337566253.89858, 0.000355720520019531], true]
column_create Entries body COLUMN_SCALAR ShortText
# [[0, 1337566253.89858, 0.000355720520019531], true]
load --table Entries
[
{"_key": "http://example.com/entry1", "title":"Hello Groonga.", "body":"This is my first entry."},
{"_key": "http://example.com/entry2", "title":"Hello world.", "body":"I love Groonga!"},
{"_key": "http://example.com/entry3", "title":"Hello Mroonga, bye Groonga.", "body":"I use Mroonga."},
{"_key": "http://example.com/entry4", "title":"Say, Hello Groonga!", "body":"I'm back."}
]
# [[0, 1337566253.89858, 0.000355720520019531], 4]
table_create Terms TABLE_PAT_KEY ShortText --default_tokenizer TokenBigram --normalizer NormalizerAuto
# [[0, 1337566253.89858, 0.000355720520019531], true]
column_create Terms entries_title COLUMN_INDEX|WITH_POSITION Entries title
# [[0, 1337566253.89858, 0.000355720520019531], true]
column_create Terms entries_body COLUMN_INDEX|WITH_POSITION Entries body
# [[0, 1337566253.89858, 0.000355720520019531], true]
column_create Terms entries_whole COLUMN_INDEX|WITH_POSITION|WITH_SECTION Entries title,body
# [[0, 1337566253.89858, 0.000355720520019531], true]

titlebody カラムを持つテーブルがあります。また、語彙表は titlebody のカラムのインデックスを持っています。

語彙表には、3つのインデックスカラムがあります。

  • entries_title: title のインデックスカラム

  • entries_body: body のインデックスカラム

  • entries_whole: titlebody のインデックスカラム

特定のデータカラムと結びついたインデックスカラムを指定した場合は、暗黙的にそのデータカラムだけを検索します。

例えば、 title または body だけを検索したい場合は、 Terms.entries_title または Terms.entries_body をインデックスカラムに指定します。

実行例:

select --table Entries --output_columns title --match_columns Terms.entries_title --query "Groonga"
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         3
#       ],
#       [
#         [
#           "title",
#           "ShortText"
#         ]
#       ],
#       [
#         "Hello Groonga."
#       ],
#       [
#         "Hello Mroonga, bye Groonga."
#       ],
#       [
#         "Say, Hello Groonga!"
#       ]
#     ]
#   ]
# ]

この例では、Terms.entries_title をインデックスとして title カラムを対象に"Groonga"を検索します。

実行例:

select --table Entries --output_columns body --match_columns Terms.entries_body --query "Groonga"
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         1
#       ],
#       [
#         [
#           "body",
#           "ShortText"
#         ]
#       ],
#       [
#         "I love Groonga!"
#       ]
#     ]
#   ]
# ]

この例では、 Terms.entries_whole をインデックスとして、 body カラムを対象に"Groonga"を検索します。

複数カラムにまたがるインデックスは、インデックスカラムの後にデータカラム名を指定することができます。これは、どのインデックスを使ってどのデータカラムを検索するかを明示しています。

例えば、 entries_whole インデックスが付いた title または body を検索する場合、 Terms.entries_whole.title または Terms.entries_whole.body を指定します。 Terms.entries_whole をインデックスとして使い、 title カラムまたは、 body カラムを対象に検索します。

実行例:

select --table Entries --output_columns title --match_columns Terms.entries_whole.title --query "Groonga"
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         3
#       ],
#       [
#         [
#           "title",
#           "ShortText"
#         ]
#       ],
#       [
#         "Hello Groonga."
#       ],
#       [
#         "Hello Mroonga, bye Groonga."
#       ],
#       [
#         "Say, Hello Groonga!"
#       ]
#     ]
#   ]
# ]

この例では、 Terms.entries_whole をインデックスとして、 title カラムを対象に"Groonga"を検索します。

実行例:

select --table Entries --output_columns body --match_columns Terms.entries_whole.body --query "Groonga"
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         1
#       ],
#       [
#         [
#           "body",
#           "ShortText"
#         ]
#       ],
#       [
#         "I love Groonga!"
#       ]
#     ]
#   ]
# ]

この例では、 Terms.entries_whole をインデックスとして、 body カラムを対象に"Groonga"を検索します。

4.7.4. インデックスの重み

もし、インデックスカラムがデータカラムに対して作成されていたら、インデックスの重みを使って検索できます。例えば、 Groonga が重要なキーワードとしてブログエントリに含まれている Blog1 テーブルをインデックスの重みを使って検索してみましょう。一般的に重要なキーワードはブログのタイトルに含まれます。そのため、 title カラムが Groonga を含んでいればそのスコア( _score )が message カラムにくらべて高くないといけません。インデックスの重みはそのような目的に使われます。

次は、tittlemessage カラムに重要なキーワードとして Groonga が含まれているブログエントリを検索する例です。

サンプルのスキーマとデータは カラムごとにインデックスを付与する場合 と同じです。

実行例:

select --table Blog1 --match_columns 'IndexBlog1.index_title * 10 || IndexBlog1.index_message' --query 'Groonga' --output_columns "_id, _score, *"
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         3
#       ],
#       [
#         [
#           "_id",
#           "UInt32"
#         ],
#         [
#           "_score",
#           "Int32"
#         ],
#         [
#           "message",
#           "ShortText"
#         ],
#         [
#           "title",
#           "ShortText"
#         ]
#       ],
#       [
#         1,
#         11,
#         "Groonga message",
#         "Groonga test"
#       ],
#       [
#         3,
#         10,
#         "none",
#         "Groonga message"
#       ],
#       [
#         2,
#         1,
#         "rakutan eggs 4 - 4 Groonga moritars",
#         "baseball result"
#       ]
#     ]
#   ]
# ]

上記の例では、 'IndexBlog1.index_title * 10 || IndexBlog1.index_message'--match_columns に指定されています。これは title カラム( IndexBlog1.index_title インデックスを使って検索)が Groonga にマッチしたら、その重みは10倍されます。もし message カラム( IndexBlog1.index_message インデックスを使って検索)が Groonga にマッチしたら、その重みは1(既定値)になります。もし Groongatitlemessage カラムの両方にマッチしたら、その重みはこの場合11(10 + 1)になります。

結果として、 Groonga test ブログエントリがリストの最初に表示されます。