ElasticSearch возвращает только документы с отличным значением
Скажем, у меня есть данные
{
"name" : "ABC",
"favorite_cars" : [ "ferrari","toyota" ]
}, {
"name" : "ABC",
"favorite_cars" : [ "ferrari","toyota" ]
}, {
"name" : "GEORGE",
"favorite_cars" : [ "honda","Hyundae" ]
}
Всякий раз, когда я запрашиваю эти данные при поиске людей, у которых любимый автомобиль является toyota, он возвращает эти данные
{
"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
Ответы
Ответ 1
Вы можете исключить дубликаты, используя aggregations. При агрегировании результаты будут сгруппированы по одному полю, например. 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
} ]
}
}
}
Обратите внимание, что использование агрегаций будет дорогостоящим из-за повторного устранения и сортировки результатов.
Ответ 2
ElasticSearch не предоставляет никаких запросов, по которым вы можете получить разные документы на основе значения поля.
В идеале вы должны индексировать один и тот же документ с тем же типом и id, поскольку эти две вещи используются ElasticSearch для создания уникального _uid id к документу. Уникальный идентификатор важен не только из-за способа обнаружения дубликатов документов, но и для обновления одного и того же документа в случае каких-либо изменений вместо вставки нового. Для получения дополнительной информации об индексировании документов вы можете прочитать this.
Но для вашей проблемы определенно есть работа. Поскольку вы используете java-api-клиент, вы можете удалить дубликаты документов на основе значения поля самостоятельно. Infact, это дает вам большую гибкость для выполнения пользовательских операций над ответами, которые вы получаете от 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.
Надеюсь, это решит вашу проблему. Пожалуйста, простите меня, если есть какие-либо ошибки в коде. Это просто псевдо-код, чтобы вы поняли, как решить свою проблему.
Спасибо
Ответ 3
@JRL почти правильный. Вам потребуется агрегация в вашем запросе. Это даст вам список лучших 10000 "favorite_cars" в вашем объекте, упорядоченном по происхождению.
{
"query":{ "match_all":{ } },
"size":0,
"Distinct" : {
"Cars" : {
"terms" : { "field" : "favorite_cars", "order": { "_count": "desc"}, "size":10000 }
}
}
}
Также стоит отметить, что вы хотите, чтобы ваше поле "favorite_car" не анализировалось, чтобы получить "McLaren F1" вместо "McLaren", "F1".
"favorite_car": {
"type": "string",
"index": "not_analyzed"
}
Ответ 4
Для одного осколка это можно обрабатывать с помощью настраиваемого фильтра, который также заботится о разбиении на страницы. Для обработки вышеуказанного варианта использования мы можем использовать поддержку script следующим образом:
- Определите пользовательский фильтр script. Для этого обсуждения предположим, что он называется AcceptDistinctDocumentScriptFilter
- Этот настраиваемый фильтр принимает в качестве входных данных список первичных ключей.
- Эти первичные ключи - это поля, значения которых будут использоваться для определения уникальности записей.
- Теперь вместо использования агрегации мы используем обычный запрос поиска и передаем пользовательский фильтр script в запрос.
- Если в поиске уже определены критерии фильтра\запроса, добавьте настраиваемый фильтр с помощью логического оператора AND.
- Ниже приведен пример использования псевдосинтаксиса если запрос: выберите * from myindex, где file_hash = 'hash_value' затем добавьте настраиваемый фильтр как:
выберите * from myindex, где file_hash = 'hash_value' AND AcceptDistinctDocumentScriptFilter (params = ['file_name', 'file_folder'])
Для распределенного поиска это сложно и требуется плагин для подключения к фазе QUERY. Подробнее здесь.