为什么在mongoose中声明一对多关联有两个引用?

时间:2017-11-05 03:18:14

标签: mongodb mongoose

我是mongodb的新手,请看one-to-many example

根据我的理解

这个例子说一个人可以写一个故事或一个故事belongs_to一个人,我认为将person._id存储在故事集中就足够了

为什么个人收藏集有field stories

获取数据的案例

案例1

获取身份证明为x

的人的所有故事

解决方案:为此,只需触发故事集where author = x

中的查询

案例2

获取特定故事的author name

解决方案:为此我们有author字段故事集

1 个答案:

答案 0 :(得分:1)

TL; DR

简单地说:因为在MongoDB中没有明确的关系这样的概念。

猫鼬不知道你想如何解决这段关系。搜索是来自给定的故事对象,作者是否会找到?或者搜索是否会找到作者对象的所有故事?所以它确保它可以解决这种关系。

请注意,该方法存在问题,存在问题。假设我们不是在谈论这个例子中的一对一关系,而是一个与一对一的Shitload"™关系。自BSON documents have a size limit of 16MB以来,您可以通过这种方式管理关系。相当一些,但会有一个人为限制。

如何解决这个问题:不要使用ODM,而是自己进行正确的建模。既然你知道你的用例。我将在下面举例说明。

详细

让我们先详细说明你的案例:

  1. 对于给定的用户(又名"我们已经拥有该用户文档的所有数据"),他或她的故事是什么?
  2. 在概述页面上列出所有故事和用户名。
  3. 对于选定的("给定的" )故事,作者的详细信息是什么?
  4. 仅用于演示目的:给定用户想要更改显示故事的名称,无论是用户名还是自然名称(它发生!)甚至是假名。
  5. 好的,现在让我们把mongoose放在一边,让我们考虑一下如何自己实现它。记住

      

    MongoDB中的数据建模是从您的用例问题中推导出您的模型,以便以最有效的方式覆盖最常见的用例。

    与RDBMS建模相反,在这里你可以识别你的实体,它们的属性和关系,然后跳过一些环节来解决你的问题以某种方式

    所以,看看我们的用户故事,我想我们可以同意2是最常见的用例,3和1接下来,4与其他用例相比是相当罕见的。

    现在我们可以开始了

    建模

    我们首先对最常见用例中涉及的数据进行建模。

    因此,我们希望使故事查询最有效。我们希望按照提交的降序对故事进行排序。很简单:

    {
      _id: new ObjectId(),
      user: "Name to Display",
      story: "long story cut short",
    }
    

    现在假设你想要展示你的故事,其中10个:

    db.stories.find({}).sort({_id:-1}).limit(10)
    

    没有关系,我们需要的所有数据,单个查询,都使用default index on _id进行排序。由于timestamp is part of the ObjectId并且它是最重要的部分,我们可以使用它来按时间对故事进行排序。问题"嘿,但如果改变他或她的用户名怎么办?"通常现在来了。简单:

    db.stories.update({"user":oldname},{$set:{"user":newname}},{multi:true})
    

    由于这是一个罕见的用例,它只需要是可行的,而且不必非常有效。但是,稍后我们将看到我们必须在user上建立一个索引。

    谈论作者:这真的取决于你想要怎么做。但我会告诉你我是如何建模的:

    {
       _id: "username",
       info1: "foo",
       info2: "bar",
       active: true,
       ...
    }
    

    我们在这里使用_id的一些属性:它是具有唯一约束的必需索引。正是我们想要的用户名。

    然而,它有一个警告:_id is immutable。因此,如果有人想要更改其用户名,我们需要将原始用户复制到包含新用户名_id的文档,并相应地更改我们故事中的用户属性。这种方式的优点是,即使更改用户名(见上文)的更新在运行时期间失败,每个故事仍然可以与用户相关。如果更新成功,我倾向于注销用户并让他再次使用新用户名登录。

    如果您想区分用户名和显示的名称,这一切都变得更加容易:

    {
      _id: "username",
      displayNames: ["Foo B. Baz","P.S. Eudonym"],
      ...
    }
    

    然后你在故事中使用显示名称。

    现在让我们看看如何获​​取给定故事的用户详细信息。我们知道作者的名字,所以它很简单:

    db.authors.find({"_id":authorNameOfStory})
    

    db.authors.find({"displayNames": authorNameOfStory})
    

    查找给定用户的所有故事也非常简单。它是:

    db.stories.find({"name":idFieldOfUser})
    

    db.stories.find({"name":{$in:displayNamesOfUser}})
    

    现在我们已经涵盖了所有用例,现在我们可以通过

    提高效率

    索引

    故事模型user字段中有一个明显的索引,所以我们这样做:

    db.stories.ensureIndex({"name":1})
    

    如果您使用"用户名为_id"只有这样,你完成了索引。使用显示名称,您显然需要索引它们。由于您最有可能希望显示名称和假名是唯一的,因此它有点复杂:

    db.authors.ensureIndex({"displayNames":1},{sparse:true, unique:true})
    

    注意:我们需要将其设为sparse index,以便在有人尚未决定使用显示名称或假名时防止出现不必要的错误。确保仅在用户决定显示名称时,才将添加此字段显式添加到作者文档中。否则,它会评估到null服务器端, 是一个有效值,而得到一个约束违规错误,即" E1100重复密钥& #34;

    结论

    我们已经涵盖了应用程序处理的所有用例,从而大大简化了我们的数据模型,并为最常见的用例提供了最有效的查询。考虑到我们在进行查询时已经拥有的信息,每个用例都包含在一个查询中。

    请注意,由于我们使用隐式关系,因此用户可以发布多少故事没有人为限制。

    对于更复杂的查询("每个用户每月提交多少个故事?"),请使用aggregation framework。这就是它的用途。