了解 MongoDB 中的多对多关系以及如何取消引用集合

Posted

技术标签:

【中文标题】了解 MongoDB 中的多对多关系以及如何取消引用集合【英文标题】:Understanding Many-to-Many relationships in MongoDB and how to dereference collections 【发布时间】:2014-09-03 06:07:54 【问题描述】:

我花了一些时间研究用于实现多对多关系的 MongoDB 替代方案,包括几篇 *** 文章(here 和 here)和 these 幻灯片。

我正在使用 MEAN 堆栈创建一个应用程序,并且我正在尝试确认我的架构设置和取消引用对象集合的最佳实践。

我在用户和会议之间建立了基本的多对多关系(想想为用户安排会议,其中一个用户可以参加许多会议并且一个会议包含多个用户)。

鉴于我的用例,我认为最好使用引用而不是嵌入。我相信(根据我所读到的内容),仅当我的会议具有单个会议独有的用户时,才使用嵌入会更好。在我的情况下,这些相同的用户在会议之间共享。此外,虽然更新用户的频率并不高(例如,更改用户名、密码),但我仍然觉得使用参考是正确的——尽管我愿意接受意见。

假设我参考了我有以下(简化的)架构:

var MeetingSchema = new Schema(
  description: 
    type: String,
    default: '',
    required: 'Please fill in a description for the meeting',
    trim: true
  ,
  location: 
    type: String,
    default: '',
    required: 'Please fill in a location for the meeting',
    trim: true
  ,
  users: [ 
    type: Schema.ObjectId,
    ref: 'User'
   ]
);

var UserSchema = new Schema(
    firstName: 
        type: String,
        trim: true,
        default: '',
        validate: [validateLocalStrategyProperty, 'Please fill in your first name']
    ,
    lastName: 
        type: String,
        trim: true,
        default: '',
        validate: [validateLocalStrategyProperty, 'Please fill in your last name']
    ,
    email: 
        type: String,
        trim: true,
        default: '',
        validate: [validateLocalStrategyProperty, 'Please fill in your email'],
        match: [/.+\@.+\..+/, 'Please fill a valid email address']
    ,
    username: 
        type: String,
        unique: true,
        required: 'Please fill in a username',
        trim: true
    ,
    password: 
        type: String,
        default: '',
        validate: [validateLocalStrategyPassword, 'Password should be longer']
    
);

首先,您会注意到我没有用户中的会议集合。我决定不添加此集合,因为我相信我可以使用 MongoDB 查找功能来获取与特定用户相关的所有会议 - 即,

db.meetings.find(users:ObjectId('x123'));

当然我需要添加一些索引。

现在,如果我希望在特定会议上尊重我的用户,我该怎么做?对于那些了解 Rails 并知道:include and :join 之间区别的人,我正在寻找一个类似的概念。我知道我们不是在处理 MongoDB 中的联接,但对我来说,为了从会议中取消引用用户集合以获取用户的名字和姓氏,我需要循环遍历 id 的集合并执行某种数据库。 users.find() 为每个 id。我假设我可以进行一些简单的 MongoDB 调用,以使其以高性能的方式发生。

【问题讨论】:

如果您在特定会议中拥有所有用户的 ID,您只需针对用户集合进行一次查找即可获取他们的姓名: db.users.find(id:$in:,firstName:1,lastName:1) 本质上。为什么不将用户的名字和姓氏存储在会议集合的用户数组中? 谢谢。所以听起来我必须做一个单独的(单个)查询来让用户使用 $in 操作 - 我想这很好,尽管我想知道我是否可以将两个查询合并为一个(获取会议和用户的第一个和姓氏)​​?我不只是存储名字和姓氏的原因是我将不得不更新那些在用户更改名字的罕见的最后一个。我想这是一个不错的权衡,这意味着我应该只存储这些并处理罕见的情况。 完全正确 - 为什么要针对可能发生的事情进行优化 - 另外,您真的需要在已经举行的会议中更新某人的姓名吗?我认为,如果他们以 Joe Shmoe 的身份参加那次会议,那么在事后 就不应该改变它,因为现在他被称为 Joe Doe……即使你确实想改变它,它也是一个单个“多”更新。 【参考方案1】:

关于 MongoDB 中的模式设计的讨论,正是这个主题,我建议您参考 MongoDB 博客上的这些帖子:

Part 1 Part 2 Part 3

特别是,查看向您展示如何进行应用程序级连接的示例 javascript 代码。

【讨论】:

谢谢,这是一组很棒的文章。因此,Mongoose 的 populate() 似乎本质上是对数据进行“应用程序级连接”/取消引用。对吗? 这正是 populate() 所做的。我建议根据您在文章中学到的内容重新阅读此页面 mongoosejs.com/docs/populate.html> 文档中没有显示查询的实际实现方式。由于查询的执行出现在填充函数之后,我假设猫鼬正在将查询优化为单个查询而不是多个查询,当然不是 n+1。在传统的 rdbms 中,“填充”函数应该只进行一次连接。由于 mongodb 没有连接,我假设填充在辅助查询中执行 $in 子句(如您的示例),而不是以某种方式将主表的检索与集合组合到单个查询中。 我对猫鼬了解不多。 populate() 的定义在这里 AFAICT:github.com/LearnBoost/mongoose/blob/3.8.x/lib/…

以上是关于了解 MongoDB 中的多对多关系以及如何取消引用集合的主要内容,如果未能解决你的问题,请参考以下文章

如何处理MongoDB中的多对多关系?

社交应用的多对多关系:Mongodb 或 Neo4j 等图形数据库

MongoDB中的多对多关系

MongoDB中的多对多

删除 NSManagedObject 子类不会取消它的多对多关系

更新 Prisma 中的多对多关系