PUT /goods/goods/1
{
"tag": ["apple","gold","4G"]
}
PUT /goods/goods/2
{
"tag": ["apple","gold"]
}
有货物有多个标签,我们也有多个标签的用户。我们试图找到标签是用户标签子集的商品。
例如:
user ["apple","gold","4G","fruit"] find goods 1 2
user ["apple","gold","4G"] find goods 1 2
user ["apple","gold"] find goods 2
user ["apple"] find nothing
Elasticsearch能实现吗?
答案 0 :(得分:0)
您希望实现的行为类似于Finding Multiple Exact Values中描述的行为。
由于你展示的最后一个例子,这不是直接可能的:
user ["apple"] find nothing
Elasticsearch必须构建一个查询find me all goods that have only one tag 'apple'
,并且不可能直接用倒排索引实现。它只存储包含该标签的文档的ID;此列表将包含所有包含该标记的文档,无论它们是否还包含其他标记。有关详细信息,请参阅此page。
虽然Exact Match有一种解决方法,但在您的情况下,您希望在Elasticsearch中检索作为子集的文档,即包含查询中的任意数量的元素,但仅此而已。与完全匹配的情况类似,它无法有效地完成。
如果您的数据允许,可能会出现解决方法。例如,如果您的商品/用户标签矩阵稀疏(意味着他们很少共享相同的标签),您可以使用带有脚本过滤器的bool should
查询来实现目标:
POST goods/goods/_search
{
"query": {
"bool": {
"filter": {
"bool": {
"must": [
{
"bool": {
"should": [
{
"term": {
"tag": {
"value": "apple"
}
}
},
{
"term": {
"tag": {
"value": "gold"
}
}
},
{
"term": {
"tag": {
"value": "4G"
}
}
}
]
}
},
{
"script": {
"script": {
"inline": "int k = 0; for (int i = 0; i < doc['tag'].length; ++i) { if (!params.user_tags.contains(doc['tag'][i])) { k +=1} } k == 0",
"lang": "painless",
"params": {
"user_tags": [
"apple",
"gold",
"4G"
]
}
}
}
}
]
}
}
}
}
}
查询执行以下操作(以简化方式):
should
部分:匹配包含任何用户标签的所有文档; script
部分:过滤掉包含其他标记的内容 script
查询允许执行几乎任何可能的查询,但它肯定不会像内置term
查询那样高效(因为它不会使用索引,而是会执行完整的查询 - 在最坏的情况下扫描。)
我们的想法是同时使用term
和script
并在AND(bool must
查询中)将它们统一起来,因此脚本部分仅应用于可能匹配的文档。 (关于在Elasticsearch中应用查询的顺序的注释可以找到here。)
与Elasticsearch一样,为了获得最佳性能,您可能需要重新排列数据(例如添加tag_count
等)。试着找出最适合你的。
希望有所帮助!