この特定のデータがあるとしましょう
{
"name" : "ABC",
"favorite_cars" : [ "ferrari","toyota" ]
}, {
"name" : "ABC",
"favorite_cars" : [ "ferrari","toyota" ]
}, {
"name" : "GEORGE",
"favorite_cars" : [ "honda","Hyundae" ]
}
好きな車がトヨタの人を検索するときにこのデータをクエリすると、このデータが返されます
{
"name" : "ABC",
"favorite_cars" : [ "ferrari","toyota" ]
}, {
"name" : "ABC",
"favorite_cars" : [ "ferrari","toyota" ]
}
結果はABCという名前の2つのレコードです。個別のドキュメントのみを選択するにはどうすればよいですか?欲しい結果はこれだけ
{
"name" : "ABC",
"favorite_cars" : [ "ferrari","toyota" ]
}
これが私のクエリです
{
"fuzzy_like_this_field" : {
"favorite_cars" : {
"like_text" : "toyota",
"max_query_terms" : 12
}
}
}
ElasticSearch 1.0.0を使用しています。 Java api client
aggregations を使用して重複を排除できます。 用語の集計 を使用すると、結果は1つのフィールドでグループ化されます。 name
は、フィールドの各値の出現回数も提供し、結果をこの回数(降順)で並べ替えます。
{
"query": {
"fuzzy_like_this_field": {
"favorite_cars": {
"like_text": "toyota",
"max_query_terms": 12
}
}
},
"aggs": {
"grouped_by_name": {
"terms": {
"field": "name",
"size": 0
}
}
}
}
hits
に加えて、結果にはbuckets
も含まれ、key
には一意の値が含まれ、doc_count
にはカウントが含まれます。
{
"took" : 4,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"failed" : 0
},
"hits" : {
"total" : 2,
"max_score" : 0.19178301,
"hits" : [ {
"_index" : "pru",
"_type" : "pru",
"_id" : "vGkoVV5cR8SN3lvbWzLaFQ",
"_score" : 0.19178301,
"_source":{"name":"ABC","favorite_cars":["ferrari","toyota"]}
}, {
"_index" : "pru",
"_type" : "pru",
"_id" : "IdEbAcI6TM6oCVxCI_3fug",
"_score" : 0.19178301,
"_source":{"name":"ABC","favorite_cars":["ferrari","toyota"]}
} ]
},
"aggregations" : {
"grouped_by_name" : {
"buckets" : [ {
"key" : "abc",
"doc_count" : 2
} ]
}
}
}
重複の排除と結果のソートのため、集計を使用するとコストがかかることに注意してください。
ElasticSearchは、フィールド値に基づいて個別のドキュメントを取得できるクエリを提供しません。
理想的には、同じtypeおよびidを使用して同じドキュメントにインデックスを付ける必要があります。これら2つはElasticSearchで使用され、ドキュメントに_ uid一意のIDを付与します。一意のIDは、重複したドキュメントを検出する方法だけでなく、新しいドキュメントを挿入する代わりに変更があった場合に同じドキュメントを更新するためにも重要です。ドキュメントのインデックス作成の詳細については、 this を参照してください。
ただし、問題の回避策は間違いありません。 Java api clientを使用しているため、フィールド値に基づいて自分で重複したドキュメントを削除できます。実際には、ESから取得した応答に対してカスタム操作を実行する柔軟性が高くなります。
SearchResponse response = client.prepareSearch().execute().actionGet();
SearchHits hits = response.getHits();
Iterator<SearchHit> iterator = hits.iterator();
Map<String, SearchHit> distinctObjects = new HashMap<String,SearchHit>();
while (iterator.hasNext()) {
SearchHit searchHit = (SearchHit) iterator.next();
Map<String, Object> source = searchHit.getSource();
if(source.get("name") != null){
distinctObjects.put(source.get("name").toString(),source);
}
}
したがって、マップ内に固有のsearchHitオブジェクトのマップがあります。
オブジェクトマッピングを作成して、SearchHitの代わりに使用することもできます。
これで問題が解決することを願っています。コードにエラーがある場合はご容赦ください。これは、問題を解決する方法を理解させるための単なる疑似コードです。
ありがとう
@JRLはほぼ正しいです。クエリには集計が必要です。これにより、オブジェクトの上位10000個の「favorite_cars」のリストが出現順に並べられます。
{
"query":{ "match_all":{ } },
"size":0,
"Distinct" : {
"Cars" : {
"terms" : { "field" : "favorite_cars", "order": { "_count": "desc"}, "size":10000 }
}
}
}
また、「McLaren」、「F1」の代わりに「McLaren F1」を取得するために、「favorite_car」フィールドを分析しないようにすることにも注意してください。
"favorite_car": {
"type": "string",
"index": "not_analyzed"
}
単一のシャードの場合、これは、ページネーションも処理するカスタムフィルターを使用して処理できます。上記の使用例を処理するために、スクリプトサポートを次のように使用できます。
分散検索の場合、これはトリッキーであり、QUERYフェーズにフックするプラグインが必要です。詳細 こちら 。