基本上,我正在尝试在模式匹配上对文档进行最佳排序。我当前的文档看起来像
{name: "Name2", likes: ["Apple","Cat"]}
{name: "Name1", likes: ["Apple","Ball"]}
{name: "Name3", likes: ["Moon","Light"]}
我想根据喜欢的匹配查找和排序文档。如果我通过{likes: ["Apple","Ball"]}
,我想按以下顺序获取文件:
{name: "Name1", likes: ["Apple","Ball"]}
{name: "Name2", likes: ["Apple","Cat"]}
{name: "Name3", likes: ["Moon","Light"]}
如果这可以用Javascript实现,那么我也想看看。
答案 0 :(得分:1)
您要寻找的是为匹配数分配一个“权重”,并以此进行排序。对于您要问的“精确”问题,最好的选择是使用聚合框架:
var test = ["Apple", "Ball"];
db.collection.aggregate([
{ "$addFields": {
"score": {
"$size": {
"$setIntersection": [ test, "$likes" ]
}
}
}},
{ "$sort": { "score": -1 } }
])
哪个会给您这样的结果:
{ "name" : "Name1", "likes" : [ "Apple", "Ball" ], "score" : 2 }
{ "name" : "Name2", "likes" : [ "Apple", "Cat" ], "score" : 1 }
{ "name" : "Name3", "likes" : [ "Moon", "Light" ], "score" : 0 }
简而言之,$addFields
是一种将“新字段”投射到文档中的方法,您可以交替使用$project
或类似的阶段进行此类操作。
添加到文档中的"score"
字段是通过使用$setIntersection
聚合运算符将您的源数组["Apple", "Ball"]
与该属性的$likes
属性中存在的数组进行比较而构成的文献。您在此处使用$
来指代该字段的“值”。
$setIntersection
的结果是一个“数组”(或“集合”),其中包含与第一个参数中给出的数组与第二个参数中给出的数组相匹配的项目。我们真正想要的只是使用$size
运算符提供"score"
值来表示匹配数的结果“集合”的总“长度”。
将这个新属性添加到文档后,您可以使用$sort
并按"score"
负排序方向指定的“降序”使用-1
值。
替代方法通常是,除特殊情况外,大多数现实世界中的搜索对文档完全不感兴趣,没有匹配项,也没有分数。
在这种情况下,改用"text search"可能更加实用和有效。
首先,您将在要搜索的文档属性上创建“文本索引”:
db.collection.createIndex({ "likes": "text" });
然后,您只需将值提供为以空格分隔的列表即可:
var test = ["Apple", "Ball"];
var search = test.join(" "); // Makes the string "Apple Ball"
db.collection.find(
{ "$text": { "$search": search } },
{ "score": { "$meta": "textScore" } }
).sort( { "score": { "$meta": "textScore" } } )
哪个会返回如下结果:
{ "name" : "Name1", "likes" : [ "Apple", "Ball" ], "score" : 2.1 }
{ "name" : "Name2", "likes" : [ "Apple", "Cat" ], "score" : 1 }
因此,您永远不会包含与给出的术语完全不相关的文档,但是在更高的工作负载下,结果要高效得多,而且速度要快得多。还要注意,操作“固有地”使用了“索引”,这是使用聚合框架计算得出的过程无法做到的。
基本上,这些是您在服务器上进行排序的方法。聚合管道功能强大,您可以做很多事情,但是与本地查询运算符和索引实际上可以为您做的事情相比,代价通常是性能下降。