【问题标题】:Elasticsearch filter by multiple fields in an object which is in an array fieldElasticsearch 按数组字段中的对象中的多个字段过滤
【发布时间】:2020-03-10 15:34:01
【问题描述】:

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

数据如下:

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

我想按membershipLevelprice 进行过滤。 例如如果我是白银会员,查询价格范围0-10,产品不应该出现,但如果我是黄金会员,产品“a”应该出现。 Elasticsearch 是否支持这种查询?

【问题讨论】:

    标签: elasticsearch elasticsearch-nested


    【解决方案1】:

    您需要为price 使用nested 数据类型,并为您的用例使用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 10price.membershipLevel 的文档返回为Gold

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

    为了找到匹配的确切嵌套文档,您需要使用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"
                      }
                    }
                  ]
                }
              }
            }
          }
        ]
      }
    }
    

    希望这会有所帮助!

    【讨论】:

      【解决方案2】:

      让我向您展示如何使用nested fieldsquery and filter context。我会以你的例子来展示,你如何定义索引映射、索引示例文档和搜索查询。

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

      请参考 Elasticsearch 文档。

      如果为真,则嵌套对象中的所有字段也会添加到父对象中 文档作为标准(平面)字段。默认为 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
                              }
                          }
                      }
                  ]
              }
          }
      }
      

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

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

      【讨论】:

      • 感谢您的详细解释!但是,我对您的“索引示例文档”有点困惑,您是否建议多次插入具有不同价格/会员信息的相同产品? (我在示例中的理解是插入了 3 个产品)
      • 是的,你是对的。这些样品适用于不同的产品
      • 但我需要的是一种具有多个价格水平的产品,你的意思是我们重复相同的产品*(价格数量)次吗?例如:Doc1: {productA,price1}, Doc2:{productA,price2} Doc3:{productA,price3} 等等...?
      • @MiDaa,给我一些时间,我会在某个时候发布它
      【解决方案3】:

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

      使用“嵌套”类型定义您的价格属性,然后您将能够按嵌套对象的每个属性进行过滤

      【讨论】:

      • 这可以通过使用嵌套查询来完成,因为 OP 是 Elasticsearch 的新手,他很难理解它,请参考我的回答,了解如何在没有嵌套查询的情况下实现它,提示:在嵌套属性中使用"include_in_parent": true 实现。
      猜你喜欢
      • 1970-01-01
      • 2016-04-03
      • 1970-01-01
      • 1970-01-01
      • 2021-06-23
      • 1970-01-01
      • 1970-01-01
      • 2014-11-17
      • 2019-05-17
      相关资源
      最近更新 更多