web-dev-qa-db-ja.com

ElasticSearchが個別の値を持つドキュメントのみを返す

この特定のデータがあるとしましょう

{
            "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

17
user962206

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
      } ]
    }
  }
}

重複の排除と結果のソートのため、集計を使用するとコストがかかることに注意してください。

21
JRL

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の代わりに使用することもできます。

これで問題が解決することを願っています。コードにエラーがある場合はご容赦ください。これは、問題を解決する方法を理解させるための単なる疑似コードです。

ありがとう

9
dark_shadow

@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"
}
3
Eulalie367

単一のシャードの場合、これは、ページネーションも処理するカスタムフィルターを使用して処理できます。上記の使用例を処理するために、スクリプトサポートを次のように使用できます。

  • カスタムスクリプトフィルターを定義します。この説明では、AcceptDistinctDocumentScriptFilterと呼ばれていると仮定します。
  • このカスタムフィルターは、主キーのリストを入力として受け取ります。
  • これらの主キーは、レコードの一意性を決定するために使用される値を持つフィールドです。
  • これで、集約を使用する代わりに、通常の検索リクエストを使用して、カスタムスクリプトフィルターをリクエストに渡します。
  • 検索に既にfilter\query基準が定義されている場合は、論理AND演算子を使用してカスタムフィルターを追加します。
  • 以下は、リクエストが疑似構文を使用する例です。select* from myindex where file_hash = 'hash_value'次に、カスタムフィルターを次のように追加します。
    select * from myindex where file_hash = 'hash_value' AND AcceptDistinctDocumentScriptFilter(params = ['file_name'、 'file_folder'])

分散検索の場合、これはトリッキーであり、QUERYフェーズにフックするプラグインが必要です。詳細 こちら

2
Ajey Dudhe