Реализация запроса Array.Except (Array2)> 0 в фильтре эластичного поиска? - PullRequest
0 голосов
/ 13 декабря 2018

Допустим, у меня проиндексированы следующие документы:

[
    {
        "Id": 1,
        "Numbers": [1, 2, 3]
    },
    {
        "Id": 2,
        "Numbers": [4, 5]
    }    
]

У меня есть параметр [1,2,4,5], который определяет, какие номера мне не разрешено видеть - я хочу найти документыгде массив «Numbers» содержит как минимум один элемент НЕ в моем входном массиве (поэтому в этом случае должен быть возвращен первый документ).

Реальный сценарий для поиска групп, которые (или дочерние группы) не содержат продуктовпринадлежность к определенному типу продукта.У меня есть рекурсивно индексированные идентификаторы типов продуктов (представленные в качестве цифр в примере), и я хочу найти группы, которые содержат продукты, не принадлежащие моему входному параметру (мой входной параметр представляет собой массив идентификаторов типов продуктов, которые мне не разрешено видеть)

Какой запрос / фильтр я должен использовать и как он должен быть построен?Я рассмотрел следующее:

        return desc.Bool(b => b
            .MustNot(mn => mn.Bool(mnb => mnb.Must(mnbm => mnbm.Terms(t => t.ItemGroups, permissions.RestrictedItemGroups) && mnbm.Term(t => t.ItemGroupCount, permissions.RestrictedItemGroups.Count())))));

, но проблема в том, что если у меня есть 6 групп ограниченных элементов, где в данной группе содержится 3 группы с ограничениями, то я не найду совпадений, потому что счет выиграл 'т совпадают.Теперь это имеет смысл.В качестве обходного пути я реализовал Results.Except (Restricted) в C #, чтобы отфильтровать ограниченные группы после поиска, но хотел бы реализовать его вasticsearch.

Ответы [ 2 ]

0 голосов
/ 30 декабря 2018

Запрос terms_set кажется вполне подходящим для этого;это похоже на запрос terms с дополнительным отличием, в котором вы можете указать, сколько терминов должно совпадать с динамическим значением, полученным из входных данных или каждого документа.

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

Будет работать что-то подобное следующемузапрос находится в предложении must_not для инвертирования совпадений, где все значения в Numbers находятся во входных терминах, и объединяется с запросом exists для Numbers, чтобы исключить документы, не имеющие значений для Numbers, так какв примере документа с идентификатором 3.

Эту задачу можно улучшить, если также индексировать длину массива Numbers в другом поле документа, а затем использовать * 1029.* вместо сценария.Просто нужно убедиться, что эти два свойства остаются синхронизированными, что было бы довольно легко сделать в C # POCO с помощью метода получения свойства, который возвращает значение длины массива Numbers.

0 голосов
/ 19 декабря 2018

Новый ответ

Я оставляю старый ответ ниже, так как он может быть полезен для других людей.В вашем случае вы хотите отфильтровать документы, которые не совпадают, а не только пометить их.Итак, следующий запрос даст вам то, что вы ожидаете, то есть только первый документ:

POST test/_search
{
  "query": {
    "script": {
      "script": {
        "source": """
          // copy the doc values into a temporary list
          def tmp = new ArrayList(doc.Numbers.values);

          // remove all ids from the params
          tmp.removeIf(n -> params.ids.contains((int)n));

          // return true if the array still contains ids, false if not
          return tmp.size() > 0;
        """,
        "params": {
          "ids": [
            1,
            2,
            4,
            5
          ]
        }
      }
    }
  }
}

Старый ответ

Один из способов решить эту проблему - использоватьПоле скрипта, которое будет возвращать true или false в зависимости от вашего состояния:

POST test/_search
{
  "_source": true,
  "script_fields": {
    "not_present": {
      "script": {
        "source": """
      // copy the numbers array
      def tmp = params._source.Numbers;

      // remove all ids from the params
      tmp.removeIf(n -> params.ids.contains(n));

      // return true if the array still contains data, false if not
      return tmp.length > 0;
""",
        "params": {
          "ids": [ 1, 2, 4, 5 ]
        }
      }
    }
  }
}

Результат будет выглядеть следующим образом:

  "hits" : {
    "total" : 2,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "test",
        "_type" : "doc",
        "_id" : "2",
        "_score" : 1.0,
        "_source" : {
          "Id" : 2,
          "Numbers" : [
            4,
            5
          ]
        },
        "fields" : {
          "not_present" : [
            false                           <--- you don't want this doc
          ]
        }
      },
      {
        "_index" : "test",
        "_type" : "doc",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "Id" : 1,
          "Numbers" : [
            1,
            2,
            3
          ]
        },
        "fields" : {
          "not_present" : [
            true                            <--- you want this one, though
          ]
        }
      }
    ]
  }
}
...