如何在MongoDB中建模多对多关系(针对MySQL用户)

10

我来自 MySQL 背景,正在努力理解 MongoDB。特别是,我在思考如何用 "Mongo方式" 建模 n:n关系时遇到了困难。

对于这个例子,假设我们有两个 集合: usersinterests。我们需要能够表示或查询数据中的几件事:

  • 用户的兴趣
  • 用户对兴趣的评价,例如"like"或"dislike"
  • 具有给定兴趣的用户
  • 每种评级的兴趣计数器(可以增加/减少)
  • 兴趣名称

MySQL 中,我会创建一个以用户ID和兴趣ID为索引的 users_interests 表。对于计数器,我会为每种评级类型单独创建列,并且每次用户对兴趣进行评分/取消评分时,都要进行交易以确保计数器永远不会出错。

我尝试过 阅读一些模式设计文章,但没有成功。

你能帮助一个迷失的灵魂找到方向吗?

2个回答

14

好问题。首先,让我简要介绍一下N:N关系的工作原理,然后我会详细说明您列出的每个要点。

在MySQL中,通常您会有一个用于关联用户和兴趣的中间表(user_interests表)。在Mongo中,您需要以略微不同的方式执行此操作。您仍然需要一个用户和兴趣的集合,但是现在,您会为用户在兴趣下存储一个键列表。因此,类似以下内容:

User Collection {
      "name":"Josh",
      "user":"jsmith",
      "interests":[
           {
            "_id":12345,
            "rating":"like"
           },
           {..}..
      ]
}

通过将您的兴趣存储在一个以您的兴趣表为键的列表中,您可以执行所需的每个操作。如果您想要进行查询,您将根据兴趣表中的ID进行查询,然后使用$in修饰符进行查询。

现在针对您的兴趣集合,我会执行以下操作:

User Interest {
      "_id":objectId
      "label":"Swimming",
      "count":intValue
}
当向用户文档添加兴趣时,计数变量将取决于您的评级定义。如果您将其评级存储在单独的区域(或逻辑中),则分配给它们的值将是您将它们与兴趣中的int值相关联的内容。例如:用户将其评级为meh(具有值1),然后将1添加到计数值中。希望这对您有所帮助,并至少带来了其他一些如何构建它的想法!祝你好运,记住MONGO很棒。

通常在编程路径中,您可能会遇到以下两种情况之一:1. 您想查看哪些用户有某个兴趣爱好,或者2. 您想查看某个用户拥有哪些兴趣爱好。对于第一种情况,您已经拥有兴趣名称,并且现在只需要使用ID进行查询(更快的查询),而不是使用字符串。对于第二种情况,您有一个ID列表,同样也是在对象ID上进行查询。此外,如果您更改了一个兴趣名称,您不必通过每个用户条目来重构以进行更新,因为您只需要在兴趣集合中使用对该特定兴趣的引用ID即可。希望这可以帮助您! - Petrogad
@mnemosyn 如果我决定将兴趣从“fishing”更改为“Fishing”或其他什么,会怎样? - ceejayoz
@Frederico 感谢您的答案更新。我真的应该提到,但我的UI用例决定了每个评分都有不同的计数。在这种情况下,评级类型是“喜欢”,“不喜欢”,“嗯”,“爱”和“书签”。这意味着您的-1、0、1想法对我的需求根本行不通。总的来说,这是一个很好的想法。 - Josh Smith
@JoshSmith 好的,我会修改那个。你是如何跟踪每种评分类型对于增加/减少兴趣度评分的影响的呢?你的代码中是否有一个映射来执行这个逻辑? - Petrogad
@Frederico 实际上会有五个独立的计数,每个都有自己的值。 - Josh Smith
显示剩余5条评论

1
为了维护每个兴趣的全局评分计数,您需要一个单独且独立的集合,在其中使用原子更新运算符根据用户执行的喜欢/不喜欢操作来更新(添加或减去)评分。
您可以将每个用户的兴趣存储为用户集合本身中的子文档数组。
此数据的JSON结构类似于:
db.User
{
    name: 'joe',
    ....,
    interests : [{ name: 'swimming', rating: 10},
              { name: 'cooking', rating: 22 }
              ]
}

现在你可以使用以下方式查询内部键:

> db.User.find( { "interests.name" : "cooking" } )

这将返回具有特定兴趣的用户。


这样,对于每个用户,评分都是非规范化的,即每个用户都有全局评分的副本(rating: 101)。这是无法维护的。 - mnemosyn
这里有评分和计数之间的区别。计数是全局的,评分是相对于用户的。评分应该是一个字符串,而不是一个整数。 - Josh Smith
1
我删除了我的答案。我支持你的方法,但建议你加上一个关于 $elemMatch 的说明,并将 rating 改为字符串。 - mnemosyn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接