【问题标题】:How to model many-to-many relationships in MongoDB (for a MySQL user)如何在 MongoDB 中建模多对多关系(对于 MySQL 用户)
【发布时间】:2012-01-09 22:32:47
【问题描述】:

我来自MySQL 背景,并试图围绕MongoDB 转转。特别是,我正在努力构思如何以“Mongo 方式”为n:n 关系建模。

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

  • 用户的兴趣
  • 用户的兴趣等级,例如“喜欢”或“不喜欢”
  • 具有特定兴趣的用户
  • 每个兴趣评级的计数器(可以递增/递减)
  • 兴趣名称

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

我试过reading about some schema designs,但无济于事。

你能帮助迷失的灵魂找到路吗?

【问题讨论】:

    标签: mysql mongodb database-schema schema-design


    【解决方案1】:

    很好的问题。让我先概述一下 N:N 关系的工作原理,然后我将详细介绍您的每个要点。

    N:N 在 MySQL 中,通常你有你的数据透视表关联你的用户和兴趣(user_interests 表)。在 mongo 中,你这样做有点不同。您仍然有一个用户和兴趣集合,但是现在,您在用户的兴趣下存储了一个键列表。所以是这样的:

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

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

    现在,为了收集您的兴趣,我将执行以下操作:

    User Interest {
          "_id":objectId
          "label":"Swimming",
          "count":intValue
    }
    

    向用户文档添加兴趣时,计数变量将取决于您的评分定义。如果您将评级存储在单独的区域(或逻辑中),那么您分配给它们的值将是您与感兴趣的 int 值相关的值。 IE:用户给它打分(它的值为 1)然后你会在计数值上加 1。

    希望这会有所帮助,并且至少会为如何构建它带来一些其他想法!

    祝你好运,记住 MONGO 很棒。

    【讨论】:

    • 为什么要存储id?您不太可能重命名兴趣,但保留引用,对吗?此外,您必须执行 $in 才能显示喜欢的名称。
    • @mnemosyn 通常代码路径有以下两种情况之一:1:您想查看用户的兴趣,或 2:您想查看用户的兴趣。对于第一个,您已经有了名称,现在您只是对 ID(更快的查询)而不是字符串进行查询。对于第二个,您有一个 id 列表,您再次查询对象 ID。此外,如果您确实更改了兴趣名称,则不必重构每个用户条目来更新它,因为您所需要的只是兴趣集合中特定兴趣的 ref id。希望这会有所帮助!
    • @mnemosyn 如果我决定将兴趣大写,从“钓鱼”到“钓鱼”什么的?
    • 没错,这就是为什么保留一个 ref 键总是好的原因,然后在一个标识符始终不变的区域中进行修改。
    • 您能否提供一个资源,说明 Mongo 的 BTree 查询对 id 的查询比对字符串的查询更快?我相信对象列表和(可能很常见)$in 查询的开销会更大。
    【解决方案2】:

    要维护每个兴趣的评分的全局计数,您将需要一个单独且独立的集合,当用户对兴趣执行喜欢/不喜欢的操作时,您需要使用原子更新运算符更新(添加或减去)评分。

    您可以将每个用户的兴趣存储为用户集合本身中的子文档数组。

    此数据的 JSON 结构类似于:

    db.User
    {
        name: 'joe',
        ....,
        interests : [{ name: 'swimming', rating: 10},
                  { name: 'cooking', rating: 22 }
                  ]
    }
    

    现在您可以使用以下方法查询内部键:

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

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

    【讨论】:

    • 这样,每个用户的评分都是非规范化的,即每个用户都有一份全局评分的副本(`rating : 101)这无法维护。
    • 评分和计数之间存在差异。计数是全局的,评级是相对于用户的。不过,评分应该是一个字符串,而不是整数。
    • 我删除了我的答案。我赞成你的方法,但我建议你添加一个关于需要$elemMatch 的注释并将rating 设为字符串。
    猜你喜欢
    • 2017-01-02
    • 1970-01-01
    • 2017-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-13
    • 2011-06-17
    相关资源
    最近更新 更多