我提供了产品的搜索功能,
用户可以按多个标签搜索
例如,用户可以搜索“iphone,128G,usa”
如果搜索字词在标题中匹配,则会得 3分,
如果搜索字词在标记中匹配,则会得分 1分。
如何重写当前查询以执行结果。
"title": "iphone 6 128G",
"tag": [
"usa",
"golden",
]
"title": "iphone 4 64G",
"tag": [
"usa",
"golden",
]
collection.aggregate(
{
"$match" => {
"tag":{ "$in"=> q_params },
}
},
{ "$unwind" => "$tag" },
{ "$match" => { "tag"=> { "$in"=> q_params } } },
{ "$group" => { "_id"=> {"title":"$title"},
"points"=> { "$sum"=>1 } } },
{ "$sort" => { "points"=> -1 } }
)
答案 0 :(得分:9)
我认为你正在以错误的方式接近这一点并且过多地询问"模糊匹配"来自数据库。相反,请考虑此修订数据样本:
db.items.insert([
{
"title": "iphone 6 128G",
"tags": [
"iphone",
"iphone6",
"128G",
"usa",
"golden",
]
},
{
"title": "iphone 4 64G",
"tags": [
"iphone",
"iphone4",
"64G",
"usa",
"golden",
]
}
])
现在你考虑一个"搜索短语"像这样:
" iphone4 128G usa"
然后你需要实现你自己的应用程序逻辑(不是一件很难的事情,只是引用主标记),扩展成这样的东西:
var searchedTags = ["iphone", "iphone4", "128G", "usa"]
您可以构建如下的管道查询:
db.items.aggregate([
{ "$match": { "tags": { "$in": searchedTags } } },
{ "$project": {
"title": 1,
"tags": 1,
"score": {
"$let": {
"vars": {
"matchSize":{
"$size": {
"$setIntersection": [
"$tags",
searchedTags
]
}
}
},
"in": {
"$add": [
"$$matchSize",
{ "$cond": [
{ "$eq": [
"$$matchSize",
{ "$size": "$tags" }
]},
"$$matchSize",
0
]}
]
}
}
}
}},
{ "$sort": { "score": -1 } }
])
返回以下结果:
{
"_id" : ObjectId("55b3551164518e494632fa19"),
"title" : "iphone 6 128G",
"tags" : [
"iphone",
"iphone6",
"128G",
"usa",
"golden"
],
"score" : 3
}
{
"_id" : ObjectId("55b3551164518e494632fa1a"),
"title" : "iphone 4 64G",
"tags" : [
"iphone",
"iphone4",
"64G",
"usa",
"golden"
],
"score" : 2
}
所以更多"标签"比赛一直赢。
但如果这句话改成了这样的话:
" iphone4 64G美国金色"
导致解析后的标签如下:
var searchedTags = ["iphone", "iphone4", "64G", "usa", "golden"]
然后相同的查询管道产生这个:
{
"_id" : ObjectId("55b3551164518e494632fa1a"),
"title" : "iphone 4 64G",
"tags" : [
"iphone",
"iphone4",
"64G",
"usa",
"golden"
],
"score" : 10
}
{
"_id" : ObjectId("55b3551164518e494632fa19"),
"title" : "iphone 6 128G",
"tags" : [
"iphone",
"iphone6",
"128G",
"usa",
"golden"
],
"score" : 3
}
在一个文档中,您不仅可以获得与其他文档上提供的标记更多匹配的好处,而且因为其中一个文档匹配"所有"提供的标签有一个额外的得分提升,推动它进一步上升排名比匹配相同数量的标签的东西。
为了打破这种局面,首先要考虑那里的$let
表达式声明了一个"变量"对于管道中的元素,我们不会重复自己"通过在多个位置为结果$$matchSize
值键入相同的表达式。
该变量本身是通过从searchedTags
数组的$setIntersection
和$tags
数组本身计算得到的数组来确定的。 "交叉口的结果"只是那些匹配的项目,这为测试该数组的$size
提供了空间。
稍后在将该匹配的$size
归因于"得分"时,通过三元$cond
给出另一个考虑因素,以查看$$matchSize
是否为$tags
等于$$matchSize
的原始长度。如果它是真的那么0
被添加到它自己(得分加倍"标记"长度)以成为"完全匹配"提供的标签,否则该条件的返回结果为db.items.createIndex({ "title": "text" })
db.items.find({
"$text": { "$search": "iphone 4 64G" } },
{ "score": { "$meta": "textScore" }}
).sort({ "score": { "$meta": "textScore" } })
。
使用$add
处理这两个数字结果会产生最终总数"得分"每个文件的价值。
这一点的主要观点是聚合框架缺乏运营商进行任何类型的模糊匹配"在诸如标题之类的字符串上。您可以在$regex
阶段$match
匹配,因为这基本上是一个查询运算符,它只会"过滤"结果
你可以"乱搞"有了它,但你真正想要的正则表达式是得到一个数字"得分"对于匹配的条款。这样的分裂(尽管在其他语言正则表达式运算符中可能)并不真正可用,因此简单地“标记化”#34;你的"标签"输入并将它们与文档"标记"。
匹配对于"数据库" (MongoDB主要是)这是一个更好的解决方案。或者,您甚至可以将其与$text
搜索运算符结合起来,以预测它自己的"得分"使用"解析标签"的组合对标题赋值逻辑如此处所示。这为"完全匹配提供了更多的有效性"。
它可以与聚合管道一起使用,但即使它本身也不会产生不良结果:
{
"_id" : ObjectId("55b3551164518e494632fa1a"),
"title" : "iphone 4 64G",
"tags" : [
"iphone",
"iphone4",
"64G",
"usa",
"golden"
],
"score" : 2
}
{
"_id" : ObjectId("55b3551164518e494632fa19"),
"title" : "iphone 6 128G",
"tags" : [
"iphone",
"iphone6",
"128G",
"usa",
"golden"
],
"score" : 0.6666666666666666
}
会产生:
renderer.setClearColor( 0xffffff, 1);
但是如果你只是想发送字符串而不想被"标记化"逻辑,并希望其他逻辑归因于您的"得分",然后查看专用文本搜索引擎,这比#"文本搜索"更好。甚至是像MongoDB这样的主要功能数据库的基本搜索功能。