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. Подробнее здесь.