MongoDB架构建议:嵌入式文档

时间:2016-02-11 10:35:35

标签: mongodb meteor

我有一群人。每个人可能有很多书。每本书都有一个撤回的日期。

这是我第一次使用来自传统SQL的Mongo。我的第一个倾向是让每本书撤回一个子文档。但是,这似乎使子文档的排序变得很痛苦。

例如:

Schemas.Person = new SimpleSchema({
  "name": {
    type: String
  },
  "withdrawals.$": {
    type: Object
  },
  "withdrawals.$.withdrawalDate": {
    type: Date
  },
  "withdrawals.$.bookName": {
    type: String
  }
});

例如,如果我想获得一份已撤回订购书的人员清单,我认为对Mongo来说这真的很复杂。

我是否应该使用引用并从Person集合中拆分撤销?

感谢您的任何建议。

2 个答案:

答案 0 :(得分:2)

Overembedding is the root of all evil.

截至本文撰写时(以及可预见的未来),BSON文档的大小限制为16MB。如果你真的想让一个人拥有无限量的书籍,你就无法嵌入书籍。同样适用于提款次数。

我们这里有一个众所周知的模式,与属性的关系,意味着关系本身受到其他属性的限制。

所以你想要的是persons集合,其中包含所有人的数据,包含书籍所有数据的书籍集,以及用于记录借阅哪本书的人的提款集。哪个日期(甚至可以退回)。

这是因为您的用例基本上是这样的:

  1. 对于给定的书,是否可用? (=没有公开撤回)
  2. 对于给出提款,谁借了这本书?
  3. 对于给定用户和给定图书以及给定日期,请记录提款。
  4. 对于给定用户和给定书籍以及给定日期,请记录退货。
  5. 集合

    所以person集合的存根看起来像

    {
        _id: new ObjectId()
       username: "foo",
       …
    }
    

    您的图书集看起来很相似:

    {
        _id: "someISBN"
        title: "Foobar and Barbaz explore the Mongoverse"
        …
    }
    

    撤回文件如下:

    {
        _id: new ObjectId(),
        userId: someUserObjectId,
        bookId: someBookId,
        withdrawalDate: new ISODate(),
        // returnDate can be omitted, too
        // since non-existing values evaluate to null
        returnDate: null
    }
    

    可用性检查

    现在,让我们说有人找到了一本他或她想要退出的书,你发现它可以通过发行来获得

    > db.withdrawals.find({bookId:someBookId,returnDate:null})
    { "_id" : ObjectId("56bc6c8188d0e8d074b11a8d"), "userId" : ObjectId("56bc6bd888d0e8d074b11a8a"), "bookId" : "1234567890", "withdrawalDate" : ISODate("2016-02-11T11:12:01.325Z") }
    

    如果此查询找到了某些内容,那么这本书就已借用而未归还。如果没有退回任何文件,您就知道该书应该可用。

    注意:通过返回文档,您现在可以通过查询persons集合找出借书的人,因为我们有userId:

    > db.persons.find({_id:ObjectId("56bc6bd888d0e8d074b11a8a")})
    { "_id" : ObjectId("56bc6bd888d0e8d074b11a8a"), "username" : "mwm" }
    

    取款和退货

    当用户" mwm"借用这本书,它以最简单的方式记录:

      db.withdrawals.insert({
         userId:ObjectId("56bc6bd888d0e8d074b11a8a"),
         bookId:"0987654321",
         withdrawalDate:new ISODate("2016-01-01T12:00:00Z")
      })
    

    现在,当(已知)用户返回(已知)书籍时,记录它非常容易:

    db.withdrawals.update(
    {
        "userId":ObjectId("56bc6bd888d0e8d074b11a8a"),
        "bookId":"0987654321"
    },{
        $set:{ returnDate: newISODate() }
    })
    

答案 1 :(得分:1)

如果我理解正确,Person集合中的每个文档都是这样的 -

{
  name: String,
  withdrawals: [
    {
      bookName: String,
      withdrawalDate: Date
    },
    {
      bookName: String,
      withdrawalDate: Date
    }
    .
    .
    .
  ]
}

如果是这种情况,我相信您可以使用以下查询来获取所有借用了特定书籍的人员的列表(本例中 book1 )按照日期的升序排序撤出。

Person.find({ "withdrawals.bookName": "book1" }, {
  sort: { "withdrawals.withdrawalDate": 1 }
});