Elasticsearch 按数组字段中的对象中的多个字段进行过滤

Elasticsearch filter by multiple fields in an object which is in an array field

目标是过滤具有多个价格的产品。

数据如下所示:

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

我想按 membershipLevelprice 过滤。 比如如果我是白银会员,查询价格区间0-10,应该不会出现该商品,如果我是金卡会员,应该会出现商品"a" . Elasticsearch支持这种查询吗?

您必须使用嵌套字段和嵌套查询来存档:https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-nested-query.html

定义类型为 "Nested" 的价格 属性,然后您将能够按每个 属性 嵌套对象

进行过滤

让我向您展示如何使用 nested fields and query and filter context。我将以您的示例向您展示如何定义索引映射、索引示例文档和搜索查询。

需要注意的是Elasticsearch映射中的include_in_parent参数,它允许我们在不使用嵌套字段的情况下使用这些嵌套字段。

请参考Elasticsearch文档。

If true, all fields in the nested object are also added to the parent document as standard (flat) fields. Defaults to false.

索引定义

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

索引示例文档

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

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

搜索查询以显示价格范围为 0-10

Gold
{
    "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
                        }
                    }
                }
            ]
        }
    }
}

以上查询没有 return 任何结果,因为没有任何匹配结果。

P.S :- 所以答案可能会帮助您了解嵌套字段并详细查询它们。

您需要为您的用例使用 nested datatype for price and make use of 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. 
    }
  }
}

上述查询的意思是,我想return price.price范围从0 to 10price.membershipLevel的所有文档为Gold

请注意,我使用了 inner_hits。原因是尽管是嵌套文档,作为响应的 ES 将 return 整个文档集,而不仅仅是特定于查询子句适用位置的文档。

为了找到匹配的确切嵌套文档,您需要使用 inner_hits

以下是响应方式 return。

回复:

{
  "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"
                  }
                }
              ]
            }
          }
        }
      }
    ]
  }
}

希望对您有所帮助!