最多' mongo'表示具有属性的多对多关系的方式?
例如:
MYSQL表
people
=> firstName, lastName, ...
Movies
=> name, length ..
peopleMovies
=> movieId, personId, language, role
将人们嵌入电影......?
在MongoDB中,我理解它对denormalize and embed
有好处,但我不想让embed
人加入电影,它在逻辑上没有任何意义。因为人们不一定只属于电影。
People
和Movies
将是两个单独的集合。
People
=>嵌入[{movieId: 12, personId: 1, language: "English", role: "Main"} ...]
Movies
=>嵌入[{movieId: 12, personId: 1, language: "English", role: "Main"} ...]
此解决方案的问题在于,当我们要为特定role
更新某人movie
时,我们需要运行两个更新查询以确保数据在在两个集合中同步。
我们也可以做一些更像关系的事情,并最终得到三个集合
People
=> firstName, lastName, ...
Movies
=> name, length ..
Castings
=> movieId, personId, language, role
问题在于,由于MongoDB中缺少连接语句,所以需要3 queries
来自人 - >电影,反之亦然。
以下是我的问题,有哪些其他方法可以在MongoDB
和NoSQL
方式中对此类内容进行建模。就所提供的解决方案而言,哪一个在mongo中的性能和惯例方面是最好的。
答案 0 :(得分:14)
meteor的API在很多方面鼓励平面关系文档,但MongoDB是一个非关系型数据存储。不幸的是,这种冲突留给了开发人员解决的练习。
模式结构和连接的概念是一个在单个答案中涵盖的巨大主题,因此我将尝试尽可能简洁。
假设您有评论和发布数据。考虑如果您在帖子中嵌入评论会发生什么。
DDP对文档进行操作。每次添加同一帖子中的新评论时,都会发送所有评论。
allow
和deny
规则对文档进行操作。期望相同的规则同时适用于帖子和评论可能是不合理的。
出版物往往在收藏方面更有意义。在上面的场景中,我们无法轻松发布独立于其帖子的评论列表。
关系数据库存在的原因很充分。其中之一就是避免第二种解决方案中固有的多重修改问题。
使用您的第三个解决方案。根据我的经验,选择关系模型的原因远远超过数据存储所施加的限制。当然,克服缺少连接并不容易,但痛苦很可能只与少数发布功能隔离开来。以下是我强烈推荐的一些资源:
How to publish a many-to-many relationship。 Chris详细介绍了您的确切用例,但是他手动执行反应联接并进行了回调,我不建议这样做。
Discover Meteor Encyclopedia。这包括了如何以及为什么应该进行反应性连接的基础知识。
来自Discover Meteor的非规范化章节。这涵盖了我上面提到的许多要点,并讨论了何时以及如何对一些数据进行非规范化。
您可以使用Publish with relations加入您的数据。替代套餐包括:smart publish,publish composite和simple publish。
如果您需要更多信息,请在下方发表评论,我会更新我的答案。
答案 1 :(得分:1)
我认为你应该对你的收藏品进行非规范化。设计MongoDB集合和文档时,重要的一点是考虑您的观点。显示视图需要哪些数据?我们的想法是,您应该尝试将这些数据作为文档的一部分。
例如,在您的情况下,您可能想要显示有关电影信息的Movies
视图。但是关于电影的那个页面可能只需要关于每个人的基本信息(名字,姓氏,照片URL)。不是所有其他的东西。反之亦然,关于一个人的页面可能会列出所有电影,但同样只需要有关每部电影的一部分信息,如标题,年份和海报照片URL。
所以一个选项是拥有两个集合,但是然后嵌入(denormalize)集合之间需要的那些少数字段。例如,Movies
集合将有一个字段people
,它将是一个子文档数组。并且People
集合将具有movies
字段,该字段将是一个子文档数组,其中包含您想要指定角色的额外字段等等。
因此文档可能如下所示。对于电影:
{
_id: "AAA",
title: "...",
year: 2015,
length: 120,
posterURL: "...",
people: [
{
person: {
_id: "BBB",
firstName: "...",
lastName: "...",
photoURL: "..."
},
role: "..."
}
]
}
对于人们:
{
_id: "BBB",
firstName: "...",
lastName: "...",
photoURL: "...",
movies: [
{
_id: "AAA",
title: "...",
year: 2015,
posterURL: "..."
}
]
}
当然,问题是如何保持这些字段同步。如果您更新电影的海报照片URL,您希望在所有个人文档中更新它。为了解决这个问题,我们开发了PeerDB,一个用于定义集合之间关系的包,然后确保它们保持同步。
所以在你的情况下,我会在CoffeeScript中的PeerDB中定义这样的集合:
class People extends Document
@Meta
name: 'People'
class Movies extends Document
@Meta
name: 'Movies'
fields: =>
people: [
person: @ReferenceField People, ['firstName', 'lastName', 'photoURL'], true, 'movies', ['title', 'year', 'posterURL']
]
简而言之,此定义表明people.person
字段应该是对People
集合的引用,并且与firstName
,lastName
,photoURL
保持同步。此外,应在字段People
下的movies
文档中使用title
,year
,posterURL
进行反向引用字段。
非常简单。但是有一些缺点。阵列可能变得非常大(可能不是电影和人,但对于其他一些数据),这可能使文档对于MongoDB每文档限制而言太大(目前为16 MB)。此外,如果您观察到,您会看到People
文档中没有关于电影列表中角色的信息。这是因为角色不是引用文档的一部分,但它是引用旁边的内容。如果您想要在人物页面/视图中显示某个人的电影角色怎么办?
所以,也许最好有三个集合,一个用于电影的基本信息,另一个用于人,然后是人与电影之间关系的集合。所以数据可能就像电影一样:
{
_id: "AAA",
title: "...",
year: 2015,
length: 120,
posterURL: "..."
}
对于人们:
{
_id: "BBB",
firstName: "...",
lastName: "...",
photoURL: "..."
}
铸造:
{
_id: "...",
movie: {
_id: "AAA",
title: "...",
year: 2015,
posterURL: "..."
},
person: {
_id: "BBB",
firstName: "...",
lastName: "...",
photoURL: "..."
},
role: "..."
}
和PeerDB定义:
class People extends Document
@Meta
name: 'People'
class Movies extends Document
@Meta
name: 'Movies'
class Casting extends Document
@Meta
name: 'Casting'
fields: =>
person: @ReferenceField People, ['firstName', 'lastName', 'photoURL']
movie: @ReferenceField Movies, ['title', 'year', 'posterURL']
然后,PeerDB会确保事情保持同步。如果从数据库中删除电影或人员,它也会删除投射文档。
然后,您可以制作有效且不需要动态构建相关查询的Meteor发布。您只需发布Casting
集合即可。您甚至可以查询某些条件。例如,您想要显示按firstName
和lastName
排序的所有导演及其电影吗?只有一个查询可能。