Поиск ElasticSearch через поле массива как эксклюзивный поиск - PullRequest
1 голос
/ 05 июля 2019

У меня есть массив данных в поле в ElasticSearch с типом ключевого слова. Я хочу искать в этом массиве эксклюзивные значения, которые я хочу искать, то есть исключать значения массива, которые не включены в мое ключевое слово поиска. Пожалуйста, смотрите детали ниже.

Спасибо!

У меня есть следующее эластичное отображение индекса поиска:

"exgroups": {
  "type": "keyword",
  "eager_global_ordinals": true
},

Со следующими образцами данных:

"id": 1,
"exgroups": ["TSX"]

"id": 2,
"exgroups": ["TSX", "OTC", "NSD"]

Мой поиск такой:

{
  "bool" : {
    "filter" : {

        "term" : {
          "exgroups" : {
            "value" : "TSX"
          }
        }

    }
  }
}

Я использовал MatchQueryBuilder, TermQueryBuilder, TermsQueryBuilder безрезультатно. По определению ElasticSearch TermQuery это должно сработать. https://www.elastic.co/guide/en/elasticsearch/reference/6.2/query-dsl-term-query.html. Но это не так, вероятно, потому что поле является массивом.

В общем, термин * запрос ведет себя так:

iterate all the documents, for each document
  check if the exgroups contains 'tsx'
  if it does, return the document

Возвращает документы 1 и 2, поскольку документ 2 также содержит TSX. Тем не менее, я хотел, чтобы он возвратил только документ 1 и ничего другого в массиве.

Как мне это сделать?

Заранее спасибо.

1 Ответ

2 голосов
/ 06 июля 2019

Решение по переиндексации:

Я недавно нашел эту документацию от ElasticSearch: https://www.elastic.co/guide/en/elasticsearch/guide/current/_finding_multiple_exact_values.html

Как в TermQuery, так и в TermsQuery или ElasticSearch в общем случае используется 'должен содержать«вместо« должен равняться »из-за его инвертированного индекса.

По их мнению, наилучшее возможное решение:

Если вы хотите такое поведение - равенство всех полей- лучший способ достичь этого - индексирование вторичного поля.В этом поле вы индексируете количество значений, содержащихся в вашем поле.Используя наши два предыдущих документа.Как только у вас есть индексированная информация о количестве, вы можете создать constant_score, который обеспечивает соответствующее количество терминов.https://www.elastic.co/guide/en/elasticsearch/guide/current/_finding_multiple_exact_values.html#_equals_exactly

Шаги ниже:

  1. Добавить дополнительное отображение в индексе с именем exgroups_count.
  2. Использовать logstash для подсчета длины массива exgroups и поместить вполе exgroups_count.
  3. сохранить индекс.

Другое решение без переиндексации:

Существуют некоторые ограничения на добавление и повторИндексировать все это.Как только ваш индекс будет расти, будет очень навязчиво добавлять поля в индекс и вычислять счетчики - что делает его очень трудоемким - не говоря уже о том, что вы должны сохранять и поддерживать свое отображение.

Я нашел решениеэто не нуждается в переиндексации.Глядя на ScriptQueryBuilder, я могу теоретически добавить фильтр сценариев, который считает длину массива и равняется 1.

"filter" : {
    "script" : {
        "script" : "doc['exgroups'].values.length == 1"
    }
}

Таким образом, полный запрос теперь выглядит так:

"bool" : {
  "must" : [
    {
      "term" : {
        "exgroups" : {
          "value" : "TSX",
          "boost" : 1.0
        }
      }
    }
  ],
  "filter" : [
    {
      "script" : {
        "script" : {
          "source" : "doc['exgroups'].values.length == 1",
          "lang" : "painless"
        },
        "boost" : 1.0
      }
    }
  ],
  "adjust_pure_negative" : true,
  "boost" : 1.0
}

В Java

BoolQueryBuilder qBool = new BoolQueryBuilder();
TermQueryBuilder query = new TermQueryBuilder("exgroups", exchangeGroup.getCode());

qBool.must(query);

ScriptQueryBuilder sQuery = new ScriptQueryBuilder(new Script("doc['exgroups'].values.length == 1"));

qBool.filter(sQuery);
...