草庐IT

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

coder 2023-11-01 原文

我正在用nodejs和mongod(不是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我正在尝试另一种方法,我将所有的用户数据嵌入到评论中,每当用户更新他们的用户名、姓名或图片时。他们也会在所有评论中更新它

最佳答案

问题
由于written before,在过度嵌入时存在几个问题:
问题1:bson大小限制
截至本文撰写之时,BSON documents are limited to 16MB。如果达到了这个限制,mongodb将抛出一个异常,并且您不能添加更多的注释,在最坏的情况下,如果更改会增加文档的大小,甚至不能更改(用户名)或图片。
问题2:查询限制和性能
在某些情况下,查询或排序comments数组是不容易的。有些事情需要一个相当昂贵的聚合,另一些则需要相当复杂的语句。
虽然有人可能会说,一旦查询到位,这并不是什么大问题,但我不同意。首先,查询越复杂,对开发人员和随后的mongodbs查询优化器来说,优化就越困难。在简化数据模型和查询方面,我取得了最好的结果,在一个实例中,响应速度提高了100倍。
在扩展时,与更简单的数据模型和相应的查询相比,复杂和/或代价高昂的查询所需的资源甚至可能总计为整台机器。
问题3:可维护性
最后但并非最不重要的是,您可能会遇到维护代码的问题。作为一个简单的经验法则
代码越复杂,就越难维护。代码越难维护,维护代码的时间就越长。维护代码的时间越长,成本就越高。
结论:复杂的代码是昂贵的。
在这种情况下,“昂贵”既指金钱(专业项目)也指时间(业余项目)。
(我的!)解决方案
这很简单:简化数据模型。因此,您的查询将变得不那么复杂(希望)更快。
步骤1:识别用例
这对我来说是个疯狂的猜测,但这里最重要的是向你展示一般的方法。我将您的用例定义如下:
对于给定的帖子,用户应该能够评论
对于给定的帖子,显示作者和评论,以及评论人和作者的用户名和图片
对于给定的用户,应该可以很容易地更改名称、用户名和图片
步骤2:相应地对数据建模
用户
首先,我们有一个简单的用户模型

{
  _id: new ObjectId(),
  name: "Joe Average",
  username: "HotGrrrl96",
  picture: "some_link"
}

这里没有什么新的,只是为了完整起见。
帖子
{
  _id: new ObjectId()
  title: "A post",
  content: " Interesting stuff",
  picture: "some_link",
  created: new ISODate(),
  author: {
    username: "HotGrrrl96",
    picture: "some_link"
  }
}

就为了一个职位。这里有两件事需要注意:首先,我们存储显示post时立即需要的author数据,因为这为我们保存了一个非常常见(如果不是普遍存在的话)用例的查询。为什么我们不把评论和评论数据保存在一起呢?由于16 MB size limit,我们试图防止在单个文档中存储引用。相反,我们将引用存储在注释文档中:
评论
{
  _id: new ObjectId(),
  post: someObjectId,
  created: new ISODate(),
  commenter: {
    username: "FooBar",
    picture: "some_link"
  },
  comment: "Awesome!"
}

与posts一样,我们拥有显示post所需的所有数据。
询问
我们现在所取得的成果是,我们绕过了bson的大小限制,我们不需要引用用户数据就可以显示帖子和评论,这将为我们节省很多查询。但是让我们回到用例和更多的查询
添加注释
现在这完全是直截了当了。
获取给定帖子的全部或部分评论
所有评论
db.comments.find({post:objectIdOfPost})

最后3条评论
db.comments.find({post:objectIdOfPost}).sort({created:-1}).limit(3)

因此,为了显示一篇文章及其所有(或部分)评论,包括用户名和图片,我们有两个查询。比你以前需要的更多,但是我们绕过了大小限制,基本上你可以有一个无限数量的评论每一篇文章。但是让我们去做一些真实的事情
获取最新的5篇文章及其最新的3条评论
这是一个两步的过程。但是,如果有适当的索引(稍后将返回到该索引),这仍然应该很快(从而节省资源):
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"}
  }
);

我们将旧用户名推送到相应的数组中。这是一种安全措施,以防以下操作出现问题。此外,为了确保数据的持久性,我们将write concern设置为相当高的级别。
db.posts.update(
  { "author.username": "HotGrrrl96"},
  { $set:{ "author.username": "Joe Cool"} },
  {
    multi:true,
    writeConcern: {w:"majority"}
  }
)

这里没什么特别的。注释的update语句看起来几乎相同。虽然这些查询需要一些时间,但很少执行。
指数
根据经验,可以说mongodb每个查询只能使用一个索引。虽然这并不完全正确,因为存在索引交叉点,但很容易处理。另一件事是复合索引中的各个字段可以独立使用。因此,一种简单的索引优化方法是找到在使用索引的操作中使用最多字段的查询,并创建它们的复合索引。请注意,查询中的发生顺序很重要。所以,我们继续吧。
帖子
db.posts.createIndex({"author.username":1,"created":-1})

评论
db.comments.createIndex({"post":1, "created":-1})

结论
诚然,每篇文章都有一个完全嵌入的文档是加载它和它的评论的最快方式。但是,它不能很好地扩展,而且由于处理它所必需的复杂查询的性质,这种性能优势可能被利用,甚至被消除。
有了以上的解决方案,您就可以以一定的速度(如果!)与基本上无限制的可伸缩性和更直接的数据处理方式相比。
Hth.

关于node.js - MongoDB查询注释以及用户信息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32409635/

有关node.js - MongoDB查询注释以及用户信息的更多相关文章

  1. ruby - ECONNRESET (Whois::ConnectionError) - 尝试在 Ruby 中查询 Whois 时出错 - 2

    我正在用Ruby编写一个简单的程序来检查域列表是否被占用。基本上它循环遍历列表,并使用以下函数进行检查。require'rubygems'require'whois'defcheck_domain(domain)c=Whois::Client.newc.query("google.com").available?end程序不断出错(即使我在google.com中进行硬编码),并打印以下消息。鉴于该程序非常简单,我已经没有什么想法了-有什么建议吗?/Library/Ruby/Gems/1.8/gems/whois-2.0.2/lib/whois/server/adapters/base.

  2. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

    大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

  3. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  4. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  5. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  6. ruby-on-rails - 在 Rails 和 ActiveRecord 中查询时忽略某些字段 - 2

    我知道我可以指定某些字段来使用pluck查询数据库。ids=Item.where('due_at但是我想知道,是否有一种方法可以指定我想避免从数据库查询的某些字段。某种反拔?posts=Post.where(published:true).do_not_lookup(:enormous_field) 最佳答案 Model#attribute_names应该返回列/属性数组。您可以排除其中一些并传递给pluck或select方法。像这样:posts=Post.where(published:true).select(Post.attr

  7. ruby-on-rails - 简单的 Ruby on Rails 问题——如何将评论附加到用户和文章? - 2

    我意识到这可能是一个非常基本的问题,但我现在已经花了几天时间回过头来解决这个问题,但出于某种原因,Google就是没有帮助我。(我认为部分问题在于我是一个初学者,我不知道该问什么......)我也看过O'Reilly的RubyCookbook和RailsAPI,但我仍然停留在这个问题上.我找到了一些关于多态关系的信息,但它似乎不是我需要的(尽管如果我错了请告诉我)。我正在尝试调整MichaelHartl'stutorial创建一个包含用户、文章和评论的博客应用程序(不使用脚手架)。我希望评论既属于用户又属于文章。我的主要问题是:我不知道如何将当前文章的ID放入评论Controller。

  8. ruby - RVM "ERROR: Unable to checkout branch ."单用户 - 2

    我在新的Debian6VirtualBoxVM上安装RVM时遇到问题。我已经安装了所有需要的包并使用下载了安装脚本(curl-shttps://rvm.beginrescueend.com/install/rvm)>rvm,但以单个用户身份运行时bashrvm我收到以下错误消息:ERROR:Unabletocheckoutbranch.安装在这里停止,并且(据我所知)没有安装RVM的任何文件。如果我以root身份运行脚本(对于多用户安装),我会收到另一条消息:Successfullycheckedoutbranch''安装程序继续并指示成功,但未添加.rvm目录,甚至在修改我的.bas

  9. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  10. 阿里云国际版免费试用:如何注册以及注意事项 - 2

    作为新的阿里云用户,您可以50免费试用多种优惠,价值高达1,700美元(或8,500美元)。这将让您了解和体验阿里云平台上提供的一系列产品和服务。如果您以个人身份注册免费试用,您将获得价值1,700美元的优惠。但是,如果您是注册公司,您可以选择企业免费试用,提交基本信息通过企业实名注册验证,即可开始价值$8,500的免费试用!本教程介绍了如何设置您的帐户并使用您的免费试用版。​关于免费试用在我们开始此试用之前,您还必须遵守以下条款和条件才能访问您的免费试用:只有在一年内创建的账户才有资格获得阿里云免费试用。通过此免费试用优惠,用户可以免费试用免费试用活动页面上列出的每种产品一次。如果您有多个帐

随机推荐