Эластичный поиск в массивах с предложением should

У меня есть элемент в моем индексе, который содержит поле, представляющее собой массив. Этот массив содержит элементы с полями даты «от» и «до». «От» всегда установлено, но «до» может быть пустым. С моим запросом я, кажется, не могу найти этот конкретный элемент.

{
    id: 1234,
    items: [
        {
            from: 1993-01-01,
            to: 2007-11-12
        },
        {
            from: 2007-11-13,
        },
    ]
}

Это мой запрос:

{
   "bool": {
     "must": [
       {
         "bool": {
           "should": [
             {
               "range": {
                 "to": {
                   "gte": 2019
                 }
               }
             },
             {
               "bool": {
                 "must_not": [
                   {
                     "exists": {
                       "field": "to"
                     }
                   }
                 ]
               }
             }
           ]
         }
       },
       {
         "range": {
           "from": {
             "lte": "2023-12-31T23:59:59.999"
           }
         }
       }
     ],
   }
 }

Я надеялся, что мой товар сверху появится в результатах поиска.

Казалось бы, предложения ИЛИ/СЛЕДУЕТ оцениваются как ложные — каждое из
их для другого элемента в массиве.
Например. :

{
    "range": {
        "to": {
            "gte": 2019
        }
    }
}

оценивается как false для первого элемента и неприменим для второго.

{
    "bool": {
        "must_not": [
            {
                "exists": {
                    "field": "to"
                }
            }
        ]
    }
}

оценивается как false для первого элемента.

Итак, false ИЛИ false => false — элемент не найден.

Если этот тезис верен, как сообщить ES, что если один из элементов массива соответствует одному из условий, элемент должен быть в результирующем наборе.

Элементы поля являются вложенными?

rabbitbr 14.04.2023 15:44
0
1
50
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вам нужно использовать тип вложенного поля и вложенный запрос для поля элементов.

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

Вот похожий вопрос stackoverflow.

Без использования вложенного поля — , как предложил Мусаб Доган — которое увеличит количество документов в вашем индексе и, следовательно, может повлиять на производительность, я вижу другую возможность исправить, но вам нужно изменить сопоставление индекса и индексация ваших документов.

Это введение null_value вместо items.to. Но вам придется явно определить эти (в настоящее время просто отсутствующие) свойства ваших документов с помощью null.

Чтобы объяснить, почему ваш запрос не работает: массивы, соответственно, значения в массиве по умолчанию выравниваются. (См.: https://www.elastic.co/guide/en/elasticsearch/reference/current/array.html) и не существующие поля/значения пропускаются:

Массив может содержать нулевые значения, которые либо заменяются сконфигурированным значением null_value, либо полностью пропускаются. Пустой массив [] рассматривается как отсутствующее поле — поле без значений.

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

{
    id: 1234,
    items.from: [ 1993-01-01, 2007-11-13 ],
    items.to: [ 2007-11-12 ]
}

См. https://www.elastic.co/guide/en/elasticsearch/reference/8.7/nested.html#nested-arrays-flattening-objects

Но если вы определяете поле items.to с применимым и разумным null_value в вашем случае использования (при условии, что нет items.to означает что-то вроде «еще не известно, но в будущем», это, вероятно, может быть «2999-12-31»), измените сопоставление соответственно и проиндексируйте items.to с явным null, если он не указан, ваш запрос будет работать, как ожидалось.

Итак, если вы измените отображение на:

{
   ....
    "mappings": {
        "_doc": {
            "properties": {
                ....
                "items": {
                    "properties": {
                        ....
                        "to": {
                          "type": "date",
                          "null_value": "2999-12-31"
                        }
                    }
                }
            }
        }
    }
}

и индексировать документы с явным null как items.to:

{
   "id": 1234,
   "items": [
     {
       "from": "1993-01-01",
       "to": "2007-11-12"
     },
     {
       "from": "2007-11-13",
       "to": null
     }
   ]
}

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

{
    id: 1234,
    items.from: [ 1993-01-01, 2007-11-13 ],
    items.to: [ 2007-11-12, 2999-12-31 ]
}

Редактировать: Есть НО... Предполагая, что вы ищете один элемент в массиве items, который удовлетворяет условиям (это один элемент имеет from и to в определенном временном диапазоне), этот способ не точен, потому что совпадения относятся к любой из дат в каждый из сглаженных массивов. Таким образом, вы также найдете документ, в котором есть первый элемент с from и to перед диапазоном, который вы ищете, и еще один элемент с from и to после диапазона, который вы ищете. Чтобы предотвратить это, использование вложенного массива — как сказал @musab-dogan — единственный способ добиться желаемого.

Другие вопросы по теме