LoginSignup
2
0

More than 3 years have passed since last update.

ElasticsearchのSpan Near Query

Last updated at Posted at 2021-04-26

概要

  • Elasticsearchでは、フレーズ検索用クエリとして Match phrase query が用意されている
  • ただ、フレーズとして指定したtermの間に別のtermが入ってしまうとヒットしなくなってしまう
  • リファレンスを見たところ、span near query でフレーズ検索やフレーズ検索に近い検索を行うことが出来そうだったので、出来るかどうか今回確認する

span near queryの使い方

以下クエリを指定することで複数単語の距離を考慮した検索を行う。

kibana(devtool)
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を用いる)

kibana(devtool)
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"
      }
    }
  }
}

データ投入

kibana(devtool)
POST sports_news/_bulk
{"index":{"_id":1}}
{"title": "大谷、メジャー初外野守備"}

span_near検索

インデックスに登録したデータがヒットするかどうかを確認していく。
ここでは、ヒットしたかどうかを確認すべくCount API を用いる。

slopが5, in_orderがfalse の場合

「大谷」と「外野」の距離は3なのでヒットする。

kibana(devtool)
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 の場合

同様にヒット

kibana(devtool)
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を指定するとヒットしなくなる。

kibana(devtool)
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の順番を逆にするとヒットしなくなる

kibana(devtool)
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 を指定して期待通りフレーズ検索を行うことが出来た。

kibana(devtool)
GET sports_news/_count
{
  "query": {
    "span_near": {
      "clauses": [
        { "span_term": { "title": "外野" } },
        { "span_term": { "title": "守備" } }
      ],
      "slop": 0,
      "in_order": true
    }
  }
}

レスポンス

{
  "count" : 1,
  "_shards" : {
     :
  }
}

試しに、「初守備」を検索すべく以下のようなクエリで検索を行ったがヒットしなかった

kibana(devtool)
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検索が行われた。

kibana(devtool)
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があるようなので他でも同様のことが出来るか見てみたい
2
0
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
2
0