MongoDB的数据建模

MongoDB是一种面向Document的NoSQL数据库,如果我们还是按照RDB的方式来思考MongoDB的数据建模,则不能有效地利用MongoDB的优势;然而,我们也不能因为Document的灵活性,就可以在设计之初放任自流。

适度的建模是非常有必要的,尤其对于相对复杂的关联关系。因为在MongoDB中,处理这种关联关系既可以使用Link,也可以使用Embedded。

我们要评价一种决策,不能将其与具体的上下文割裂开来做判断,那种单纯说A技术要比B技术好的做法,就像小孩子看卡通片里的人物只知道说谁是好人谁是坏人一般的幼稚。世界上没有一种完美至善的技术,关键还是要结合场景来看使用是否得法。

例如使用Embedded方式,就各有优缺点。举例来说,倘若我们采用Embedded方式将Addresses作为Person对象内部的数组:

{
  name: 'Kate Monster',
  ssn: '123-456-7890',
  addresses : [
     { street: '123 Sesame St', city: 'Anytown', cc: 'USA' },
     { street: '123 Avenue Q', city: 'New York', cc: 'USA' }
  ]
}

当我们在查询Person的信息时,要获取其内嵌的属性细节,我们无需再执行多次查询。倘若我们改变一下领域场景,需要开发一个任务跟踪系统。如果我们将Tasks的信息嵌入到Person对象中,当我们面对以下需求:

  • 显示所有明天到期的任务
  • 显示所有未完成的任务

采用这种Embedded就不那么令人愉快了。

如果采用Link方式,情况就完全不同了:

//Tasks
[
    {
        _id: ObjectID('AAAA'),
        task_number: 1234,
        taks_name: 'Prepare MongoDB environment',
        due_date: '2017-01-15'
    },
    {
        _id: ObjectID('BBBB'),
        task_number: 1235,
        taks_name: 'Import Test Data',
        due_date: '2017-02-15'
    },
]

//Persons
{
  name: 'Kate Monster',
  role: 'Manager',
  tasks : [
    ObjectID('AAAA'),
    ObjectID('BBBB')
  ]
}

有得必有失,当我们需要查询Person承担的Tasks时,采用这种方式,就需要采用application-level join方式执行两次查询。

这种建模方式还带来另一种可能,就是原本Person->Tasks的one-to-N关系就可以变为N-to-N关系,因为一个Task可以被多个Person所拥有。如果采用Embedded方式,则会导致Task数据的冗余。

在文章 6 Rules of Thumb for MongoDB Schema Design中,作者将这种1对N关联实现的判断依据划分为三种形式:

  • one-to-few
  • one-to-many
  • one-to-squillions

但我认为该怎么实现关联,应该从Entity之间的领域关系来判断,我们可以引入DDD的Aggregation设计概念作为建模的依据。简单来说,如果使用Embedded,可以认为该Entity处于Aggregation边界之内,对外应该通过Aggregation Root来访问。文章 6 Rules of Thumb for MongoDB Schema Design的说法就是:

Will the entities on the “N” side of the One-to-N ever need to stand alone?

如果是Stand Alone,就意味着该Entity可以成为一个独立的Aggregation,然后再通过ID与另外一个Aggregate关联。

在SegmentFault上则有人做了如此总结

  • FirstClass (比如“User”这种) 应该用独立的Collection
  • "条目类型"的,应该 embedded
  • 两个模型之间如果是包含关系,用 embedded
  • 多对多关系,用 link(类似sql里面的foregin key)
  • 如果一个模型,其可能存的对象很少,那么就用独立的collection,这样有助于mongodb server做缓存
  • embedded方式不利于做复杂的关联,复杂的查询
  • embedded方式性能很有优势,如果你有“性能”方面的要求,可以考虑用embbed

原文发布于微信公众号 - 逸言(YiYan_OneWord)

原文发表时间:2017-01-26

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏互联网开发者交流社区

STC-单片机控制系统

1113
来自专栏c#开发者

xmldocument内嵌入另一个xmldocument,xmlnode的方法

string xmlstr1 = @"<root><head>myHead</head><body></body></root>"; s...

2725
来自专栏大内老A

开发自己的Data Access Application Block[下篇]

上接:[原创] 我的ORM: 开发自己的Data Access Application Block - Part I 4. Database 下面来介绍重中之重...

2246
来自专栏hbbliyong

socket 通信 多线程调用窗体(委托)的几个知识点,记录在案,以备查阅

1.socket 通信传输汉字的方法:Encoding.GetEncoding("GB2312").GetString(Receivebyte) 发送接收都这样...

2737
来自专栏田超学前端

【微信小程序】c# 实现获取openid、session_key 服务端

5260
来自专栏木宛城主

曾今的代码系列——自己的分页控件+存储过程实现分页

项目里面的测试代码,仅供参考 LoginByAjax <title>Ajax登陆</title> <script src="Scripts/c...

1865
来自专栏Golang语言社区

GO语言 TCP传输实例

package main import ( "net" "fmt" ) var ( maxRead = 1100 msgStop = []byt...

3406
来自专栏.net core新时代

数据字典生成工具之旅(5):DocX组件读取与写入Word

      由于上周工作比较繁忙,所以这篇文章等了这么久才写(预告一下,下一个章节正式进入NVelocity篇,到时会讲解怎么使用NVelocity做一款简易的...

3848
来自专栏跟着阿笨一起玩NET

C# WinForm 判断窗体控件是否修改过

790
来自专栏码匠的流水账

zuul自定义SimpleHostRoutingFilter

zuul的SimpleHostRoutingFilter主要用来转发不走eureka的proxy,里头是使用httpclient来转发请求的,但是有时候我们需要...

1322

扫码关注云+社区