Elasticsearch 6.2如何按数组元素逐一精准搜索?
咱们先理清楚你遇到的问题根源:Elasticsearch默认会把数组的所有元素“扁平化”处理——简单说就是把数组里每个元素的所有词项都合并到同一个倒排索引里。所以你用multi_match加operator: and查询"ikea desk"时,只要"ikea"出现在数组的某一个元素里,"desk"出现在另一个元素里,就会匹配到文档,而不是要求两个词必须同时出现在同一个数组元素中。
要实现针对数组单个元素的搜索(也就是两个查询词必须在同一个include_products元素里),最靠谱的方式是用Elasticsearch的nested类型,下面给你一步步讲怎么操作:
1. 修改字段映射为nested类型
首先得把include_products字段改成nested类型,这样数组里的每个元素都会被当作独立的子文档来索引,咱们就能针对单个元素做查询了。
情况A:把数组元素包装成对象(推荐)
如果你能调整数据结构,把原来的字符串数组改成对象数组(比如[{"name": "ikea chair"}, {"name": "marketO desk"}]),映射可以这么设置:
PUT /你的索引名/_mapping/information { "properties": { "title": { "type": "text" }, "include_products": { "type": "nested", "properties": { "name": { "type": "text" } } } } }
情况B:不修改数据结构(直接用字符串数组)
如果没法改数据,也可以直接把字符串数组设为nested类型:
PUT /你的索引名/_mapping/information { "properties": { "title": { "type": "text" }, "include_products": { "type": "nested", "type": "text" } } }
2. 使用nested查询进行精准搜索
映射改好后,就可以用nested查询来指定在每个数组元素内部匹配了:
对应情况A的查询语句
{ "query": { "nested": { "path": "include_products", "query": { "multi_match": { "query": "ikea desk", "operator": "and", "fields": ["include_products.name"] } } } } }
对应情况B的查询语句
{ "query": { "nested": { "path": "include_products", "query": { "multi_match": { "query": "ikea desk", "operator": "and", "fields": ["include_products"] } } } } }
临时替代方案(不修改映射)
如果暂时没法改索引映射,你可以试试match_phrase查询,但这个方法有局限性——它要求查询词的顺序和连续性完全匹配数组元素,比如只有元素是"ikea desk"才会命中,"desk ikea"就不行:
{ "query": { "match_phrase": { "include_products": "ikea desk" } } }
所以还是优先推荐nested类型的方案,它能灵活支持各种匹配场景,不管词的顺序如何,只要两个词在同一个数组元素里就能命中。
内容的提问来源于stack exchange,提问作者JSKIM

