我在MongoDB上使用Mongoose。这是我的模型的样子。
var BookSchema = new Schema({
name: String,
viewCount: { type: Number, default: 0 },
description: {
type: String,
default: 'No description'
},
body: {
type: String,
default: ''
}
}
});
我需要在Name, Description, Body
字段上搜索一些文本。到目前为止,这就是我所做的。它的工作:
Book.find().or([{ 'name': { $regex: term, $options: "$i" }}, { 'description': { $regex: term, $options: "$i" }}, { 'body': { $regex: term, $options: "$i" }}]).exec(
function (err, topics) {
if (err) {
return handleError(res, err);
}
return res.status(200).json(books);
});
问题:我需要想出一些机制,我分配权重/分数给所有字段(Name,Description,Body
), name
具有最高的权重,description
具有比名称和body
具有最小的权重。当结果出来时,我想按分数/权重对结果进行排序。
到目前为止,我已经查看了这个链接&权重,但不确定什么是获得预期结果的最佳方式。我也想了解,我是否需要在每次搜索之前创建权重,或者它是一次活动&如何使用Mongoose实现权重
"文本索引"和搜索确实可能是最好的选择,只要你是搜索整个单词。
在模式定义中添加文本索引非常简单:
BookSchema.index(
{
"name": "text",
"description": "text",
"body": "text"
},
{
"weights": {
"name": 5,
"description": 2
}
}
)
这允许你用"set"权重来执行简单的搜索:
Book.find({ "$text": { "$search": "Holiday School Year" } })
.select({ "score": { "$meta": "textScore" } })
.sort({ "score": { "$meta": "textScore" } })
.exec(function(err,result) {
}
);
每个匹配的词将根据它所在的字段进行考虑,该字段给出了最大的权重和出现次数。
赋予权重是附加在"索引"上的,所以定义只做一次,不能改变。另一个限制是,at"文本搜索"不会查看"部分"单词。例如,"ci"不匹配"City"或"Citizen",对于这种事情,您将需要正则表达式代替。
如果你需要更多的灵活性,或者通常必须能够动态地改变结果的权重,那么你需要像聚合框架或mapReduce这样的东西。
聚合框架不能执行"逻辑"匹配操作(它可以通过$match
操作符过滤,但不是"逻辑"匹配)的"正则表达式"到您的条件。如果合适的话,你可以使用单个单词和"精确"匹配。
Book.aggregate(
[
{ "$match": {
"$or": [
{ "name": /Holiday/ },
{ "description": /Holiday/ },
{ "body": /Holiday/ }
]
}},
{ "$project": {
"name": 1,
"description": 1,
"body": 1,
"score": {
"$add": [
{ "$cond": [{ "$eq": [ "$name", "Holiday" ] },5,0 ] },
{ "$cond": [{ "$eq": [ "$description", "Holiday" ] },2,0 ] },
{ "$cond": [{ "$eq": [ "$body", "Holiday" ] },1,0 ] }
]
}
}},
{ "$sort": { "score": -1 } }
],
function(err,results) {
}
)
作为一个聚合管道使用数据结构来查询,您可以在其中更改每个执行的权重参数为您当前需要的任何参数。
MapReduce也有类似的原则,你可以在主键的一部分中包含一个计算过的"分数"作为先导元素。MapReduce自然地对这个键发出的所有输入进行排序,作为提供给reduce函数的优化。但是,您不能进一步排序或"限制"这样的结果。