ElasticSearchで単語の一部を検索する方法


128

私は最近ElasticSearchを使い始めましたが、単語の一部を検索させることができません。

例:ElasticSearchでインデックス付けされたcouchdbからの3つのドキュメントがあります。

{
  "_id" : "1",
  "name" : "John Doeman",
  "function" : "Janitor"
}
{
  "_id" : "2",
  "name" : "Jane Doewoman",
  "function" : "Teacher"
}
{
  "_id" : "3",
  "name" : "Jimmy Jackal",
  "function" : "Student"
} 

それでは、「Doe」を含むすべてのドキュメントを検索します

curl http://localhost:9200/my_idx/my_type/_search?q=Doe

それはヒットを返しません。しかし、私が検索した場合

curl http://localhost:9200/my_idx/my_type/_search?q=Doeman

1つのドキュメント(John Doeman)を返します。

インデックスのプロパティとして、さまざまなアナライザーとさまざまなフィルターを設定してみました。また、本格的なクエリを使用してみました(例:

{
  "query": {
    "term": {
      "name": "Doe"
    }
  }
}

)しかし、何も機能しないようです。

「Doe」を検索するときに、ElasticSearchでJohn DoemanとJane Doewomanの両方を検索するにはどうすればよいですか?

更新

私は、Igorが提案したように、nGramトークナイザーとフィルターを次のように使用しようとしました:

{
  "index": {
    "index": "my_idx",
    "type": "my_type",
    "bulk_size": "100",
    "bulk_timeout": "10ms",
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "type": "custom",
          "tokenizer": "my_ngram_tokenizer",
          "filter": [
            "my_ngram_filter"
          ]
        }
      },
      "filter": {
        "my_ngram_filter": {
          "type": "nGram",
          "min_gram": 1,
          "max_gram": 1
        }
      },
      "tokenizer": {
        "my_ngram_tokenizer": {
          "type": "nGram",
          "min_gram": 1,
          "max_gram": 1
        }
      }
    }
  }
}

私が今持っている問題は、すべてのクエリがすべてのドキュメントを返すということです。ポインタはありますか?nGramの使用に関するElasticSearchのドキュメントは適切ではありません...


9
当然ですが、最小/最大のngramが1に設定されているため、1文字:)
マーティンB.

回答:


85

私もnGramを使用しています。標準のトークナイザーとnGramをフィルターとして使用しています。これが私のセットアップです:

{
  "index": {
    "index": "my_idx",
    "type": "my_type",
    "analysis": {
      "index_analyzer": {
        "my_index_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "mynGram"
          ]
        }
      },
      "search_analyzer": {
        "my_search_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "standard",
            "lowercase",
            "mynGram"
          ]
        }
      },
      "filter": {
        "mynGram": {
          "type": "nGram",
          "min_gram": 2,
          "max_gram": 50
        }
      }
    }
  }
}

50文字までの単語の部分を見つけましょう。必要に応じてmax_gramを調整します。ドイツ語の単語は非常に大きくなる可能性があるため、高い値に設定します。



それは、インデックスの設定から得られるものですか、それともそれを構成するためにelasticsearchに投稿するものですか?
Tomas Jansson 2014年

Elasticsearchを構成するためのPOSTです。
roka 2014年

私は現在のバージョンのElasticsearchに固執
roka

1
@JimC私は少なくとも7年間ElasticSearchを使用していないので、プロジェクトの現在の変更点がわかりません。
roka

63

大きなインデックスでは、先頭と末尾のワイルドカードを使用した検索は非常に遅くなります。単語のプレフィックスで検索できるようにするには、先頭のワイルドカードを削除します。単語の途中で部分文字列を見つける必要がある場合は、ngramトークナイザーを使用した方がよいでしょう。


14
イゴールは正しいです。少なくとも先頭の*を削除してください。NGram ElasticSearchの例については、次の要旨を参照してください:gist.github.com/988923
karmi

3
@karmi:完全な例をありがとう!多分あなたはあなたのコメントを実際の答えとして追加したいでしょう、それは私にとってそれがうまくいったものであり、私が賛成したいものです。
Fabian Steeg

54

マッピングを変更する必要はないと思います。query_stringを使用してみてください。完璧です。すべてのシナリオは、デフォルトの標準アナライザーで機能します。

データがあります:

{"_id" : "1","name" : "John Doeman","function" : "Janitor"}
{"_id" : "2","name" : "Jane Doewoman","function" : "Teacher"}

シナリオ1:

{"query": {
    "query_string" : {"default_field" : "name", "query" : "*Doe*"}
} }

応答:

{"_id" : "1","name" : "John Doeman","function" : "Janitor"}
{"_id" : "2","name" : "Jane Doewoman","function" : "Teacher"}

シナリオ2:

{"query": {
    "query_string" : {"default_field" : "name", "query" : "*Jan*"}
} }

応答:

{"_id" : "1","name" : "John Doeman","function" : "Janitor"}

シナリオ3:

{"query": {
    "query_string" : {"default_field" : "name", "query" : "*oh* *oe*"}
} }

応答:

{"_id" : "1","name" : "John Doeman","function" : "Janitor"}
{"_id" : "2","name" : "Jane Doewoman","function" : "Teacher"}

編集-Springデータエラスティック検索を使用した同じ実装 https://stackoverflow.com/a/43579948/2357869

query_stringが他よりも優れている理由のもう1つの説明 https://stackoverflow.com/a/43321606/2357869


3
これが一番簡単だと思います
Esgi Dendyanri

はい 。私は自分のプロジェクトに実装しました。
Opster Elasticsearch Pro-Vijay 2017

検索する複数のフィールドを含める方法は?
Shubham A.17年

これを試してください:-{"query":{"query_string":{"fields":["content"、 "name"]、 "query": "this AND that"}}}
Opster Elasticsearch Pro-Vijay


14

インデックスマッピングを変更せずに、希望するような部分検索を行う単純なプレフィックスクエリを実行できます

すなわち。

{
  "query": { 
    "prefix" : { "name" : "Doe" }
  }
}

https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-prefix-query.html


プレフィックスクエリを使用してマルチフィールド検索を実行できますか?
batmaci 2018年

ありがとう、ちょうど私が探していたもの!パフォーマンスへの影響について何か考えはありますか?
Vingtoft

6

ここで説明されている解決策を試してください:ElasticSearchでの正確な部分文字列検索

{
    "mappings": {
        "my_type": {
            "index_analyzer":"index_ngram",
            "search_analyzer":"search_ngram"
        }
    },
    "settings": {
        "analysis": {
            "filter": {
                "ngram_filter": {
                    "type": "ngram",
                    "min_gram": 3,
                    "max_gram": 8
                }
            },
            "analyzer": {
                "index_ngram": {
                    "type": "custom",
                    "tokenizer": "keyword",
                    "filter": [ "ngram_filter", "lowercase" ]
                },
                "search_ngram": {
                    "type": "custom",
                    "tokenizer": "keyword",
                    "filter": "lowercase"
                }
            }
        }
    }
}

ディスク使用量の問題と長すぎる検索語の問題を解決するために、短い8文字の長いngramが使用されます("max_gram":8で構成)。8文字を超える用語を検索するには、検索をブールANDクエリに変換して、その文字列内の8文字のサブ文字列をすべて検索します。たとえば、ユーザーが大きな庭(10文字の文字列)を検索した場合、検索は次のようになります。

"arge ya AND arge yar AND rge yard


2
デッドリンク、pls修正
DarkMukke

このようなものをしばらく探していました。ありがとうございました!あなたはどのようにして、メモリのスケールを知っていますmin_gramし、max_gramそれがフィールド値の大きさとの範囲に直線的に依存になるようにそれはそうminmax。このようなものをどのように使っているのですか?
Glen Thompson、

またngram、トークナイザーのフィルターである理由は何ですか?それをトークナイザーとして使用してから小文字のフィルターを適用することはできませんか?index_ngram: { type: "custom", tokenizer: "ngram_tokenizer", filter: [ "lowercase" ] }私はそれを試してみましたが、アナライザーテストAPIを使用して同じ結果が得られるようです
Glen Thompson

2

オートコンプリート機能を実装する場合は、補完候補が最も優れたソリューションです。次のブログ投稿には、これがどのように機能するかが非常に明確に記載されています。

言い換えると、これはFSTと呼ばれるメモリ内のデータ構造であり、有効な提案を含み、高速な取得とメモリの使用のために最適化されています。基本的に、それは単なるグラフです。例えば、およびFSTは、単語を含むhotelmarriotmercuremunchenmunich次のようになります。

ここに画像の説明を入力してください


2

正規表現を使用できます。

{ "_id" : "1", "name" : "John Doeman" , "function" : "Janitor"}
{ "_id" : "2", "name" : "Jane Doewoman","function" : "Teacher"  }
{ "_id" : "3", "name" : "Jimmy Jackal" ,"function" : "Student"  } 

このクエリを使用する場合:

{
  "query": {
    "regexp": {
      "name": "J.*"
    }
  }
}

名前が「J」で始まるすべてのデータを指定します。次のクエリを使用できるように、名前が「man」で終わる最初の2つのレコードのみを受け取りたいとします。

{
  "query": { 
    "regexp": {
      "name": ".*man"
    }
  }
}

名前に "m"が存在するすべてのレコードを受け取りたい場合は、次のクエリを使用できます。

{
  "query": { 
    "regexp": {
      "name": ".*m.*"
    }
  }
}

これは私にとってはうまくいきます。そして、私の答えがあなたの問題を解決するのに適していることを願っています。


1

ワイルドカード(*)を使用すると、スコアの計算ができなくなります


1
回答に詳細を追加できますか?これが何をするかについてのサンプルコードまたはドキュメントへの参照を提供します。
クレイ

0

私はこれを使用していて、働きました

"query": {
        "query_string" : {
            "query" : "*test*",
            "fields" : ["field1","field2"],
            "analyze_wildcard" : true,
            "allow_leading_wildcard": true
        }
    }

-6

気にしないで。

Luceneのドキュメントを確認する必要がありました。ワイルドカードを使用できるようです!:-)

curl http://localhost:9200/my_idx/my_type/_search?q=*Doe*

トリックを行います!


11
@imotovの回答を参照してください。ワイルドカードの使用は、まったくうまく拡張できません。
Mike Munroe

5
@Idx-自分の回答がどのように投票されているかを確認します。反対票は、回答の質と関連性を表します。正解を受け入れるために少し時間を割いていただけませんか?少なくとも新しいユーザーはあなたに感謝するでしょう。
asyncwait 2013

3
十分な反対票。OPは現在、最良の答えは何かを明らかにしました。+1は、誰かがより良い答えを投稿する前に、最良の答えであると思われるものを共有するためのものです。
s.Daniel 2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.