问题 与nosql(mongodb和mongoose)的多对多关系


我和mongoDb和mongoose.js有很多很多关系,我知道有很多选择,我的情况是这样的:

我有两个文档,用户和项目,一个用户可以有很多项目,一个项目可以有很多用户,所以在我的情况下我有4个选项:

1 - 项目文档中的id_user数组。

2 - 用户文档中的id_project数组。

3 - 项目文档中的id_user数组&&数组   用户文档中的id_project。

4 - 映射用户和项目关系的第三个表(如a   关系型数据库)。

选项1和2不可用,因为,想象在选项1的场景中如果我想从用户找到所有项目,我将不得不在用户的每个项目文档数组中查找此用户ID(遍历此数组在每个项目中),这绝对不是一个好方法。

选项3很好,但我必须进行某种交易以确保两个文件都会被写入,这并不是那么糟糕,因为这两个文件的读取都比写的要多得多

选项4更简单,因为当我向项目添加一个用户时,它只是添加一个带有两个id的新文档(我认为这是一个很好的解决方案,因为我不需要关心事务,这是一个很好的解决方案吗?)

那么,什么是最好的解决方案?


6286
2017-08-03 03:36


起源



答案:


相反,解决方案1和2是你最好的选择。当更新/创建频率与项目和用户的读取频率相比非常少时,可以考虑解决方案3,即使更新/创建,它需要两个查询,阅读的便利性将弥补这一点。

要在解决方案1和2中进行选择,您需要考虑读取频率。您是否需要更频繁地使用用户的项目或项目的使用并根据该选择进行选择。如果您认为两者的频率相对相同,则最好将用户对象保持为尽可能少的群集。无论您选择哪种方案,都要考虑保留 index 在存储的数组上 _id(项目或用户)。

对于前者

userSchema = new Schema(
            {//otherstuff
               project_ids: [{type: Schema.Types.ObjectId, ref: 'Project'}})
              ...
            }) 
userSchema.index({'project_ids':1})

要么

projectSchema = new Schema(
            {//otherstuff
               user_ids: [{type: Schema.Types.ObjectId, ref: 'User'}})
              ...
            }) 
projectSchema.index({'user_ids':1})

保持数组的索引 _id 将会大大提高你的查询速度,你担心会有很大的开销。

但保持 index 只有这种关系是一个重要的关系,正在进行大量的查询。如果这只是项目的一个侧面功能,那么您可以这样做 without 索引也是。

如果用户可以做很多事情并且有很多关系,那么您将在整个应用程序中不断地需要该用户对象,因此如果您的应用程序不是特定于项目的,那么最好不要将项目ID放在用户模式中。但是,由于我们只是放置ID,所以无论如何都不是很大的开销。无需担心。

两个阵列上的Reg索引:是的,你可以当然。但是,当您使用解决方案3时,您根本不需要索引,因为您不会进行查询以获取用户的项目列表或项目中的用户列表。解决方案3使阅读变得非常简单,但写得有点麻烦。但正如您提到的那样,您的用例涉及到 reading>>writing使用解决方案3,但总是存在数据不一致的危险,您需要注意这一点。

索引只会让事情变得更快。经过 文档 并做一些谷歌搜索。没有什么花哨。查询索引数组比普通数组更有效。对于前者我们假设您使用解决方案2。 将项目ID存储在project_ids字段中。

您可以轻松获得用户的项目。这是直截了当的。

但要获得project1的用户。你需要这样的查询。

User.find({project_ids:project._id},function(err,docs){
     //here docs will be the list of the users of project1
})
//The above query might be slow if the user base is large. 
//But it can be improved vastly by indexing the project_ids field in the User schema.

类似于解决方案1.每个项目都有user_ids字段。让我们假设我们有一个user1。 要获取用户的项目,我们执行以下查询

Project.find({user_ids:user1._id},function(err,docs){
      //here docs will be the projects of user1
      //But it can be improved vastly by indexing the user_ids field in the Project schema.

如果你正在考虑解决方案1和解决方案2,我猜测解决方案1更好。在某些情况下,您可能需要没有项目的用户,但是没有用户要求项目的可能性非常低。但这取决于您的确切用例。


7
2017-08-03 09:36



“最好将用户对象保持为尽可能少的群集”你的意思是什么?我可以在两个模式(项目和用户)中使用此索引吗? - Rodrigo Fonseca
解决方案1和解决方案2并不好,因为两种方式几乎都会以相同的数量读取,但它们的写入频率会非常低 - Rodrigo Fonseca
@RodrigoFonseca检查编辑。 - ma08
索引基本上排列文档,使得对索引字段的查询超快。 1表示升序索引,您可以使用-1来降序索引。这里真的没关系,但确实有数字和日期。究竟。经历交易的陷阱(失败的情况,即不一致)并评估您的选择。 - ma08
@RodrigoFonseca检查编辑。 - ma08


答案:


相反,解决方案1和2是你最好的选择。当更新/创建频率与项目和用户的读取频率相比非常少时,可以考虑解决方案3,即使更新/创建,它需要两个查询,阅读的便利性将弥补这一点。

要在解决方案1和2中进行选择,您需要考虑读取频率。您是否需要更频繁地使用用户的项目或项目的使用并根据该选择进行选择。如果您认为两者的频率相对相同,则最好将用户对象保持为尽可能少的群集。无论您选择哪种方案,都要考虑保留 index 在存储的数组上 _id(项目或用户)。

对于前者

userSchema = new Schema(
            {//otherstuff
               project_ids: [{type: Schema.Types.ObjectId, ref: 'Project'}})
              ...
            }) 
userSchema.index({'project_ids':1})

要么

projectSchema = new Schema(
            {//otherstuff
               user_ids: [{type: Schema.Types.ObjectId, ref: 'User'}})
              ...
            }) 
projectSchema.index({'user_ids':1})

保持数组的索引 _id 将会大大提高你的查询速度,你担心会有很大的开销。

但保持 index 只有这种关系是一个重要的关系,正在进行大量的查询。如果这只是项目的一个侧面功能,那么您可以这样做 without 索引也是。

如果用户可以做很多事情并且有很多关系,那么您将在整个应用程序中不断地需要该用户对象,因此如果您的应用程序不是特定于项目的,那么最好不要将项目ID放在用户模式中。但是,由于我们只是放置ID,所以无论如何都不是很大的开销。无需担心。

两个阵列上的Reg索引:是的,你可以当然。但是,当您使用解决方案3时,您根本不需要索引,因为您不会进行查询以获取用户的项目列表或项目中的用户列表。解决方案3使阅读变得非常简单,但写得有点麻烦。但正如您提到的那样,您的用例涉及到 reading>>writing使用解决方案3,但总是存在数据不一致的危险,您需要注意这一点。

索引只会让事情变得更快。经过 文档 并做一些谷歌搜索。没有什么花哨。查询索引数组比普通数组更有效。对于前者我们假设您使用解决方案2。 将项目ID存储在project_ids字段中。

您可以轻松获得用户的项目。这是直截了当的。

但要获得project1的用户。你需要这样的查询。

User.find({project_ids:project._id},function(err,docs){
     //here docs will be the list of the users of project1
})
//The above query might be slow if the user base is large. 
//But it can be improved vastly by indexing the project_ids field in the User schema.

类似于解决方案1.每个项目都有user_ids字段。让我们假设我们有一个user1。 要获取用户的项目,我们执行以下查询

Project.find({user_ids:user1._id},function(err,docs){
      //here docs will be the projects of user1
      //But it can be improved vastly by indexing the user_ids field in the Project schema.

如果你正在考虑解决方案1和解决方案2,我猜测解决方案1更好。在某些情况下,您可能需要没有项目的用户,但是没有用户要求项目的可能性非常低。但这取决于您的确切用例。


7
2017-08-03 09:36



“最好将用户对象保持为尽可能少的群集”你的意思是什么?我可以在两个模式(项目和用户)中使用此索引吗? - Rodrigo Fonseca
解决方案1和解决方案2并不好,因为两种方式几乎都会以相同的数量读取,但它们的写入频率会非常低 - Rodrigo Fonseca
@RodrigoFonseca检查编辑。 - ma08
索引基本上排列文档,使得对索引字段的查询超快。 1表示升序索引,您可以使用-1来降序索引。这里真的没关系,但确实有数字和日期。究竟。经历交易的陷阱(失败的情况,即不一致)并评估您的选择。 - ma08
@RodrigoFonseca检查编辑。 - ma08


好的解决方案1和2看起来不是那么糟糕!如果索引ObjectIds数组,则可以直接访问所需内容。

解决方案3看起来也很好,但是4,不是真的,你需要更多的查询,除非你在项目和用户的关系之间有很多变化,但不是它们本身。

我更喜欢带有索引的解决方案1。我想很多时候你需要获取项目对象取决于用户或直接取决于id,所以 .find() 会做你想做的一切。我认为用最少的信息保持用户架构并不坏,它们看起来像隔离对象,你可能需要它们用于其他目的。


2
2017-08-03 07:11



好的解决方案一个是好的,但如果我想从相应的用户找到所有项目?我将不得不在所有项目文档中遍历用户数组id对象?我认为这不是一个好的解决方案,你能否简要解释一下这个指数是如何运作的? - Rodrigo Fonseca
看一眼 这个。获得这些用户所需要的只是 .find({user_ids:YOURID}) 并确保它是如此之快,在mongo shell中运行它像这样: db.collection.find({user_ids:YOURID}).explain() 看看需要多长时间。 - Foad Nosrati Habibi
多键索引?我会用这个: 链接 有什么不同? - Rodrigo Fonseca
多键索引是用于数组的索引类型。 Mongoose将检测索引类型本身。所以不要担心。你所做的一切都很好。 - Foad Nosrati Habibi