MongoDB数据模型/结构设计中重要的经验法则

MongoDB数据模型/结构设计中重要的经验法则

One-to-N:MongoDB数据结构设计中6条重要的经验法则,part 1

双向关联、反范式设计:MongoDB数据库设计中6条重要的经验法则,part 2

经验法则总结:MongoDB数据库设计中6条重要的经验法则,part 3

Part1结论:所以,即使这种简单的讨论也有能察觉出mongobd的建模和关系模型建模的不同之处。你必须要注意一下两个因素:

  • Will the entities on the “N” side of the One-to-N ever need to stand alone? 一对多中的多是否需要一个单独的实体。

  • What is the cardinality of the relationship: is it one-to-few; one-to-many; or one-to-squillions? 这个关系中集合的规模是一对很少,多,还是非常多。

Based on these factors, you can pick one of the three basic One-to-N schema designs: 基于以上因素来决定采取一下三种建模的方式:

  • One-to-Few(内联)

一对很少且不需要单独访问内嵌内容的情况下可以使用内嵌多的一方的方案。

  • One-to-Many(子引用)

一对多且多的一段内容因为各种理由需要单独存在的情况下可以使用通过数组的方式引用多的一方的方案。

  • One-to-Squillions(父引用)

一对非常多的情况下,请将一的那端引用签入进多端的方案。

One-to-Few(一对很少)

## < db.person.findOne()
{
    "name": "KateMonster",
    "ssn": "123-456-7890",
    "addresses": [
        {
            "street": "123SesameSt",
            "city": "Anytown",
            "cc": "USA"
        },
        {
            "street": "123AvenueQ",
            "city": "NewYork",
            "cc": "USA"
        }
    ]
}
  • 适用场景

一个人的地址为例,这时候使用内嵌文档是很合适,可以在person文档中嵌入数组

这种设计拥有内嵌文档设计中所有的优缺点。

  • 最主要的优点

不需要单独执行一条语句去获取内嵌的内容。

  • 最主要的缺点

无法把这些内嵌文档当做单独的实体去访问。

例如,如果你是在对一个任务跟踪系统进行建模,每个用户将会被分配若干个任务。内嵌这些任务到用户文档在遇到“查询昨天所有的任务”这样的问题时将会非常困难。我会在下一篇文章针对这个用例提供一些适当的设计。

One-to-Many(一对多)

以商品替换零件订货系统为例。每个商品有数百个可替换的零件,但是不会超过数千个。这个用例很适合使用间接引用-将零件的objectid作为数组存放在商品文档中(在这个例子中我使用更加易读的2字节的ObjectID,现实世界中他们可能是由12个字节组成的)。

## <db.parts.findOne()
{
    "_id": "ObjectID(‘AAAA’)",
    "partno": "123-aff-456",
    "name": "#4grommet",
    "qty": 94,
    "cost": 0.94,
    "price": 3.99
}

每个产品的文档中parts数组中将会存放多个零件的ObjectID

## < db.products.findOne()
{
    "name": "left-handedsmokeshifter",
    "manufacturer": "AcmeCorp",
    "catalog_number": 1234,
    "parts": [//arrayofreferencestoPartdocuments
        "ObjectID('AAAA')",
        //referencetothe#4grommetabove
        "ObjectID('F17C')",
        //referencetoadifferentPart
        "ObjectID('D2AA')",
        //etc
    ]
}

在获取特定产品中所有零件,需要一个应用层级别的join

// Fetch the Product document identified by this catalog number
< product = db.products.findOne({catalog_number: 1234});

// Fetch all the Parts that are linked to this Product
< product_parts = db.parts.find({_id: { $in : product.parts } } ).toArray() ;

这中引用的方式是对内嵌优缺点的补充。

每个零件是个单独的文档,可以很容易的独立去搜索和更新他们。

使用这种建模方式需要考虑的一个问题是需要一条单独的语句去获取零件的具体内容

这种建模方式中的零件部分可以被多个产品使用,所以在多对多时不需要一张单独的连接表。

One-to-Squillions(一对非常多)

我们用一个收集不同机器日志的例子来讨论一对很多的问题。

由于每个mongodb的文档有16M的大小限制,所以即使你是存储ObjectID也是不够的。

我们可以使用很经典的处理方法父级引用用一个文档存储主题,在每个日志文档中保存这个主机的ObjectID。

< db.hosts.findOne()

{
    "_id": "ObjectID(‘AAAB’)",
    "name": "goofy.example.com",
    "ipaddr": "127.66.66.66"
}

< db.logmsg.findOne()

{
    "time": "ISODate(“2014-03-28T09: 42: 41.382Z”)",
    "message": "cpuisonfire!",
    "host": "ObjectID(‘AAAB’)"//ReferencetotheHostdocument
}

以下是个稍微不同的应用级别的join用来查找一台主机最近5000条的日志信息

// find the parent ‘host’ document

< host = db.hosts.findOne({ipaddr : ’127.66.66.66′}); // assumes unique index

// find the most recent 5000 log message documents linked to that host

< last_5k_msg = db.logmsg.find({host: host._id}).sort({time : -1}).limit(5000).toArray()
微信扫一扫交流

作者:ryanemax
微信关注:ryanemax (刘雨飏)
本文出处:https://romantic-hoover-f991f1.netlify.com/cookbook/database/mongoOneToN/
授权协议: CC BY-SA 4.0