假设我有这个给定的数据
{
"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客户端
答案 0 :(得分:21)
您可以使用aggregations消除重复项。使用term aggregation,结果将按一个字段分组,例如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
} ]
}
}
}
请注意,由于重复删除和结果排序,使用聚合会很昂贵。
答案 1 :(得分:9)
ElasticSearch不提供任何查询,您可以通过该查询根据字段值获取不同的文档。
理想情况下,您应该使用相同的类型和 ID 对相同的文档编制索引,因为ElasticSearch使用这两件事来提供 _uid 唯一id到文档。唯一ID很重要,不仅因为它检测重复文档的方式,而且在任何修改的情况下更新同一文档而不是插入新文档。有关索引文档的更多信息,请阅读this。
但是肯定会解决你的问题。由于您使用的是java api客户端,因此您可以根据自己的字段值删除重复的文档。事实上,它使您可以更灵活地对从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。
我希望这能解决你的问题。如果代码中有任何错误,请原谅我。这只是一个伪代码,可以让您了解如何解决问题。
由于
答案 2 :(得分:3)
@JRL几乎是正确的。您需要在查询中使用聚合。这将为您提供前10000名&#34; favorite_cars&#34;在您的对象中按出现顺序排列。
{
"query":{ "match_all":{ } },
"size":0,
"Distinct" : {
"Cars" : {
"terms" : { "field" : "favorite_cars", "order": { "_count": "desc"}, "size":10000 }
}
}
}
值得注意的是,你会想要你的&#34; favorite_car&#34;为了得到迈凯轮F1&#34;麦克拉伦F1&#34;而不是&#34;麦克拉伦&#34;,&#34; F1&#34;。
"favorite_car": {
"type": "string",
"index": "not_analyzed"
}
答案 3 :(得分:2)
对于单个分片,可以使用自定义过滤器处理,该过滤器也可以处理分页。要处理上述用例,我们可以使用脚本支持,如下所示:
对于分布式搜索,这很棘手,需要插件才能挂钩到QUERY阶段。更多详情here。