Node.js MongoDB查询注释以及用户信息

Node.js MongoDB查询注释以及用户信息,node.js,mongodb,Node.js,Mongodb,我正在用nodejs和mongood(不是mongoose)创建一个应用程序。我有一个问题,让我头痛了几天,任何人都请建议一种方法!!。 我有这样的mongodb设计 post{ _id:ObjectId(...), picture: 'some_url', comments:[ {_id:ObjectId(...), user_id:Object('123456'), body:"some content" }, {_id:ObjectI

我正在用nodejs和mongood(不是mongoose)创建一个应用程序。我有一个问题,让我头痛了几天,任何人都请建议一种方法!!。 我有这样的mongodb设计

post{
  _id:ObjectId(...),
  picture: 'some_url',
  comments:[
    {_id:ObjectId(...),
     user_id:Object('123456'),
     body:"some content"
    },
    {_id:ObjectId(...),
     user_id:Object('...'),
     body:"other content"
    } 
  ]
}

user{
 _id:ObjectId('123456'),
 name: 'some name', --> changable at any times
 username: 'some_name', --> changable at any times
 picture: 'url_link' --> changable at any times
}
我想查询帖子以及所有用户信息,因此查询如下:

[{
  _id:ObjectId(...),
  picture: 'some_url',
  comments:[
    {_id:ObjectId(...),
     user_id:Object('123456'),
     user_data:{
         _id:ObjectId('123456'),
         name: 'some name',
         username: 'some_name',
         picture: 'url_link'
     }
     body:"some content"
    },
    {_id:ObjectId(...),
     user_id:Object('...'),
     body:"other content"
    } 
  ]
}]
我尝试使用loop手动获取用户数据并添加到评论中,但事实证明这很困难,而且我的编码技能无法实现:(

如果你有任何建议,我将不胜感激


p/s我正在尝试另一种方法,我会将所有用户数据嵌入到注释中,每当用户更新其用户名、名称或图片时。他们也会在所有注释中更新它。您遵循的是规范化数据模型方法。如果您遵循此模型,则意味着您必须编写另一个查询以获取用户info或者,如果您使用嵌入式文档存储,则每当更新用户文档时,所有用户文档都必须更改。 阅读此链接了解更多信息。

问题 如前所述,过度嵌入时存在几个问题:

问题1:BSON大小限制 在撰写本文时,。如果达到该限制,MongoDB将抛出异常,您将无法添加更多注释,在最坏的情况下,如果更改会增加文档的大小,甚至不会更改(用户名)或图片

问题2:查询限制和性能 在某些条件下,查询或排序comments数组是不容易的。有些事情需要相当昂贵的聚合,而有些事情则需要相当复杂的语句

虽然有人可能会说,一旦查询到位,这并不是什么大问题,但我不同意。首先,对于开发人员和随后的MongoDB查询优化器来说,查询越复杂,优化就越困难。我已经通过简化数据模型和查询获得了最好的结果,将响应速度提高了一倍f 100在一个实例中

在扩展时,与更简单的数据模型和相应的查询相比,复杂和/或昂贵的查询所需的资源甚至可能总计为整个机器

问题3:可维护性 最后但并非最不重要的一点是,维护代码时可能会遇到问题

代码越复杂,维护就越困难。代码越难维护,维护代码所需的时间就越多。维护代码所需的时间越多,维护代码的成本就越高

结论:复杂的代码是昂贵的

在这种情况下,“昂贵”指的是金钱(用于专业项目)和时间(用于业余项目)

(我的!)解决方案 这很简单:简化数据模型。因此,您的查询将变得不那么复杂,并且(希望)更快

步骤1:确定您的用例 这对我来说是一个猜测,但这里重要的是向您展示一般方法。我将按如下方式定义您的用例:

  • 对于给定的帖子,用户应该能够发表评论
  • 对于给定的帖子,显示作者和评论,以及评论者和作者的用户名和图片
  • 对于给定的用户,应该可以很容易地更改名称、用户名和图片
  • 步骤2:相应地为数据建模 使用者 首先,我们有一个简单的用户模型

    {
      _id: new ObjectId(),
      name: "Joe Average",
      username: "HotGrrrl96",
      picture: "some_link"
    }
    
    这里没有什么新内容,只是为了完整性而添加

    帖子 这就是关于一篇文章的内容。这里有两件事需要注意:首先,我们在显示一篇文章时存储了我们立即需要的作者数据,因为这为我们保存了一个非常常见(如果不是普遍存在的话)用例的查询。为什么我们不将注释和注释数据按顺序保存?因为ces在单个文档中。相反,我们将引用存储在注释文档中:

    评论 与post一样,我们拥有显示post所需的所有数据

    疑问 我们现在所取得的成就是,我们绕过了BSON的大小限制,我们不需要为了能够显示帖子和评论而引用用户数据,这将为我们节省大量的查询。但让我们回到用例和一些更多的查询

    添加评论 现在这完全是直截了当的

    获取给定帖子的全部或部分评论 所有评论

    db.comments.find({post:objectIdOfPost})
    
    最新的3条评论

    db.comments.find({post:objectIdOfPost}).sort({created:-1}).limit(3)
    
    因此,对于显示一篇文章及其所有(或部分)评论,包括用户名和图片,我们有两个查询。比您以前需要的更多,但我们绕过了大小限制,基本上您可以为每篇文章提供无限数量的评论。但让我们来看看真实的情况

    获取最新的5篇帖子和最新的3条评论 这是一个分为两步的过程。但是,通过适当的索引(稍后再讨论),这仍然应该是快速的(从而节省资源):

    获取给定用户的所有帖子,从最新到最旧进行排序,并获取其评论 请注意,这里只有两个查询。虽然您需要“手动”在帖子和它们各自的评论之间建立连接,但这应该非常简单

    更改用户名 这可能是一个很少执行的用例。然而,对于所说的数据模型来说,它并不是很复杂

    首先,我们更改用户文档

    db.users.update(
      { username: "HotGrrrl96"},
      {
        $set: { username: "Joe Cool"},
        $push: {oldUsernames: "HotGrrrl96" }
      },
      {
        writeConcern: {w: "majority"}
      }
    );
    
    我们将旧用户名推送到相应的数组中。这是一种安全措施,以防以下操作出错。此外,我们将写关注点设置为一个相当高的级别,以确保数据的持久性

    db.posts.update(
      { "author.username": "HotGrrrl96"},
      { $set:{ "author.username": "Joe Cool"} },
      {
        multi:true,
        writeConcern: {w:"majority"}
      }
    )
    
    这里没什么特别的。注释的update语句看起来几乎一样。虽然这些查询需要一些时间,但很少执行

    指数 根据经验,可以说MongoDB在每个查询中只能使用一个索引
    var posts = db.posts.find().sort({created:-1}).limit(5)
    posts.forEach(
      function(post) {
        doSomethingWith(post);
        var comments = db.comments.find({"post":post._id}).sort("created":-1).limit(3);
        doSomethingElseWith(comments);
      }
    )
    
    var posts = db.posts.find({"author.username": "HotGrrrl96"},{_id:1}).sort({"created":-1});
    var postIds = [];
    posts.forEach(
      function(post){
        postIds.push(post._id);
      }
    )
    var comments = db.comments.find({post: {$in: postIds}}).sort({post:1, created:-1});
    
    db.users.update(
      { username: "HotGrrrl96"},
      {
        $set: { username: "Joe Cool"},
        $push: {oldUsernames: "HotGrrrl96" }
      },
      {
        writeConcern: {w: "majority"}
      }
    );
    
    db.posts.update(
      { "author.username": "HotGrrrl96"},
      { $set:{ "author.username": "Joe Cool"} },
      {
        multi:true,
        writeConcern: {w:"majority"}
      }
    )
    
    db.posts.createIndex({"author.username":1,"created":-1})
    
    db.comments.createIndex({"post":1, "created":-1})