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的记录。 如何仅选择不同的文档? 我想得到的结果只是这个

 { "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客户端

您可以使用聚合消除重复项。 使用术语聚合 ,结果将按一个字段(例如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 ,结果还将包含具有key唯一值的buckets以及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不提供任何查询,您可以通过该查询根据字段值获取不同的文档。

理想情况下,您应该使用相同的类型ID索引相同的文档,因为ElasticSearch使用这两个内容为文档提供_uid唯一ID。 唯一ID很重要,不仅因为它检测重复文档的方式,而且在任何修改的情况下更新同一文档而不是插入新文档。 有关索引文档的更多信息,您可以阅读此内容 。

但肯定有一个解决你的问题的方法。 由于您使用的是java api客户端,因此您可以根据自己的字段值删除重复的文档。 事实上,它使您可以更灵活地对从ES获得的响应执行自定义操作。

 SearchResponse response = client.prepareSearch().execute().actionGet(); SearchHits hits = response.getHits(); Iterator iterator = hits.iterator(); Map distinctObjects = new HashMap(); while (iterator.hasNext()) { SearchHit searchHit = (SearchHit) iterator.next(); Map 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 } } } } 

同样值得注意的是,为了获得“迈凯轮F1”而不是“迈凯轮”,“F1”,你将不希望你的“favorite_car”字段被分析。

 "favorite_car": { "type": "string", "index": "not_analyzed" } 

对于单个分片,可以使用自定义filter来处理,该filter也可以处理分页。 要处理上述用例,我们可以使用脚本支持,如下所示:

  • 定义自定义脚本filter。 对于此讨论,假设它被称为AcceptDistinctDocumentScriptFilter
  • 此自定义筛选器将主键列表作为输入。
  • 这些主键是其值将用于确定记录唯一性的字段。
  • 现在,我们使用普通搜索请求而不是使用聚合,并将自定义脚本filter传递给请求。
  • 如果搜索已经定义了filter\查询条件,则使用逻辑AND运算符附加自定义filter。
  • 以下是使用伪语法的示例,如果请求是:select * from myindex where file_hash =’hash_value’,则将自定义filter附加为:
    select * from myindex where file_hash =’hash_value’和AcceptDistinctDocumentScriptFilter(params = [‘file_name’,’file_folder’])

对于分布式搜索,这很棘手,需要插件才能挂钩到QUERY阶段。 更多细节在这里 。