FreeText搜索用例中的嵌套内部匹配

时间:2019-07-23 14:17:45

标签: json elasticsearch

我在销售汽车的网站上建立了标准的免费文本搜索。

在搜索框中,用户可以输入传递给查询的搜索词,该词用于匹配嵌套和非嵌套属性。

我正在使用inner_hits来限制查询返回的变体数量(在此示例中,variants并未从_source中删除)

在嵌套属性color上进行匹配时,inner_hits集合包含预期的正确变体。

但是,当对非嵌套属性title进行匹配时,inner_hits集合为空。我知道为什么它是空的。

您能建议一种更好的查询结构吗?

另一种选择是始终仅返回至少一个变体-但是如何实现?

映射

PUT test
{
  "mappings": {
    "car": {
      "properties": {
        "variants": {
          "type": "nested"
        }
      }
    }
  }
}

插入数据

PUT test/car/1
{
  "title": "VW Golf",
  "variants": [
    {
      "color": "red",
      "forsale": true
    },
    {
      "color": "blue",
      "forsale": false
    }
  ]
}

按颜色查询

GET test/_search
{
  "query": {
    "nested": {
      "path": "variants",
      "query": {
        "match": {
          "variants.color": "blue"
        }
      },
      "inner_hits": {}
    }
  }
}

颜色查询:按预期工作!

"hits" : [
      {
        "_source" : {
          "title" : "VW Golf",
          "variants" : [
            {
              "color" : "red",
              "forsale" : true
            },
            {
              "color" : "blue",
              "forsale" : false
            }
          ]
        },
        "inner_hits" : {
          "variants" : {
            "hits" : {
              "total" : 1,
              "hits" : [
                {
                  "_nested" : {
                    "field" : "variants",
                    "offset" : 1
                  },
                  "_source" : {
                    "color" : "blue",
                    "forsale" : false
                  }
                }
              ]
            }
          }
        }
      }
    ]

按品牌查询

GET test/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "title": "golf"
          }
        },
        {
          "nested": {
            "path": "variants",
            "query": {
              "match": {
                "variants.color": "golf"
              }
            },
            "inner_hits": {}
          }
        }
      ]
    }
  }
}

品牌查询结果:-(

"hits" : [
      {
        "_source" : {
          "title" : "VW Golf",
          "variants" : [
            {
              "color" : "red",
              "forsale" : true
            },
            {
              "color" : "blue",
              "forsale" : false
            }
          ]
        },
        "inner_hits" : {
          "variants" : {
            "hits" : {
              "total" : 0,
              "hits" : [ ]
            }
          }
        }
      }

2 个答案:

答案 0 :(得分:1)

您已经知道它,但是inner_hits返回一个空数组,因为在嵌套查询中没有匹配的嵌套文档。

一个简单的解决方案是更改查询,以使嵌套查询始终匹配。这可以通过将嵌套查询包装到bool查询中并添加match_all查询来完成。

如果将boost查询的match_all设置为0,它将不会对得分有所贡献。因此,如果嵌套文档匹配,它将是第一个。

现在,内部匹配项将不会为空,但是还有另一个问题,所有文档都将匹配。您可以:

  • min_score设置为非常小的值(例如0.00000001)以丢弃得分为0的文档
  • 复制原始的嵌套查询,并在2处使用minimum_should_match
{
    "query": {
        "bool": {
            // Ensure that at least 1 of the first 2 queries will match
            // The third query will always match
            "minimum_should_match": 2,
            "should": [
                {
                    "match": {
                        "title": <SEARCH_TERM>
                    }
                },
                {
                    "nested": {
                        "path": "variants",
                        "query": {
                            "match": {
                                "variants.color": <SEARCH_TERM>
                            }
                        }
                    }
                },
                {
                    "nested": {
                        "path": "variants",
                        "query": {
                            "bool": {
                                "should": [
                                    {
                                        "match": {
                                            "variants.color": <SEARCH_TERM>
                                        }
                                    },
                                    {
                                        // Disable scoring
                                        "match_all": { "boost": 0 }
                                    }
                                ]
                            }
                        },
                        "inner_hits": {}
                    }
                }
            ]
        }
    }
}

答案 1 :(得分:1)

一种实现方法是使用script_fields子句。

您将轻松编写一个小脚本,该脚本将执行以下操作:

  1. 将您从variants获得的 List 存储在变量中
  2. 然后遍历此 List
  3. 中的 Maps
  4. 如果地图具有 color蓝色,您将返回地图。 (如果没有结果为true,则返回一个空 地图)。这将为每个搜索结果创建一个附加字段,仅包含color为蓝色的那些变体。

一个重要的缺点是这是非常繁重的操作,尤其是在您有很多记录的情况下。

如果您只能做这种事情,可以采取这种方法,也许一年高峰时段以外可能会这样做一次。如果您的用例是经常使用的并且要由许多用户执行,那么我将更改映射,整体返回variants或选择其他解决方案。