NGram Tokenizerについて

はじめまして。
Elasticsearch初心者です。

日本語・英語・記号など、さまざまな文字が含まれるデータの中より、
単純に指定した文字が含まれる(部分一致)データを抽出しようとしています。

指定する文字は「1文字~」です。

ngram_tokenizer: {
    type: "nGram",
    min_gram: "1",
    max_gram: "2",
    token_chars: ["letter", "digit", "punctuation", "symbol"]
 }

 ngram_analyzer: {
    tokenizer: "ngram_tokenizer",
    filter: [
        "greek_lowercase_filter"
    ]
 }

といったようなAnalyzer定義をし、実際のデータの
マッピング指定は、単純に以下のような指定としています。

descriptiondata: { type: 'text',
                                 analyzer: "ngram_analyzer" }

インデックスに登録されたデータが日本語、英語であろうと、
大半のパターンが問題なく検索できているのですが、
検索対象文字を test と指定しているにも関わらず、

 esstem

がデータで登録されているものがヒットしてしまいます。

ElasticserachのVerは5.4.3です。
NGramであれば、このような検索結果となってしまうのでしょうか。

よろしくお願いいたします。

こんにちわ

NGramであれば、というよりは検索クエリに依ると考えています。
どのような検索クエリを実行されてますでしょうか?

たとえば、以下のようなクエリを想定しました。

POST forum0131/_search
{
  "query": {
    "simple_query_string": {
      "query": "test",
      "fields": ["descriptiondata"],
      "default_operator": "and"
    }
  }
}

POST forum0131/_search
{
  "query": {
    "match": {
      "descriptiondata": {
        "query": "test",
        "operator": "and"
      }
    }
  }
}

こうした指定ですと、"t”, "e", "s", "t", "te", "es", "st" が順不同でトークンとして存在すれば、検索にヒットとなります。
esstem を考えたとき、 "e", "s", "t" というトークンが出来るわけで、順不同であれば testから生成されるトークンと合致しますよね。

順不同ではなく、順序も条件に含めたいときは、simple_query_stringであれば 検索キーワードを""で囲む、あるいはmatch_phraseを使うと良いのではと思います。

POST forum0131/_search
{
  "query": {
    "simple_query_string": {
      "query": "\"test\"",
      "fields": ["descriptiondata"],
      "default_operator": "and"
    }
  }
}

POST forum0131/_search
{
  "query": {
    "match_phrase": {
      "descriptiondata": "test"
    }
  }
}

念のためですが、simple_query_stringやmatchは、デフォルトでOR検索になっているので
test という条件を入れたときは、 t OR e OR s OR t OR te OR es OR st というような検索になるので、
検索対象のフィールドに "t" しかない状態でも、testという検索キーワードでヒットする、ということになります。

そのため、最初の方に示した例では、operatorにand指定を追加するようにしております。

ご参考になれば幸いです。

以下、こちらが確認した際に使った設定を転記しておきます。

インデックス設定

PUT forum0131
{
  "settings": {
    "analysis": {
      "analyzer": {
        "ngram_analyzer": {
          "tokenizer": "ngram_tokenizer",
          "filter": [
            
          ]
        }
      },
      "tokenizer": {
        "ngram_tokenizer": {
          "type": "nGram",
          "min_gram": "1",
          "max_gram": "2",
          "token_chars": [
            "letter",
            "digit",
            "punctuation",
            "symbol"
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "descriptiondata": {
        "type": "text",
        "analyzer": "ngram_analyzer"
      }
    }
  }
}

テストデータ

POST forum0131/_doc/1
{
  "descriptiondata": "esstem"
}

tsgkdt さん、ありがとうございます!

ご指摘通り、単に and 指定のクエリになっています。
NGram自体の使い方には、これ以上変更するものはない
と思っていましたので、怪しい可能性があるクエリを
ご指摘いただき、希望の光が見えてきました。

「 こうした指定ですと、"t”, "e", "s", "t", "te", "es", "st" が 順不同で
トークンとして存在すれば 、検索にヒットとなります。
esstem を考えたとき、 "e", "s", "t" というトークンが出来るわけで、
順不同であれば testから生成されるトークンと合致しますよね。」

そうですよね。おっしゃる通りです。

クエリに関する知識がほとんどないので、本当に助かりました。
ご提示いただいた内容で、いろいろ試してみたいと思います。

取り急ぎ、お礼のお返事でした。。

何点か補足させてください。

5.4.xは、既にEOLになっているので、バージョンアップも計画されたほうが良いと老婆心ながら思いました。
https://www.elastic.co/jp/support/eol

なぜヒットするか、ヒットしないのか、という点については、
どういうトークンに分割されているのかという確認が大切だと思っています。
その確認には、analyze apiが使えます。よろしければ参照ください。
https://www.elastic.co/guide/en/elasticsearch/reference/5.4/indices-analyze.html

GUIでの確認については、@johtani さんが、プラグインとして作成されて公開していらっしゃいます。
利用イメージは下記をごらんください。(たぶん、バージョン6系以降のリリースだった気がします)

あとは、検索クエリに "explain": true をつけることで、検索にヒットしたときのより詳細な情報を得ることができます。

ご参考になれば幸いです。

tsgkdtさん

もろもろご指摘、情報ありがとうございます!
まずは、先程のアドバイスに従って修正したところ、
期待している動きとなりました。ありがとうございました!

そうですよね。VerUPも必要ですよね。計画しておきます。
Kibanaもインストールして利用しているのですが、
まだ十分に活用できてない気がしてます。

トークン分割の確認もマストですよね。
そもそも、ヒットが順不同という点を動きから推測は
できてもどのように対処すべきか調べきれてませんでした。

大変助かりました。本当にありがとうございました。
("explain":true も初めてりました。ありがとうございます)