概要
- Elasticsearchでは、フレーズ検索用クエリとして Match phrase query が用意されている
- ただ、フレーズとして指定したtermの間に別のtermが入ってしまうとヒットしなくなってしまう
- リファレンスを見たところ、span near query でフレーズ検索やフレーズ検索に近い検索を行うことが出来そうだったので、出来るかどうか今回確認する
span near queryの使い方
以下クエリを指定することで複数単語の距離を考慮した検索を行う。
GET _search
{
"query": {
"span_near": {
"clauses": [
{ "span_term": { "field": "value1" } },
{ "span_term": { "field": "value2" } },
{ "span_term": { "field": "value3" } }
],
"slop": 12,
"in_order": false
}
}
}
-
slop
検索にヒットさせたいterm間の最大距離(距離はterm数) -
in_order
指定されたterm順で検索にヒットさせたいかどうか
このクエリの slopに0、in_orderにtrue
を指定してすることでフレーズ検索を行うことが出来そう。
span near queryはElasitcsearch内部では、luceneのSpanNearQuery を呼び出して処理しているよう。
動作確認
1件だけデータを登録したインデックスを作成して、実際にspan near queryリクエストを行いヒットするかどうかを確認する。
インデックス作成、データ投入
インデックス作成(tokenizerはkuromojiを用いる)
PUT sports_news
{
"settings": {
"number_of_shards": 1,
"analysis": {
"tokenizer": {
"my_kuromoji_tokenizer": {
"type": "kuromoji_tokenizer",
"mode": "search",
"discard_punctuation": "false"
}
},
"analyzer": {
"my_analyzer": {
"type": "custom",
"tokenizer": "my_kuromoji_tokenizer"
}
}
}
},
"mappings": {
"properties": {
"title": {
"type": "text",
"index": true,
"analyzer": "my_analyzer"
}
}
}
}
データ投入
POST sports_news/_bulk
{"index":{"_id":1}}
{"title": "大谷、メジャー初外野守備"}
span_near検索
インデックスに登録したデータがヒットするかどうかを確認していく。
ここでは、ヒットしたかどうかを確認すべくCount API を用いる。
slopが5, in_orderがfalse
の場合
「大谷」と「外野」の距離は3なのでヒットする。
GET sports_news/_count
{
"query": {
"span_near": {
"clauses": [
{ "span_term": { "title": "大谷" } },
{ "span_term": { "title": "外野" } }
],
"slop": 5,
"in_order": false
}
}
}
レスポンス
{
"count" : 1,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
}
}
slopが5, in_orderがtrue
の場合
同様にヒット
GET sports_news/_count
{
"query": {
"span_near": {
"clauses": [
{ "span_term": { "title": "大谷" } },
{ "span_term": { "title": "外野" } }
],
"slop": 5,
"in_order": true
}
}
}
レスポンス
{
"count" : 1,
"_shards" : {
:
}
}
slopが2, in_orderがtrue
の場合
slopに2を指定するとヒットしなくなる。
GET sports_news/_count
{
"query": {
"span_near": {
"clauses": [
{ "span_term": { "title": "大谷" } },
{ "span_term": { "title": "外野" } }
],
"slop": 2,
"in_order": true
}
}
}
レスポンス
{
"count" : 0,
"_shards" : {
:
}
}
slopが5, in_orderがtrue
でtermの指定を逆にした場合
先程はslopに5を, in_orderにtrueを指定した場合はヒットしたのだが、termの順番を逆にするとヒットしなくなる
GET sports_news/_count
{
"query": {
"span_near": {
"clauses": [
{ "span_term": { "title": "外野" } },
{ "span_term": { "title": "大谷" } }
],
"slop": 5,
"in_order": true
}
}
}
レスポンス
{
"count" : 0,
"_shards" : {
:
}
}
フレーズ検索
以下のように slopに0、in_orderにtrue
を指定して期待通りフレーズ検索を行うことが出来た。
GET sports_news/_count
{
"query": {
"span_near": {
"clauses": [
{ "span_term": { "title": "外野" } },
{ "span_term": { "title": "守備" } }
],
"slop": 0,
"in_order": true
}
}
}
レスポンス
{
"count" : 1,
"_shards" : {
:
}
}
試しに、「初守備」を検索すべく以下のようなクエリで検索を行ったがヒットしなかった
GET sports_news/_count
{
"query": {
"span_near": {
"clauses": [
{ "span_term": { "title": "初" } },
{ "span_term": { "title": "守備" } }
],
"slop": 0,
"in_order": true
}
}
}
レスポンス
{
"count" : 0,
"_shards" : {
:
}
}
bool queryでの確認
実際に利用するときは以下のようにbool queryで利用することもあると思われるので確認してみたが、問題なくAnd検索が行われた。
GET sports_news/_count
{
"query": {
"bool": {
"must": [
{ "span_near": {
"clauses": [
{ "span_term": { "title": "大谷" } },
{ "span_term": { "title": "外野" } }
],
"slop": 3,
"in_order": true
} },
{ "term": {"title": "守備"} }
]
}
}
}
まとめ
- span near queryを用いることでフレーズ検索やフレーズ検索のような検索を行うことが出来た
- このqueryの性能面はまだ確認していないので気になるところ。文書内での出現位置も見るようになるため、AND検索よりは遅くなると思われる
- また、span queryにはspan near query以外にもqueryがあるようなので他でも同様のことが出来るか見てみたい