MongoDB 多对多关联
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2336700/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
MongoDB Many-to-Many Association
提问by Josh Close
How would you do a many-to-many association with MongoDB?
您将如何与 MongoDB 进行多对多关联?
For example; let's say you have a Users table and a Roles table. Users have many roles, and roles have many users. In SQL land you would create a UserRoles table.
例如; 假设您有一个用户表和一个角色表。用户有很多角色,角色有很多用户。在 SQL 域中,您将创建一个 UserRoles 表。
Users:
Id
Name
Roles:
Id
Name
UserRoles:
UserId
RoleId
How is same sort of relationship handled in MongoDB?
MongoDB 中如何处理同类关系?
采纳答案by diederikh
Depending on your query needs you can put everything in the user document:
根据您的查询需要,您可以将所有内容放入用户文档中:
{name:"Joe"
,roles:["Admin","User","Engineer"]
}
To get all the Engineers, use:
要获得所有工程师,请使用:
db.things.find( { roles : "Engineer" } );
If you want to maintain the roles in separate documents then you can include the document's _id in the roles array instead of the name:
如果要在单独的文档中维护角色,则可以在角色数组中包含文档的 _id 而不是名称:
{name:"Joe"
,roles:["4b5783300334000000000aa9","5783300334000000000aa943","6c6793300334001000000006"]
}
and set up the roles like:
并设置如下角色:
{_id:"6c6793300334001000000006"
,rolename:"Engineer"
}
回答by paegun
Instead of trying to model according to our years of experience with RDBMS's, I have found it much easier to model document-repository solutions using MongoDB, Redis, and other NoSQL data stores by optimizing for the read use cases, while being considerate of the atomic write operations that need to be supported by the write use cases.
我没有尝试根据我们多年来使用 RDBMS 的经验进行建模,而是发现通过优化读取用例,同时考虑原子性,使用 MongoDB、Redis 和其他 NoSQL 数据存储对文档存储库解决方案进行建模要容易得多写用例需要支持的写操作。
For instance, the uses of a "Users in Roles" domain follow:
例如,“角色中的用户”域的使用如下:
- Role - Create, Read, Update, Delete, List Users, Add User, Remove User, Clear All Users, Index of User or similar to support "Is User In Role" (operations like a container + its own metadata).
- User - Create, Read, Update, Delete (CRUD operations like a free-standing entity)
- 角色 - 创建、读取、更新、删除、列出用户、添加用户、删除用户、清除所有用户、用户索引或类似支持“用户是否参与角色”(类似于容器 + 自己的元数据的操作)。
- 用户 - 创建、读取、更新、删除(像独立实体一样的 CRUD 操作)
This can be modeled as the following document templates:
这可以建模为以下文档模板:
User: { _id: UniqueId, name: string, roles: string[] }
Indexes: unique: [ name ]
Role: { _id: UniqueId, name: string, users: string[] }
Indexes: unique: [ name ]
To support the high frequency uses, such as Role-related features from the User entity, User.Roles is intentionally denormalized, stored on the User as well as Role.Users having duplicate storage.
为了支持高频使用,例如来自 User 实体的与 Role 相关的功能,User.Roles 被故意非规范化,存储在 User 以及具有重复存储的 Role.User 上。
If it is not readily apparent in the text, but this is the type of thinking that is encouraged when using document repositories.
如果它在文本中不是很明显,但这是使用文档存储库时鼓励的思维类型。
I hope that this helps bridge the gap with regard to the read side of the operations.
我希望这有助于弥合操作读取方面的差距。
For the write side, what is encouraged is to model according to atomic writes. For instance, if the document structures require acquiring a lock, updating one document, then another, and possibly more documents, then releasing the lock, likely the model has failed. Just because we can build distributed locks doesn't mean that we are supposed to use them.
对于写入端,鼓励的是根据原子写入进行建模。例如,如果文档结构需要获取锁,更新一个文档,然后更新另一个,可能还有更多文档,然后释放锁,很可能模型失败了。仅仅因为我们可以构建分布式锁并不意味着我们应该使用它们。
For the case of the User in Roles model, the operations that stretch our atomic write avoidance of locks is adding or removing a User from a Role. In either case, a successful operation results in both a single User and a single Role document being updated. If something fails, it is easy to perform cleanup. This is the one reason the Unit of Work pattern comes up quite a lot where document repositories are used.
对于角色模型中的用户的情况,扩展我们对锁的原子写避免的操作是从角色中添加或删除用户。在任何一种情况下,成功的操作都会导致更新单个用户和单个角色文档。如果出现故障,很容易执行清理。这是在使用文档存储库的地方经常出现工作单元模式的原因之一。
The operation that really stretches our atomic write avoidance of locks is clearing a Role, which would result in many User updates to remove the Role.name from the User.roles array. This operation of clear then is generally discouraged, but if needed can be implemented by ordering the operations:
真正扩展我们对锁的原子写避免的操作是清除 Role,这将导致许多 User 更新以从 User.roles 数组中删除 Role.name。这种 clear then 操作通常是不鼓励的,但如果需要,可以通过对操作进行排序来实现:
- Get the list of user names from Role.users.
- Iterate the user names from step 1, remove the role name from User.roles.
- Clear the Role.users.
- 从 Role.users 获取用户名列表。
- 迭代步骤 1 中的用户名,从 User.roles 中删除角色名。
- 清除 Role.users。
In the case of an issue, which is most likely to occur within step 2, a rollback is easy as the same set of user names from step 1 can be used to recover or continue.
对于最有可能在步骤 2 中发生的问题,回滚很容易,因为可以使用步骤 1 中的同一组用户名来恢复或继续。
回答by cortopy
I've just stumbled upon this question and, although it's an old one, I thought it would be useful to add a couple of possibilities not mentioned in the answers given. Also, things have moved on a bit in the last few years, so it is worth emphasising that SQL and NoSQL are moving closer to each other.
我刚刚偶然发现了这个问题,虽然它是一个老问题,但我认为添加一些未在给出的答案中提到的可能性会很有用。此外,过去几年事情发生了一些变化,因此值得强调的是 SQL 和 NoSQL 正在相互接近。
One of the commenters brought up the wise cautionary attitude that “if data is relational, use relational”. However, that comment only makes sense in the relational world, where schemas always come before the application.
其中一位评论者提出了“如果数据是关系型的,就使用关系型”这一明智的谨慎态度。但是,该评论仅在关系世界中有意义,其中模式始终位于应用程序之前。
RELATIONAL WORLD: Structure data > Write application to get it
NOSQL WORLD: Design application > Structure data accordingly
RELATIONAL WORLD:构建数据 > 编写应用程序来获取它
NOSQL WORLD:设计应用程序 > 相应地构建数据
Even if data is relational, NoSQL is still an option. For example, one-to-many relationships are no problem at all and are widely covered in MongoDB docs
即使数据是关系数据,NoSQL 仍然是一种选择。例如,一对多关系完全没有问题,并且在MongoDB 文档中被广泛涵盖
A 2015 SOLUTION TO A 2010 PROBLEM
2010 年问题的 2015 年解决方案
Since this question was posted, there have been serious attempts at bringing noSQL closer to SQL. The team led by Yannis Papakonstantinou at the University of California (San Diego) have been working on FORWARD, an implementation of SQL++ which could soon be the solution to persistent problems like the one posted here.
自从这个问题被发布以来,已经有认真的尝试让 noSQL 更接近 SQL。由加利福尼亚大学(圣地亚哥)的 Yannis Papakonstantinou 领导的团队一直在研究FORWARD,这是一种 SQL++ 的实现,它很快就会成为解决像这里发布的那样的持久性问题的方法。
At a more practical level, the release of Couchbase 4.0 has meant that, for the first time, you can do native JOINs in NoSQL. They use their own N1QL. This is an example of a JOIN
from their tutorials:
在更实际的层面上,Couchbase 4.0 的发布意味着,您第一次可以在 NoSQL 中进行本地 JOIN。他们使用自己的 N1QL。这是JOIN
他们教程中的一个示例:
SELECT usr.personal_details, orders
FROM users_with_orders usr
USE KEYS "Elinor_33313792"
JOIN orders_with_users orders
ON KEYS ARRAY s.order_id FOR s IN usr.shipped_order_history END
N1QL allows for most if not all SQL operations including aggregration, filtering, etc.
N1QL 允许大多数(如果不是全部)SQL 操作,包括聚合、过滤等。
THE NOT-SO-NEW HYBRID SOLUTION
不太新的混合解决方案
If MongoDB is still the only option, then I'd like to go back to my point that the application should take precedence over the structure of data. None of the answers mention hybrid embedding, whereby most queried data is embedded in the document/object, and references are kept for a minority of cases.
如果 MongoDB 仍然是唯一的选择,那么我想回到我的观点,即应用程序应该优先于数据结构。没有一个答案提到混合嵌入,即大多数查询的数据都嵌入在文档/对象中,并且在少数情况下保留了引用。
Example: can information (other than role name) wait? could bootstrapping the application be faster by not requesting anything that the user doesn't need yet?
示例:信息(角色名称除外)可以等待吗?通过不请求用户还不需要的任何东西来引导应用程序可以更快吗?
This could be the case if user logs in and s/he needs to see all the options for all the roles s/he belongs to. However, the user is an “Engineer” and options for this role are rarely used. This means the application only needs to show the options for an engineer in case s/he wants to click on them.
如果用户登录并且他/他需要查看他/他所属的所有角色的所有选项,则可能是这种情况。但是,用户是“工程师”,很少使用此角色的选项。这意味着应用程序只需要为工程师显示选项,以防他/她想要点击它们。
This can be achieved with a document which tells the application at the start (1) which roles the user belongs to and (2) where to get information about an event linked to a particular role.
这可以通过一个文档来实现,该文档在开始时告诉应用程序 (1) 用户属于哪些角色,以及 (2) 从哪里获取有关与特定角色相关联的事件的信息。
{_id: ObjectID(),
roles: [[“Engineer”, “ObjectId()”],
[“Administrator”, “ObjectId()”]]
}
Or, even better, index the role.name field in the roles collection, and you may not need to embed ObjectID() either.
或者,更好的是,在角色集合中索引 role.name 字段,您可能也不需要嵌入 ObjectID()。
Another example: is information about ALL the roles requested ALL the time?
另一个例子:关于所有角色的信息是否一直被请求?
It could also be the case that the user logs in to the dashboard and 90% of the time performs tasks linked to the “Engineer” role. Hybrid embedding could be done for that particular role in full and keep references for the rest only.
也可能是用户登录到仪表板并且 90% 的时间执行与“工程师”角色相关的任务。混合嵌入可以完全针对该特定角色完成,而仅保留其余角色的参考。
{_id: ObjectID(),
roles: [{name: “Engineer”,
property1: value1,
property2: value2
},
[“Administrator”, “ObjectId()”]
]
}
Being schemaless is not just a characteristic of NoSQL, it could be an advantage in this case. It's perfectly valid to nest different types of objects in the “Roles” property of an user object.
无模式不仅仅是 NoSQL 的一个特征,在这种情况下它可能是一个优势。在用户对象的“角色”属性中嵌套不同类型的对象是完全有效的。
回答by Andrei Andrushkevich
in case when employee and company is entity-objecttry to use following schema:
如果员工和公司是实体对象,请尝试使用以下模式:
employee{
//put your contract to employee
contracts:{ item1, item2, item3,...}
}
company{
//and duplicate it in company
contracts:{ item1, item2, item3,...}
}