Фильтр Elasticsearch по нескольким полям в объекте, который находится в поле массива - PullRequest
1 голос
/ 10 марта 2020

Цель состоит в том, чтобы фильтровать товары по нескольким ценам.

Данные выглядят так:

{
  "name":"a",
  "price":[
    {
      "membershipLevel":"Gold",
      "price":"5"
    },
    {
      "membershipLevel":"Silver",
      "price":"50"
    },
    {
      "membershipLevel":"Bronze",
      "price":"100"
    }
    ]
}

Я бы хотел отфильтровать по membershipLevel и price. Например, , если я серебряный участник и ценовой диапазон запроса 0-10, продукт не должен появляться, но если я золотой участник, должен появиться продукт "a". Поддерживается ли этот тип запросов Elasticsearch?

Ответы [ 3 ]

1 голос
/ 10 марта 2020

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

Важно отметить параметр include_in_parent в сопоставлении Elasticsearch, который позволяет нам использовать эти вложенные поля без использования вложенных полей.

Пожалуйста, обратитесь к документации Elasticsearch об этом.

Если true, все поля во вложенном объекте также добавляются к родителю документ в виде стандартных (плоских) полей. По умолчанию установлено значение false.

Индекс Def

{
    "mappings": {
        "properties": {
            "product": {
                "type": "nested",
                "include_in_parent": true
            }
        }
    }
}

Индекс образцов документов

{
    "product": {
        "price" : 5,
        "membershipLevel" : "Gold"
    }
}
{
    "product": {
        "price" : 50,
        "membershipLevel" : "Silver"
    }
}

{
    "product": {
        "price" : 100,
        "membershipLevel" : "Bronze"
    }
}

Поисковый запрос для показа Gold с диапазоном цен 0-10

{
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "product.membershipLevel": "Gold"
                    }
                }
            ],
            "filter": [
                {
                    "range": {
                        "product.price": {
                            "gte": 0,
                            "lte" : 10
                        }
                    }
                }
            ]
        }
    }
}

Результат

"hits": [
            {
                "_index": "so-60620921-nested",
                "_type": "_doc",
                "_id": "1",
                "_score": 1.0296195,
                "_source": {
                    "product": {
                        "price": 5,
                        "membershipLevel": "Gold"
                    }
                }
            }
        ]

Поисковый запрос для исключения Silver с тем же ценовым диапазоном

{
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "product.membershipLevel": "Silver"
                    }
                }
            ],
            "filter": [
                {
                    "range": {
                        "product.price": {
                            "gte": 0,
                            "lte" : 10
                        }
                    }
                }
            ]
        }
    }
}

Вышеупомянутый запрос не возвращает никакого результата так как нет никакого соответствующего результата.

PS: - этот SO ответ может помочь вам понять вложенные поля и запросить их подробно.

1 голос
/ 10 марта 2020

Вам необходимо использовать nested тип данных для price и использовать nested query для вашего случая использования.

См. Приведенное ниже отображение, образец документа, запрос и ответ:

Отображение:

PUT my_price_index
{
  "mappings": {
    "properties": {
      "name":{
        "type":"text"
      },
      "price":{
        "type":"nested",
        "properties": {
          "membershipLevel":{
            "type":"keyword"
          },
          "price":{
            "type":"double"
          }
        }
      }
    }
  }
}

Образец документа:

POST my_price_index/_doc/1
{
  "name":"a",
  "price":[
    {
      "membershipLevel":"Gold",
      "price":"5"
    },
    {
      "membershipLevel":"Silver",
      "price":"50"
    },
    {
      "membershipLevel":"Bronze",
      "price":"100"
    }
    ]
}

Запрос:

POST my_price_index/_search
{
  "query": {
    "nested": {
      "path": "price",
      "query": {
        "bool": {
          "must": [
            {
              "term": {
                "price.membershipLevel": "Gold"
              }
            },
            {
              "range": {
                "price.price": {
                  "gte": 0,
                  "lte": 10
                }
              }
            }
          ]
        }
      },
      "inner_hits": {}           <---- Do note this. 
    }
  }
}

Приведенный выше запрос означает, что я хочу вернуть все документы с диапазоном price.price от 0 to 10 и price.membershipLevel как Gold.

Обратите внимание, что я использовал inner_hits. Причина в том, что несмотря на то, что это вложенный документ, ES в качестве ответа будет возвращать весь набор документов, а не только документ, указанный c, к которому применимо условие запроса.

Чтобы найти точное вложенное до c, которое было найдено, вам необходимо использовать inner_hits.

Ниже приведен ответ.

Ответ:

{
  "took" : 128,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 1.9808291,
    "hits" : [
      {
        "_index" : "my_price_index",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.9808291,
        "_source" : {
          "name" : "a",
          "price" : [
            {
              "membershipLevel" : "Gold",
              "price" : "5"
            },
            {
              "membershipLevel" : "Silver",
              "price" : "50"
            },
            {
              "membershipLevel" : "Bronze",
              "price" : "100"
            }
          ]
        },
        "inner_hits" : {
          "price" : {
            "hits" : {
              "total" : {
                "value" : 1,
                "relation" : "eq"
              },
              "max_score" : 1.9808291,
              "hits" : [
                {
                  "_index" : "my_price_index",
                  "_type" : "_doc",
                  "_id" : "1",
                  "_nested" : {
                    "field" : "price",
                    "offset" : 0
                  },
                  "_score" : 1.9808291,
                  "_source" : {
                    "membershipLevel" : "Gold",
                    "price" : "5"
                  }
                }
              ]
            }
          }
        }
      }
    ]
  }
}

Надеюсь, это поможет!

0 голосов
/ 10 марта 2020

Для архивирования необходимо использовать вложенные поля и вложенный запрос: https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-nested-query.html

Определите свойство Price с типом "Nested", и тогда вы сможете фильтровать по каждому свойство вложенного объекта

...