2015-07-20 147 views
1

我想创建一个小型的MongoDB搜索查询,我想根据结果集对完全匹配进行排序,然后输入no。的比赛。MongoDB搜索和排序,匹配数量和精确匹配

例如,如果我有以下标签

Physics 
11th-Physics 
JEE-IIT-Physics 
Physics-Physics 

然后,如果我搜索“物理学”,它应该排序为

Physics 
Physics-Physics 
11th-Physics 
JEE-IIT-Physics 

回答

3

寻找那种“得分王”你在这里谈论的是一个锻炼; Tibial在“不完美的解决方案”中。在这种情况下,“最适合”在这里以“文本搜索”开始,“不完美”是在使用MongoDB的文本搜索功能时首先要考虑的术语。

MongoDB“不是”专用的“文本搜索”产品,也不是(像大多数数据库)一样。 “文本搜索”的全部功能是专门用于那些专业领域的产品。所以也许不是最合适的,但是对于那些可以忍受这些限制并且不想实现另一个引擎的人来说,“文本搜索”是一个选项。或者然而!至少。

说了那么,让我们来看看你的可以做的数据样本给出。首先设置一些数据的收集:

db.junk.insert([ 
    { "data": "Physics" }, 
    { "data": "11th-Physics" }, 
    { "data": "JEE-IIT-Physics" }, 
    { "data": "Physics-Physics" }, 
    { "data": "Something Unrelated" } 
]) 

那当然“使”文本搜索capabilties的,那么你需要指数与“文本”索引类型的文档中的字段中的至少一个:

db.junk.createIndex({ "data": "text" }) 

既然是“蓄势待发”,让我们来看看第一个基本查询:

db.junk.find(
    { "$text": { "$search": "\"Physics\"" } }, 
    { "score": { "$meta": "textScore" } } 
).sort({ "score": { "$meta": "textScore" } }) 

这是要给结果是这样的:

{ 
    "_id" : ObjectId("55af83b964876554be823f33"), 
    "data" : "Physics-Physics", 
    "score" : 1.5 
} 
{ 
    "_id" : ObjectId("55af83b964876554be823f30"), 
    "data" : "Physics", 
    "score" : 1 
} 
{ 
    "_id" : ObjectId("55af83b964876554be823f31"), 
    "data" : "11th-Physics", 
    "score" : 0.75 
} 
{ 
    "_id" : ObjectId("55af83b964876554be823f32"), 
    "data" : "JEE-IIT-Physics", 
    "score" : 0.6666666666666666 
} 

因此,这与您想要的结果“接近”,但当然没有“完全匹配”组件。另外,文本搜索功能在$text运算符中使用的逻辑意味着“物理 - 物理”是此处的首选匹配。

这是因为然后引擎不识别“非单词”,如之间的“连字符”。对此,“物理”一词在该文档的索引内容中出现过几次,因此它的分数较高。

现在,您的逻辑的其余部分取决于“完全匹配”的应用以及您的意思。如果你正在寻找字符串中的“物理”和周围没有“连字符”或其他字符的“不”,那么以下不适合。但是,你可以只匹配字段,“值”,即“准确”只是“物理”:

db.junk.aggregate([ 
    { "$match": { 
     "$text": { "$search": "Physics" } 
    }}, 
    { "$project": { 
     "data": 1, 
     "score": { 
      "$add": [ 
       { "$meta": "textScore" }, 
       { "$cond": [ 
        { "$eq": [ "$data", "Physics" ] }, 
        10, 
        0 
       ]} 
      ] 
     } 
    }}, 
    { "$sort": { "score": -1 } } 
]) 

这会给你一个结果是既着眼于由发动机产生的“textScore”,然后应用一些数学与逻辑测试。在这种情况下“数据”是完全等于“物理学”,那么我们“重”的得分由一个附加因素使用$add

{ 
    "_id": ObjectId("55af83b964876554be823f30"), 
    "data" : "Physics", 
    "score" : 11 
} 
{ 
    "_id" : ObjectId("55af83b964876554be823f33"), 
    "data" : "Physics-Physics", 
    "score" : 1.5 
} 
{ 
    "_id" : ObjectId("55af83b964876554be823f31"), 
    "data" : "11th-Physics", 
    "score" : 0.75 
} 
{ 
    "_id" : ObjectId("55af83b964876554be823f32"), 
    "data" : "JEE-IIT-Physics", 
    "score" : 0.6666666666666666 
} 

这正是aggregation framework能为你做什么,通过允许操作返回的数据附加条件。最终结果传递给$sort阶段(注意它按降序排列)以允许将新值排序。

但是聚合框架确实只能处理字符串上的“精确匹配”。目前没有工具来处理正则表达式匹配或字符串中的索引位置,这些字符串为投影返回有意义的值。甚至没有合乎逻辑的匹配。并且$regex操作仅用于在查询中“过滤”,因此在此处未使用。

因此,如果您在“短语”中查找某些内容比“字符串等于”完全匹配更具吸引力,那么其他选项使用mapReduce

这是另一种“不完美”的方法,因为mapReduce命令的限制意味着来自引擎的这种查询的“textScore”“完全消失”。虽然实际文档将被正确选择,但引擎不能使用继承的“排名数据”。这是MongoDB首先将“分数”投影到文档中的副产品,“投影”不是mapReduce可用的功能。

但是你可以“玩”使用JavaScript,我的“不完美”的样品中的字符串:

db.junk.mapReduce(
    function() { 
     var _id = this._id, 
      score = 0; 

     delete this._id; 

     score += this.data.indexOf(search); 
     score += this.data.lastIndexOf(search); 

     emit({ "score": score, "id": _id }, this); 
    }, 
    function() {}, 
    { 
     "out": { "inline": 1 }, 
     "query": { "$text": { "$search": "Physics" } }, 
     "scope": { "search": "Physics" } 
    } 
) 

其中给出的结果是这样的:

{ 
    "_id" : { 
     "score" : 0, 
     "id" : ObjectId("55af83b964876554be823f30") 
    }, 
    "value" : { 
     "data" : "Physics" 
    } 
}, 
{ 
    "_id" : { 
     "score" : 8, 
     "id" : ObjectId("55af83b964876554be823f33") 
    }, 
    "value" : { 
     "data" : "Physics-Physics" 
    } 
}, 
{ 
    "_id" : { 
     "score" : 10, 
     "id" : ObjectId("55af83b964876554be823f31") 
    }, 
    "value" : { 
     "data" : "11th-Physics" 
    } 
}, 
{ 
    "_id" : { 
     "score" : 16, 
     "id" : ObjectId("55af83b964876554be823f32") 
    }, 
    "value" : { 
     "data" : "JEE-IIT-Physics" 
    } 
} 

我自己的“愚蠢的小算法“这里基本上是将匹配字符串的”第一个“和”最后“索引位置放在这里,并将它们加在一起得出分数。这可能不是你真正想要的,但问题是,如果你可以用JavaScript编写你的逻辑,那么你可以把它放在引擎上以产生所需的“排名”。

唯一真正的“绝招”这里要记住的是,“分数” 必须是“前述”分组“钥匙”在这里,的一部分,如果包括原单文档_id值,则该组合键的一部分必须被重新命名,否则_id将优先顺序。

这只是mapReduce的一部分,其中作为“优化”的所有输出“键”值在由减速器处理之前按“升序”排序。当然,这里什么都不做,因为我们不是“聚合”,而是一般地使用JavaScript运行器和文档整形mapReduce


所以总的说明是,那些是可用的选项。他们没有一个是完美的,但你可能能够和他们一起生活,甚至只是“接受”默认的引擎结果。

如果您想要更多,然后看看外部“专用”文本搜索产品,这将是更适合。


旁注:这里的$text搜索优于$regex,因为他们可以使用索引。一个“非锚定”的正则表达式(没有脱字符号^)不能在MongoDB中最佳地使用索引。因此,$text搜索通常会成为查找短语中“单词”的更好基础。