groonga - オープンソースのカラムストア機能付き全文検索エンジン

7.4.17. select

7.4.17.1. 概要

select はテーブルから指定された条件にマッチするレコードを検索し、見つかったレコードを出力します。

select は最も重要なgroongaのコマンドです。groongaの力を最大限に活かすためには select を理解する必要があります。

7.4.17.2. 構文

select には多くの引数があります。必須の引数は table だけで、残りは省略できます。:

select table
       [match_columns=null]
       [query=null]
       [filter=null]
       [scorer=null]
       [sortby=null]
       [output_columns="_id, _key, *"]
       [offset=0]
       [limit=10]
       [drilldown=null]
       [drilldown_sortby=null]
       [drilldown_output_columns=null]
       [drilldown_offset=0]
       [drilldown_limit=10]
       [cache=yes]
       [match_escalation_threshold=0]
       [query_expansion=null]

7.4.17.3. 使い方

例を使いながら select の使い方を学びましょう。このセクションではよく使われる使い方を紹介します。

使い方を示すために使うスキーマ定義とサンプルデータは以下の通りです。

実行例:

table_create Entries TABLE_HASH_KEY ShortText
# [[0, 1337566253.89858, 0.000355720520019531], true]
column_create Entries content COLUMN_SCALAR Text
# [[0, 1337566253.89858, 0.000355720520019531], true]
column_create Entries n_likes COLUMN_SCALAR UInt32
# [[0, 1337566253.89858, 0.000355720520019531], true]
table_create Terms TABLE_PAT_KEY|KEY_NORMALIZE ShortText --default_tokenizer TokenBigram
# [[0, 1337566253.89858, 0.000355720520019531], true]
column_create Terms entries_key_index COLUMN_INDEX|WITH_POSITION Entries _key
# [[0, 1337566253.89858, 0.000355720520019531], true]
column_create Terms entries_content_index COLUMN_INDEX|WITH_POSITION Entries content
# [[0, 1337566253.89858, 0.000355720520019531], true]
load --table Entries
[
{"_key":    "The first post!",
 "content": "Welcome! This is my first post!",
 "n_likes": 5},
{"_key":    "Groonga",
 "content": "I started to use groonga. It's very fast!",
 "n_likes": 10},
{"_key":    "Mroonga",
 "content": "I also started to use mroonga. It's also very fast! Really fast!",
 "n_likes": 15},
{"_key":    "Good-bye Senna",
 "content": "I migrated all Senna system!",
 "n_likes": 3},
{"_key":    "Good-bye Tritonn",
 "content": "I also migrated all Tritonn system!",
 "n_likes": 3}
]
# [[0, 1337566253.89858, 0.000355720520019531], 5]

ブログエントリ用の Entries テーブルがあります。各エントリはタイトルと内容と「いいね!」数を持っています。タイトルは Entries のキーとします。内容は Entries.content カラムの値とします。「いいね!」数は Entries.n_likes カラムの値とします。

Entries._key カラムと Entries.content カラムには TokenBigram トークナイザーを使ったインデックスを作成します。そのため、 Entries._keyEntries.content は両方とも全文検索できます。

これで例を示すためのスキーマとデータの準備ができました。

7.4.17.3.1. 簡単な使い方

上記のスキーマとデータを使った一番簡単な使い方は以下の通りです。これは Entries テーブルのすべてのレコードを出力します。

実行例:

select Entries
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         5
#       ],
#       [
#         [
#           "_id",
#           "UInt32"
#         ],
#         [
#           "_key",
#           "ShortText"
#         ],
#         [
#           "content",
#           "Text"
#         ],
#         [
#           "n_likes",
#           "UInt32"
#         ]
#       ],
#       [
#         1,
#         "The first post!",
#         "Welcome! This is my first post!",
#         5
#       ],
#       [
#         2,
#         "Groonga",
#         "I started to use groonga. It's very fast!",
#         10
#       ],
#       [
#         3,
#         "Mroonga",
#         "I also started to use mroonga. It's also very fast! Really fast!",
#         15
#       ],
#       [
#         4,
#         "Good-bye Senna",
#         "I migrated all Senna system!",
#         3
#       ],
#       [
#         5,
#         "Good-bye Tritonn",
#         "I also migrated all Tritonn system!",
#         3
#       ]
#     ]
#   ]
# ]

どうしてこのコマンドがすべてのレコードを出力するのでしょうか?理由は2つです。1つ目の理由はこのコマンドが検索条件を何も指定していないからです。検索条件を指定しないとすべてのレコードがマッチします。2つ目の理由は全レコード数が5だからです。 select コマンドはデフォルトでは最大10レコードを出力します。この例では5レコードしかありません。これは10よりも少ないのですべてのレコードを出力します。

7.4.17.3.2. 検索条件

検索条件は query または filter で指定します。 queryfilter を両方指定することもできます。この場合は queryfilter の両方の条件にマッチしたレコードが出力されます。

7.4.17.3.2.1. 検索条件: query

query はWebページの検索ボックス用に用意されています。例えば、google.co.jpにあるような検索ボックスです。 query の検索条件はスペース区切りでキーワードを指定します。例えば、 検索 エンジン検索エンジン という2つのキーワードを含むレコードを検索します。

通常は query 引数は全文検索条件を指定するために使います。全文検索条件以外も指定できますが、その用途には filter 引数の方が向いています。

query 引数で全文検索条件を指定する場合は、 match_columns 引数と一緒に使います。 match_columns はどのカラムまたはインデックスを使って query を検索するかを指定します。

以下は簡単な query の使用例です。

実行例:

select Entries --match_columns content --query fast
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         2
#       ],
#       [
#         [
#           "_id",
#           "UInt32"
#         ],
#         [
#           "_key",
#           "ShortText"
#         ],
#         [
#           "content",
#           "Text"
#         ],
#         [
#           "n_likes",
#           "UInt32"
#         ]
#       ],
#       [
#         2,
#         "Groonga",
#         "I started to use groonga. It's very fast!",
#         10
#       ],
#       [
#         3,
#         "Mroonga",
#         "I also started to use mroonga. It's also very fast! Really fast!",
#         15
#       ]
#     ]
#   ]
# ]

この select コマンドは Entries テーブルの中から content カラムの値に fast を含んでいるレコードを検索します。

query はクエリ構文という構文を使いますが、詳細はここでは説明しません。詳細は クエリ構文 を参照してください。

7.4.17.3.2.2. 検索条件: filter

filter は複雑な検索条件を指定するために用意されています。ECMAScriptのような構文で filter に検索条件を指定します。

以下は簡単な filter の使用例です。

実行例:

select Entries --filter 'content @ "fast" && _key == "Groonga"'
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         1
#       ],
#       [
#         [
#           "_id",
#           "UInt32"
#         ],
#         [
#           "_key",
#           "ShortText"
#         ],
#         [
#           "content",
#           "Text"
#         ],
#         [
#           "n_likes",
#           "UInt32"
#         ]
#       ],
#       [
#         2,
#         "Groonga",
#         "I started to use groonga. It's very fast!",
#         10
#       ]
#     ]
#   ]
# ]

この select コマンドは Entries テーブルの中の content カラムの値に fast という単語を含んでいて、かつ、 _keyGroonga のレコードを検索します。このコマンドの中には @&&== という3つの演算子があります。 @ は全文検索用の演算子です。 &&== はECMAScriptと同じ意味です。 && が論理積用の演算子で == が等価演算子です。

filter にはもっと演算子や構文があります。例えば、 (...) を使った検索条件のグループ化などです。しかし、ここでは詳細は説明しません。詳細は スクリプト構文 を参照してください。

7.4.17.3.3. ページング

offsetlimit を指定することで出力されるレコードの範囲を指定できます。以下は2番目のレコードだけを出力する例です。

実行例:

select Entries --offset 1 --limit 1
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         5
#       ],
#       [
#         [
#           "_id",
#           "UInt32"
#         ],
#         [
#           "_key",
#           "ShortText"
#         ],
#         [
#           "content",
#           "Text"
#         ],
#         [
#           "n_likes",
#           "UInt32"
#         ]
#       ],
#       [
#         2,
#         "Groonga",
#         "I started to use groonga. It's very fast!",
#         10
#       ]
#     ]
#   ]
# ]

offset は0基点です。 --offset 1 は2番目以降のレコードを出力するという意味になります。

limit は出力レコード数の最大値を指定します。 --limit 1 は多くても1レコードを出力するという意味になります。もし、1つもレコードがマッチしていなければ select コマンドはどのレコードも出力しません。

7.4.17.3.4. 全レコード数

--limit 0 を使うとレコードの内容は取得せずに全レコード数だけを取得できます。

実行例:

select Entries --limit 0
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         5
#       ],
#       [
#         [
#           "_id",
#           "UInt32"
#         ],
#         [
#           "_key",
#           "ShortText"
#         ],
#         [
#           "content",
#           "Text"
#         ],
#         [
#           "n_likes",
#           "UInt32"
#         ]
#       ]
#     ]
#   ]
# ]

--limit 0 はマッチしたレコード数だけを取得したいときにも便利です。

7.4.17.4. 引数

このセクションではすべての引数について説明します。引数はカテゴリわけしています。

7.4.17.4.1. 必須引数

table だけが必須の引数です。

7.4.17.4.1.1. table

検索対象のテーブルを指定します。 table は必ず指定しなければいけません。

存在しないテーブルを指定するとエラーが返ります。

実行例:

select Nonexistent
# [
#   [
#     -22,
#     1337566253.89858,
#     0.000355720520019531,
#     "invalid table name: <Nonexistent>",
#     [
#       [
#         "grn_select",
#         "proc.c",
#         542
#       ]
#     ]
#   ]
# ]

7.4.17.4.3. 高度な検索のための引数

7.4.17.4.3.1. match_escalation_threshold

検索方法をエスカレーションするかどうかを決定するための閾値を指定します。この閾値はマッチしたレコード数との比較に使われます。マッチしたレコード数がこの閾値以下の場合は検索方法をエスカレーションします。検索方法のエスカレーションについては 検索 を参照してください。

デフォルトの閾値は0です。これは1つもレコードがマッチしなかったときだけ検索方法をエスカレーションするということです。

デフォルトの閾値は以下の方法でカスタマイズできます。

  • configureの --with-match-escalation-threshold オプション
  • groongaコマンドの --match-escalation-threshold オプション
  • 設定ファイルの match-escalation-threshold 設定項目

以下は簡単な match_escalation_threshold の使用例です。最初の selectmatch_escalation_threshold 引数がありません。2番目の selectmatch_escalation_threshold 引数があります。

実行例:

select Entries --match_columns content --query groo
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         1
#       ],
#       [
#         [
#           "_id",
#           "UInt32"
#         ],
#         [
#           "_key",
#           "ShortText"
#         ],
#         [
#           "content",
#           "Text"
#         ],
#         [
#           "n_likes",
#           "UInt32"
#         ]
#       ],
#       [
#         2,
#         "Groonga",
#         "I started to use groonga. It's very fast!",
#         10
#       ]
#     ]
#   ]
# ]
select Entries --match_columns content --query groo --match_escalation_threshold -1
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         0
#       ],
#       [
#         [
#           "_id",
#           "UInt32"
#         ],
#         [
#           "_key",
#           "ShortText"
#         ],
#         [
#           "content",
#           "Text"
#         ],
#         [
#           "n_likes",
#           "UInt32"
#         ]
#       ]
#     ]
#   ]
# ]

The first select command searches records that contain a word groo in content column value from Entries table. But no records are matched because the TokenBigram tokenizer tokenizes groonga to groonga not gr|ro|oo|on|ng|ga. (The TokenBigramSplitSymbolAlpha tokenizer tokenizes groonga to gr|ro|oo|on|ng|ga. See Tokenizers for details.) It means that groonga is indexed but groo isn't indexed. So no records are matched against groo by exact match. In the case, the search storategy escalation is used because the number of matched records (0) is equal to match_escalation_threshold (0). One record is matched against groo by unsplit search.

2番目の select コマンドも Entries テーブルから content カラムの値に groo という単語を含むレコードを検索します。そして、この select コマンドもマッチしません。この場合、マッチしたレコード数(0)が match_escalation_threshold (-1)より大きいので、検索方法をエスカレーションしません。そして、1つもレコードがマッチしません。

7.4.17.4.3.2. query_expansion

クエリ展開用の引数です。クエリ展開はクエリ中の特定の単語を別の単語に置換します。通常は類義語検索に使います。

query 引数の値を置換するために使うカラムを指定します。この引数の値の書式は「 ${TABLE}.${COLUMN} 」です。例えば、 「 Terms.synonym 」は Terms テーブルの synonym カラムを指定しています。

クエリ展開用のテーブルを「置換テーブル」と呼びます。置換テーブルのキーは ShortText にしてください。そのため、配列テーブル( TABLE_NO_KEY )は置換テーブルに使うことはできません。なぜなら、配列テーブルにはキーがないからです。

クエリ展開用のカラムを「置換カラム」と呼びます。置換カラムの値の型は ShortText にしてください。カラムの種類はベクター( COLUMN_VECTOR )にしてください。

クエリ展開はクエリの中にある置換テーブルのキーを置換カラムの値で置換します。 query の中にある単語が置換テーブルのキーだったら、キーに対応する置換カラムの値でその単語を置換します。置換は再帰的に実行しません。これは、置換されたクエリ内に置換対象の単語があっても置換されないということです。

以下は query_expansion の簡単な使用例を示すためのサンプル置換テーブルです。

実行例:

table_create Thesaurus TABLE_PAT_KEY|KEY_NORMALIZE ShortText
# [[0, 1337566253.89858, 0.000355720520019531], true]
column_create Thesaurus synonym COLUMN_VECTOR ShortText
# [[0, 1337566253.89858, 0.000355720520019531], true]
load --table Thesaurus
[
{"_key": "mroonga", "synonym": ["mroonga", "tritonn", "groonga mysql"]},
{"_key": "groonga", "synonym": ["groonga", "senna"]}
]
# [[0, 1337566253.89858, 0.000355720520019531], 2]

Thesaurus 置換テーブルは2つの類義語があります。 "mroonga""groonga" です。ユーザが "mroonga" で検索すると、groongaは "((mroonga) OR (tritonn) OR (groonga mysql))" で検索します。ユーザーが "groonga" で検索すると、groongaは "((groonga) OR (senna))" で検索します。通常、置換テーブルには KEY_NORMALIZE フラグをつけた方がよいです。このフラグを使うと、置換対象の単語が大文字小文字区別せずにマッチするようになります。

これらの類義語の値の中に "mroonga""groonga" といったキーの値も含まれていることに注意してください。このように類義語にキーの値も含めることを推奨します。もしキーの値を含めないと、置換した値には元の置換対象の値が含まれません。通常、元の値が含まれていた方がよい検索結果になります。もし、検索してほしくない単語がある場合は、元の単語を含めないでください。例えば、空のベクター値を指定することで「ストップワード」機能を実現することもできます。

以下は簡単な query_expansion の使用例です。

実行例:

select Entries --match_columns content --query "mroonga"
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         1
#       ],
#       [
#         [
#           "_id",
#           "UInt32"
#         ],
#         [
#           "_key",
#           "ShortText"
#         ],
#         [
#           "content",
#           "Text"
#         ],
#         [
#           "n_likes",
#           "UInt32"
#         ]
#       ],
#       [
#         3,
#         "Mroonga",
#         "I also started to use mroonga. It's also very fast! Really fast!",
#         15
#       ]
#     ]
#   ]
# ]
select Entries --match_columns content --query "mroonga" --query_expansion Thesaurus.synonym
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         2
#       ],
#       [
#         [
#           "_id",
#           "UInt32"
#         ],
#         [
#           "_key",
#           "ShortText"
#         ],
#         [
#           "content",
#           "Text"
#         ],
#         [
#           "n_likes",
#           "UInt32"
#         ]
#       ],
#       [
#         3,
#         "Mroonga",
#         "I also started to use mroonga. It's also very fast! Really fast!",
#         15
#       ],
#       [
#         5,
#         "Good-bye Tritonn",
#         "I also migrated all Tritonn system!",
#         3
#       ]
#     ]
#   ]
# ]
select Entries --match_columns content --query "((mroonga) OR (tritonn) OR (groonga mysql))"
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         2
#       ],
#       [
#         [
#           "_id",
#           "UInt32"
#         ],
#         [
#           "_key",
#           "ShortText"
#         ],
#         [
#           "content",
#           "Text"
#         ],
#         [
#           "n_likes",
#           "UInt32"
#         ]
#       ],
#       [
#         3,
#         "Mroonga",
#         "I also started to use mroonga. It's also very fast! Really fast!",
#         15
#       ],
#       [
#         5,
#         "Good-bye Tritonn",
#         "I also migrated all Tritonn system!",
#         3
#       ]
#     ]
#   ]
# ]

最初の select コマンドはクエリ展開を使いません。そのため、 "tritonn" という単語を含んでいるレコードは見つかりません。2番目の select コマンドはクエリ展開を使っています。そのため、 "tritonn" という単語を含んでいるレコードが見つかります。3番目の select コマンドはクエリ展開を使っていませんが、2番目の select コマンドと同じ結果になります。これは、3番目の select コマンドは展開後のクエリを使っているからです。

それぞれの置換する値は (...)OR といった クエリ構文 を使えます。これらの構文を使うことにより複雑な置換をすることができます。

以下はクエリ構文を使った複雑な置換の使用例です。

実行例:

load --table Thesaurus
[
{"_key": "popular", "synonym": ["popular", "n_likes:>=10"]}
]
# [[0, 1337566253.89858, 0.000355720520019531], 1]
select Entries --match_columns content --query "popular" --query_expansion Thesaurus.synonym
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         2
#       ],
#       [
#         [
#           "_id",
#           "UInt32"
#         ],
#         [
#           "_key",
#           "ShortText"
#         ],
#         [
#           "content",
#           "Text"
#         ],
#         [
#           "n_likes",
#           "UInt32"
#         ]
#       ],
#       [
#         2,
#         "Groonga",
#         "I started to use groonga. It's very fast!",
#         10
#       ],
#       [
#         3,
#         "Mroonga",
#         "I also started to use mroonga. It's also very fast! Really fast!",
#         15
#       ]
#     ]
#   ]
# ]

この load コマンドは新しく "popular" という類義語を登録しています。これは ((popular) OR (n_likes:>=10)) に置換されます。置換されたクエリは、「popular」というのは「popular」という単語を含んでいるか10以上の「いいね!」数を持つエントリという意味になります。

この select コマンドは Entries テーブルの中から n_likes カラムの値が 10 以上のレコードを出力します。

7.4.17.5. 返値

TODO: write in English and add example.

以下のようなjson形式で値が返却されます。

[[リターンコード, 処理開始時間, 処理時間], [検索結果, ドリルダウン結果]]

リターンコード

grn_rcに対応する数値が返されます。0(GRN_SUCCESS)以外の場合は、続いてエラー内容を示す 文字列が返されます。

処理開始時間

処理を開始した時間について、1970年1月1日0時0分0秒を起点とした秒数を小数で返します。

処理時間

処理にかかった秒数を返します。

検索結果

drilldown条件が実行される前の検索結果が以下のように出力されます。:

[[検索件数], [[カラム名1,カラム型1],..], 検索結果1,..]

検索件数

検索件数が出力されます。

カラム名n

output_columnsに指定された条件に従って、対象となるカラム名が出力されます。

カラム型n

output_columnsに指定された条件に従って、対象となるカラム型が出力されます。

検索結果n

output_columns, offset, limitによって指定された条件に従って各レコードの値が出力されます。

drilldown結果

drilldown処理の結果が以下のように出力されます。:

[[[件数], [[カラム名1,カラム型1],..], 検索結果1,..],..]

件数

drilldownに指定されたカラムの値の異なり数が出力されます。

カラム名n

drilldown_output_columnsに指定された条件に従って、対象となるカラム名が出力されます。

カラム型n

drilldown_output_columnsに指定された条件に従って、対象となるカラム型が出力されます。

ドリルダウン結果n

drilldown_output_columns, drilldown_offset, drilldown_limitによって指定された条件に従って各レコードの値が出力されます。