当前位置: 首页 > 知识库问答 >
问题:

如何在Elasticsearch中使用无痛脚本获取数组字段的匹配字段值?

杨慎之
2023-03-14

我用的是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
            }
          ]
        }
      }
    ]
  }
}

非常感谢您的回答:)

共有1个答案

韦辰钊
2023-03-14

您可以使用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
            }
          ]
        }
      }
    ]

编辑1:对于需要使用嵌套查询的嵌套字段,不能直接对这些字段运行搜索。

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

< b >编辑2:要仅考虑嵌套查询的得分,您可以给它更高的提升,以便匹配您的嵌套得分的文档得分更高。如果你不想让你的多场比赛有任何分数。你可以把它放在constant _ score 0 boost中,匹配它的文档将得到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
                    }
                  }
                ]
              }
            }
          }
        }
      ]
    }
  }
}
 类似资料:
  • 以下是我的查询的简化版本: 希望有办法解决这个... 提前感谢你的帮助

  • 问题内容: 使用elasticsearch,我试图将计算出的字段添加到地理搜索中。我只想在搜索文档中附加一个额外的计算字段,但是当我通过“ script_fields”添加计算字段时,仅返回该字段。 我尝试添加通配符字段部分,但它不会影响结果。 如何使此查询返回添加了额外计算字段的? 问题答案: 不建议检索字段,而应使用源过滤。 所以,代替这个 用这个:

  • 我正在Node.js中编写一个RESTAPI,使用Mongoose访问MongoDB后端数据库。我想提供一个APIendpoint,它返回与特定variationStatus匹配的数组对象数量的计数。 这是我到目前为止得到的,但它给了我一个空洞的回答。。。 这是我的模型。。。 有人帮我吗? 谢谢

  • 在elasticsearch中,假设我有如下文档 有一个用例,如果我在搜索栏中键入“Bio”,我应该从elasticsearch中获得匹配的字段值和字段名。 对于本例, 输入:“生物” 预期产出: 我应该使用什么类型的查询?我可以想到使用NGram标记器,然后使用匹配查询。但是,我不确定如何只获得匹配的字段值(而不是整个文档)和相应的字段名作为输出。

  • 在Elasticsearch 6.2中,我有一个索引,其中有两个时间戳字段,如下所示: 在我无痛的内联脚本中,对于一些计算,我想将它们(和)转换为此字符串格式()。任何想法怎么能做到?我无法在ES官方文档中找到任何格式化文档。

  • 问题内容: 我正在尝试创建一个脚本字段,该脚本字段将计算两个时间戳之间的时间差,然后在该脚本字段上聚合一个。 我首先尝试: 在合计平均值下产生价值。 然后我尝试了: 生成了一条错误消息,内容为:“在映射中找不到[timedifference]的字段” 问题答案: 简单地将脚本移到聚合上怎么样?