我想做一个简单的"聊天",那里有一篇帖子和他们的答案(只有1个深度),我决定走这条路,所以一个文档看起来像这个
{
_id: ObjectId(...),
posted: date,
author: "name",
content: "content",
comments: [
{ posted: date,
author: "name2"},
content: '...'
}, ... ]
}
我的问题是,我应该如何以这种方式搜索内容?我首先需要在"父"内容中查找匹配项,然后在注释列表中查找内容。我该怎么做?
如果您可以在每个内容中搜索正则表达式,您可以使用:{$or : [
{'content':{$regex:'your search regex'}},
{'comments' : { $elemMatch: { 'content':{$regex:'your search regex'}}}]}
请注意,在获取结果时,一旦与父级或子级匹配,您将收到包含父级和子级的整个mongo文档。如果您想避免这种情况(以确定您已经找到了什么),您可以首先只对父级运行regex查询,然后只对子级运行regex查询,而不是单个$or
查询。
有关$elemMatch
的更多详细信息,请访问:docs.mongoodb.org/manual/reference/operator/query/elemMatch
正如前面的注释中所述,"find"的基本查询只是在此处使用$or
的一个简单问题,它还对返回true
的第一个条件进行短路匹配。这里只有一个数组元素,所以不需要$elemMatch
,只需要使用"点表示法",因为不需要多个字段匹配:
db.messages.find({
"$or": [
{ "content": { "$regex": ".*Makefile.*" } },
{ "comments.content": { "$regex": ".*Makefile.*" } }
]
})
这个确实匹配了符合这些条件的文档,这就是.find()
所做的。然而,你似乎在寻找一个有点"更时髦"的东西,你想在"父"结果和"子"结果之间"辨别"。
这有点超出了.find()
的范围,而且这种操作实际上是MongoDB其他操作的领域。不幸的是,当您正在寻找与条件匹配的"字符串的一部分"时,在聚合框架之类的东西中不存在执行类似于$regex
操作的"逻辑"操作。如果是这样的话,这将是最好的选择,但没有这样的比较运算符,逻辑比较就是你想要的。这同样适用于基于"文本"的搜索,因为仍然需要区分父代和子代。
这不是最理想的方法,因为它确实涉及JavaScript处理,但这里的下一个最佳选项是mapReduce()
。
db.messages.mapReduce(
function() {
// Check parent
if ( this.content.match(re) != null )
emit(
{ "_id": this._id, "type": "P", "index": 0 },
{
"posted": this.posted,
"author": this.author,
"content": this.content
}
);
var parent = this._id;
// Check children
this.comments.forEach(function(comment,index) {
if ( comment.content.match(re) != null )
emit(
{ "_id": parent, "type": "C", "index": index },
{
"posted": comment.posted,
"author": comment.author,
"content": comment.content
}
);
});
},
function() {}, // no reduce as all are unique
{
"query": {
"$or": [
{ "content": { "$regex": ".*Makefile.*" } },
{ "comments.content": { "$regex": ".*Makefile.*" } }
]
},
"scope": { "re": /.*Makefile.*/ },
"out": { "inline": 1 }
}
)
基本上,与此输入的查询相同的是,它确实选择了您想要的"文档",而这里真正使用的是"scope",它可以更容易地将正则表达式作为参数传递,而无需每次重写JavaScript代码以包含该值。
这里的逻辑很简单,只针对您正在测试的每个"去规范化"元素,看看正则表达式条件是否与该特定元素匹配。返回的结果是"去规范化"的,并区分匹配的元素是父元素还是子元素。
您可以更进一步,只需将其移动到else
,就可以不必检查子代是否匹配。同样,如果你愿意的话,你甚至可以通过某种方式返回"第一个"孩子的匹配。
无论如何,这应该会让你走上最终代码的道路。但这是在服务器上处理这种区别的唯一方法的基本方法,而客户端的后期处理将遵循大致相同的模式。