web-dev-qa-db-ja.com

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

私は最近ElasticSearchを使い始めましたが、Wordの一部を検索させることができないようです。

例: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の両方を見つけるにはどうすればよいですか?

UPDATE

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ドキュメントは素晴らしいものではありません...

111
ldx

私も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文字のWordパーツを見つけましょう。必要に応じてmax_gramを調整します。ドイツ語では言葉が本当に大きくなる可能性があるので、高い値に設定します。

78
roka

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

60
imotov

マッピングを変更する必要はないと思います。 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"}

編集-春のデータ弾性検索で同じ実装 https://stackoverflow.com/a/43579948/2357869

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

37
Vijay Gupta

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

すなわち。

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

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

12

ここで説明されているソリューションを試してください: 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文字の短いngramsが使用されます(構成: "max_gram":8)。 8文字を超える用語を検索するには、検索をブールANDクエリに変換して、その文字列内のすべての異なる8文字の部分文字列を検索します。たとえば、ユーザーがlarge yard(10文字の文字列)を検索した場合、検索は次のようになります。

"arge ya AND arge yar AND rge yard

オートコンプリート機能を実装する場合、 Completion Suggester が最も適切なソリューションです。次の ブログ投稿 には、これがどのように機能するかについての非常に明確な説明が含まれています。

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

enter image description here

2
Neshta

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

{ "_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
Ali Moshiri

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

0
Dardino