首页 > 解决方案 > 如何在 Elasticsearch 中使用无痛脚本获取数组字段的匹配字段值?

问题描述

我正在使用 Elasticsearch 7.6

我在餐厅索引中有如下文件:

  "name" : "ABC restaurant",
  "menu" : [
    {
      "name" : "chicken",
      "count" : 23
    },
    {
      "name" : "rice",
      "count" : 10        }
   ]

计数是指收到的订单数。

当客户在网站中按菜单名称搜索时,我想给几家餐厅中菜单数量多的餐厅打高分,并将其展示在搜索结果的顶部。

为此,似乎有必要知道无痛脚本中每个文档中的匹配菜单。

我想知道这是可能的。如果是这样,我该怎么做?


更新感谢您的回答@jaspreet chahal

我做了这样的索引:

PUT restaurant
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text"
      },
      "menu":{
        "type": "nested", 
        "properties": {
          "name": {"type": "text"},
          "count": {"type": "integer"}
        }
      }
    }
  }
}

POST /restaurant/_doc/1
{
  "name": "ABC Restaurant",
  "menu": [
    {"name": "chicken", "count": 3},
    {"name": "cake", "count": 5}
  ]
}

POST /restaurant/_doc/2
{
  "name": "TEST Restaurant",
  "menu": [
    {"name": "chicken", "count": 10},
    {"name": "cake", "count": 7},
    {"name": "rice", "count": 2}
  ]
}


POST /restaurant/_doc/3
{
  "name": "Good Restaurant",
  "menu": [
    {"name": "chicken", "count": 20},
    {"name": "cake", "count": 13},
    {"name": "rice", "count": 5}
  ]
}

我要做的是在使用多重匹配时根据匹配的菜单计数获得总分,如下所示:

GET restaurant/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "function_score": {
            "query": {
              "bool": {
                "must": [
                  {
                    "multi_match": {
                      "query": "chicken",
                      "type": "cross_fields",
                      "fields": [
                        "menu.name", 
                        "name"
                      ],
                      "operator": "and"
                    }
                  }
                ]
              }
            },
            "boost_mode": "replace",
            "functions": [
              {
                "field_value_factor": {
                  "field": "menu.count",
                  "missing": 0
                }
              }
            ]
          }
        }
      ]
    }
  }
}

但是上面的查询没有得到任何结果。

为了使它工作,我在菜单映射中添加了“include_in_root:True”。但是在这种情况下,我无法获得正确的分数..(似乎无论搜索词如何,都获得了菜单计数的最低分数)

请问如何按照我的预期进行这项工作?谢谢 !


再次更新。

我在您的查询中添加了多重匹配

GET restaurant/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "multi_match": {
            "query": "Good Restaurant chicken", 
            "type": "cross_fields", 
            "fields": [
              "menu.name",
              "name"
            ]
          }
        },
        {
          "nested": {
            "path": "menu",
            "query": {
              "function_score": {
                "query": {
                  "bool": {
                    "should": [
                      {
                        "match": {
                          "menu.name": {
                            "query": "Good Restaurant chicken",
                            "operator": "or"
                          }
                        }
                      }
                    ]
                  }
                },
                "boost_mode": "replace",
                "functions": [
                  {
                    "field_value_factor": {
                      "field": "menu.count",
                      "missing": 0
                    }
                  }
                ]
              }
            }
          }
        }
      ]
    }
  }
}

它得到了所有的结果!但是分数受到多匹配查询的影响。

这是查询的结果:

  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 3,
      "relation" : "eq"
    },
    "max_score" : 21.11436,
    "hits" : [
      {
        "_index" : "restaurant",
        "_type" : "_doc",
        "_id" : "3",
        "_score" : 21.11436,
        "_source" : {
          "name" : "Good Restaurant",
          "menu" : [
            {
              "name" : "chicken",
              "count" : 20
            },
            {
              "name" : "cake",
              "count" : 13
            },
            {
              "name" : "rice",
              "count" : 5
            }
          ]
        }
      },
      {
        "_index" : "restaurant",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 10.133532,
        "_source" : {
          "name" : "TEST Restaurant",
          "menu" : [
            {
              "name" : "chicken",
              "count" : 10
            },
            {
              "name" : "cake",
              "count" : 7
            },
            {
              "name" : "rice",
              "count" : 2
            }
          ]
        }
      },
      {
        "_index" : "restaurant",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 3.1335313,
        "_source" : {
          "name" : "ABC Restaurant",
          "menu" : [
            {
              "name" : "chicken",
              "count" : 3
            },
            {
              "name" : "cake",
              "count" : 5
            }
          ]
        }
      }
    ]
  }
}

非常感谢您的回答 :)

标签: elasticsearchelasticsearch-painless

解决方案


您可以使用function_score根据计数值为嵌套文档提供更高的分数。

询问:

{
  "query": {
    "nested": {
      "path": "menu",
      "query": {
        "function_score": {
          "score_mode": "sum",
          "boost_mode": "replace",
          "query": {
            "match": {
              "menu.name": "chicken"
            }
          },
          "functions": [
            {
              "field_value_factor": {
                "field": "menu.count"
              }
            }
          ]
        }
      }
    }
  }
}

结果:

"hits" : [
      {
        "_index" : "index63",
        "_type" : "_doc",
        "_id" : "tA8IPHIBzLrvZDnz-ghE",
        "_score" : 23.0,
        "_source" : {
          "name" : "ABC restaurant",
          "menu" : [
            {
              "name" : "chicken",
              "count" : 23
            },
            {
              "name" : "rice",
              "count" : 10
            }
          ]
        }
      },
      {
        "_index" : "index63",
        "_type" : "_doc",
        "_id" : "tQ8JPHIBzLrvZDnz-AiA",
        "_score" : 20.0,
        "_source" : {
          "name" : "XYZ restaurant",
          "menu" : [
            {
              "name" : "chicken",
              "count" : 20
            },
            {
              "name" : "rice",
              "count" : 8
            }
          ]
        }
      }
    ]

Edit1: 对于需要使用嵌套查询的嵌套字段,您不能直接在这些字段上运行搜索。

{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "name": {
              "operator": "and",
              "query": "chicken"
            }
          }
        },
        {
          "nested": {
            "path": "menu",
            "query": {
              "function_score": {
                "query": {
                  "bool": {
                    "must": [
                      {
                        "match": {
                          "menu.name": {
                            "query": "chicken",
                            "operator": "and"
                          }
                        }
                      }
                    ]
                  }
                },
                "boost_mode": "replace",
                "functions": [
                  {
                    "field_value_factor": {
                      "field": "menu.count",
                      "missing": 0
                    }
                  }
                ]
              }
            }
          }
        }
      ]
    }
  }
}

Edit2:要仅考虑来自嵌套查询的分数,您可以给它更高的提升,以便与您的嵌套分数匹配的文档得分更高。如果您不希望您的多重匹配有任何分数。您可以将它放在 constant_score 中,提升为 0,与此匹配的文档将获得 0 分

{
  "query": {
    "bool": {
      "should": [
        {
          "constant_score": {
            "filter": {
              "multi_match": {
                "query": "Good Restaurant chicken",
                "type": "cross_fields",
                "fields": [
                  "name"
                ]
              }
            },
            "boost": 0
          }
        },
        {
          "nested": {
            "path": "menu",
            "query": {
              "function_score": {
                "query": {
                  "bool": {
                    "should": [
                      {
                        "match": {
                          "menu.name": {
                            "query": "Good Restaurant chicken",
                            "operator": "or"
                          }
                        }
                      }
                    ]
                  }
                },
                "boost_mode": "replace",
                "functions": [
                  {
                    "field_value_factor": {
                      "field": "menu.count",
                      "missing": 0
                    }
                  }
                ]
              }
            }
          }
        }
      ]
    }
  }
}

推荐阅读