Elasticsearch如何实现文档所有分词均在查询词列表中的检索
方案结论
完全可以实现,你已经预存了文档分词数字段n_tokens的前提下,用terms_set查询即可高效满足需求,无需额外改造索引结构。
实现逻辑
你需要的检索逻辑等价于:文档name字段的每一个分词,都必须出现在给定的查询分词列表中。我们可以让ES对给定的查询分词列表做匹配,要求匹配成功的数量等于文档自身的分词总数n_tokens,即可满足所有分词都落在查询列表内的要求。
实现代码
Python调用示例如下:
# 给定的查询分词列表 query_tokens = ['my', 'tasty', 'big', 'red', 'apple'] es_client.search(index='my_index_name', body={ "size": 1000, "query": { "terms_set": { "name": { "terms": query_tokens, # 要求匹配上的分词数量等于文档自身的分词总数 "minimum_should_match_field": "n_tokens" } } } })
效果验证
和你给出的示例完全匹配:
- 文档
big apple:2个分词都在查询列表中,匹配数=2等于n_tokens=2,命中返回 - 文档
big red apple:3个分词都在查询列表中,匹配数=3等于n_tokens=3,命中返回 - 文档
big red orange:仅2个分词匹配,匹配数=2小于n_tokens=3,不命中
备选方案(无n_tokens字段时使用)
如果没有预存n_tokens字段,可以用Painless脚本查询实现,仅要求name字段有keyword类型的子字段即可,不过大数据量下性能低于terms_set方案,示例如下:
query_tokens = ['my', 'tasty', 'big', 'red', 'apple'] es_client.search(index='my_index_name', body={ "size": 1000, "query": { "bool": { "filter": { "script": { "script": { "source": """ def doc_tokens = doc['name.keyword'].value.splitOnToken(' '); for (token in doc_tokens) { if (!params.query_tokens.contains(token)) return false; } return true; """, "params": {"query_tokens": query_tokens} } } } } } })
内容的提问来源于stack exchange,提问作者Robert Axe
相关产品推荐
相关产品推荐

