【问题标题】:Searching in embeded comments in mongodb在 mongodb 中搜索嵌入的评论
【发布时间】:2014-09-02 21:23:12
【问题描述】:

我想做一个简单的“聊天”,其中有一个帖子和答案(只有 1 个深度),我决定采用this 方式,所以单个文档看起来像这样

{
    _id: ObjectId(...),
    posted: date,
    author: "name",
    content: "content",
    comments: [
        { posted: date,
          author:  "name2"},
          content: '...' 
        }, ... ]
}

我的问题是我应该如何以这种方式搜索内容?我首先需要在“父”内容中查找匹配项,然后是 cmets 列表中的内容。我该怎么做?

【问题讨论】:

  • 您能否提供有关搜索用例的更多详细信息?是否要在单个查询中进行搜索?
  • 在内容中查找字符串,包括“父”和“子”cmets。如果可以轻松完成单个查询,那就太好了。
  • 如果你可以搜索一个正则表达式,你可以使用: {$or : [{'content':{$regex:'your search regex'}}, {'cmets' : { $elemMatch : { 'content':{$regex:'你的搜索正则表达式'}}}]}。这行得通吗?
  • 查看docs.mongodb.org/manual/reference/operator/query/elemMatch 了解有关 elemMatch 功能的更多详细信息
  • @BaruchOxman 我尝试了您的查询,但如果找到“子”评论,则结果包含“父”评论。例如,如果我尝试这个: db.messages.find({$or : [{'message':{$regex:'.*Makefile.*'}}, {'cmets' : { $elemMatch: { 'message ':{$regex:'.*Makefile.*'}}}}]}, {'message': 1})并且在“child”消息中找到,结果将只包含“parents”消息

标签: javascript mongodb mapreduce mongodb-query


【解决方案1】:

如果您可以在每个内容中搜索正则表达式,您可以使用: {$or : [ {'content':{$regex:'your search regex'}}, {'comments' : { $elemMatch: { 'content':{$regex:'your search regex'}}}]}

请注意,在获取结果时,在匹配到父级或子级时,您将收到整个 mongo 文档,其中包含父级和子级。 如果您想避免这种情况(以确保您发现了什么),您可以先仅在父级上运行正则表达式查询,然后仅在子级上运行,而不是单个 $or 查询。

有关$elemMatch 的更多详细信息,请查看:docs.mongodb.org/manual/reference/operator/query/elemMatch

【讨论】:

    【解决方案2】:

    正如之前在 cmets 中所述,“查找”的基本查询只是在此处使用 $or 的简单问题,它也会在返回 true 的第一个条件上进行短路匹配。这里只有一个数组元素,所以不需要$elemMatch,只需使用“点表示法”,因为不需要多个字段匹配:

    db.messages.find({
        "$or": [
            { "content": { "$regex": ".*Makefile.*" } },
            { "comments.content": { "$regex": ".*Makefile.*" } }
        ]
    })
    

    这个确实实际上匹配了满足这些条件的文档,这就是.find()所做的。但是,您似乎正在寻找的是有点“funkier”的东西,您想在其中“区分”“父”结果和“子”结果。

    这有点超出了.find() 的范围,这种操作实际上是 MongoDB 其他操作的领域。不幸的是,当您正在寻找“字符串的一部分”以匹配您的条件时,在诸如聚合框架之类的东西中不存在与 $regex 操作等效的“逻辑”操作。如果确实如此,那将是最好的选择,但没有这样的comparison operator,并且您想要一个合乎逻辑的比较。这同样适用于基于“文本”的搜索,因为仍然需要区分父级和子级。

    不是最理想的方法,因为它确实涉及 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 }
        }
    )
    

    基本上与输入相同的查询确实会选择您想要的“文档”,并且实际上只是在此处使用“范围”,这样可以更容易地将正则表达式作为参数传递而无需重新编写 JavaScript 代码每次都包含该值。

    那里的逻辑很简单,只是针对您正在测试的每个“非规范化”元素,以查看正则表达式条件是否与该特定元素匹配。结果以“去规范化”的形式返回,并区分匹配的元素是父元素还是子元素。

    您可以更进一步,只需将其移至else 即可检查父级是否匹配。同样,如果这是您的愿望,您甚至可以通过某种方式返回“第一个”子匹配项。

    无论如何,这应该会让您走上最终代码的道路。但这是在服务器上处理这种区别的唯一方法的基本方法,客户端后处理将遵循大致相同的模式。

    【讨论】:

    • 似乎 'cmets' 可以包含多个 cmets(@kvictor - 如果我错了,请纠正我)
    • @BaruchOxman 你错了。请参阅文档中的"Dot Notation"。这不是在谈论数组位置中的元素,而是在谈论数组的“任何”元素。 $elemMatch 仅在需要匹配数组元素的多个字段时才需要。
    • 感谢您的时间和精力,mapReduce 完全按照我的预期工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-06-12
    • 2016-07-02
    • 2021-06-03
    • 2019-08-23
    • 2021-09-09
    • 2015-04-07
    • 1970-01-01
    相关资源
    最近更新 更多