Запрос с полем массива фильтров

#elasticsearch

Вопрос:

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

 {   
    "orderNumber":"ORD-111",
    "items":[{"name":"part-1","status":"new"},
             {"name":"part-2","status":"paid"}]
}
{
    "orderNumber":"ORD-112",
    "items":[{"name":"part-3","status":"paid"},
             {"name":"part-4","status":"supplied"}]
}
 

Я хочу создать запрос, чтобы мой результат включал все документы заказа, но только с элементами, которые соответствуют {«статус»:»поставлено»}.
Результат должен выглядеть так:

 {   
    "orderNumber":"ORD-111",
    "items":[]
}
{
    "orderNumber":"ORD-112",
    "items":[{"name":"part-4","status":"supplied"}]
}
 

Ответ №1:

Вы можете использовать вложенный запрос вместе с inner_hits тем, чтобы получить в результате только соответствующие значения массива

Добавление рабочего примера

Сопоставление индексов:

 {
  "mappings": {
    "properties": {
      "items": {
        "type": "nested"
      }
    }
  }
}
 

Поисковый запрос:

 {
  "query": {
    "nested": {
      "path": "items",
      "query": {
        "bool": {
          "must": [
            {
              "match": {
                "items.status": "supplied"
              }
            }
          ]
        }
      },
      "inner_hits": {}
    }
  }
}
 

Результат поиска:

 "hits": [
      {
        "_index": "67890614",
        "_type": "_doc",
        "_id": "2",
        "_score": 1.2039728,
        "_source": {
          "orderNumber": "ORD-112",
          "items": [
            {
              "name": "part-3",
              "status": "paid"
            },
            {
              "name": "part-4",
              "status": "supplied"
            }
          ]
        },
        "inner_hits": {
          "items": {
            "hits": {
              "total": {
                "value": 1,
                "relation": "eq"
              },
              "max_score": 1.2039728,
              "hits": [
                {
                  "_index": "67890614",
                  "_type": "_doc",
                  "_id": "2",
                  "_nested": {
                    "field": "items",
                    "offset": 1
                  },
                  "_score": 1.2039728,
                  "_source": {
                    "name": "part-4",
                    "status": "supplied"      // note this
                  }
                }
              ]
            }
          }
        }
      }
    ]
 

Комментарии:

1. @Дэнни Барак, у вас была возможность ознакомиться с ответом, с нетерпением жду ваших отзывов 🙂

2. Спасибо @ESCOder, я знаком и понимаю этот подход. Я все еще задаюсь вопросом, следует ли устанавливать сопоставление перед созданием индекса или его можно установить и для существующего индекса. Другое дело, как будет работать сортировка после фильтрации.

3. @DannyBarack вам необходимо создать сопоставление перед индексированием данных, вы не можете обновить это сопоставление в существующем индексе

Ответ №2:

Elasticsearch удаляет соответствующее поле, поэтому не может определить, какой элемент в массиве был фактическим, который соответствует.

Как уже было сказано ранее, вы можете использовать вложенные запросы.

Как сглаживаются массивы объектов Elasticsearch не имеет понятия о внутренних объектах. Поэтому он сглаживает иерархии объектов в простой список имен полей и значений. Например, рассмотрим следующий документ:

 PUT my-index-000001/_doc/1
{
  "group" : "fans",
  "user" : [ 
    {
      "first" : "John",
      "last" :  "Smith"
    },
    {
      "first" : "Alice",
      "last" :  "White"
    }
  ]
}
 

Поле пользователя динамически добавляется как поле типа объект.

Предыдущий документ будет внутренне преобразован в документ, который больше похож на этот:

 {
  "group" :        "fans",
  "user.first" : [ "alice", "john" ],
  "user.last" :  [ "smith", "white" ]
}
 

Пользователь.первый и пользователь.последние поля сглаживаются в многозначные поля, и связь между Алисой и белым теряется. Этот документ неверно соответствовал бы запросу для Алисы И Смита:

 GET my-index-000001/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "user.first": "Alice" }},
        { "match": { "user.last":  "Smith" }}
      ]
    }
  }
}
 
 

Чтобы ответить на ваш вопрос:

Если вам нужно индексировать массивы объектов и поддерживать независимость каждого объекта в массиве, используйте вложенный тип данных вместо объектного типа данных.

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

 PUT my-index-000001
{
  "mappings": {
    "properties": {
      "user": {
        "type": "nested" 
      }
    }
  }
}

PUT my-index-000001/_doc/1
{
  "group" : "fans",
  "user" : [
    {
      "first" : "John",
      "last" :  "Smith"
    },
    {
      "first" : "Alice",
      "last" :  "White"
    }
  ]
}

GET my-index-000001/_search
{
  "query": {
    "nested": {
      "path": "user",
      "query": {
        "bool": {
          "must": [
            { "match": { "user.first": "Alice" }},
            { "match": { "user.last":  "Smith" }} 
          ]
        }
      }
    }
  }
}

GET my-index-000001/_search
{
  "query": {
    "nested": {
      "path": "user",
      "query": {
        "bool": {
          "must": [
            { "match": { "user.first": "Alice" }},
            { "match": { "user.last":  "White" }} 
          ]
        }
      },
      "inner_hits": { 
        "highlight": {
          "fields": {
            "user.first": {}
          }
        }
      }
    }
  }
}
 

Поле пользователя отображается как вложенный тип вместо объекта типа.

Этот запрос не совпадает, потому что Алиса и Смит не находятся в одном и том же вложенном объекте.

Этот запрос совпадает, потому что Алиса и Уайт находятся в одном и том же вложенном объекте.

inner_hits позволяют нам выделить соответствующие вложенные документы.

Взаимодействие с вложенными документами Вложенные документы могут быть:

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

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

для получения более подробной информации

вы можете проверить источник: https://www.elastic.co/guide/en/elasticsearch/reference/current/nested.html