本系列教程目录:

MongoDB入门实战教程(1)

MongoDB入门实战教程(2)

MongoDB入门实战教程(3)

MongoDB入门实战教程(4)

MongoDB入门实战教程(5)

MongoDB入门实战教程(6)

前面我们学习了聚合查询,本篇我们来看看在模型设计中如何应用引用模式来提高查询效率。

1内嵌模式

在进行MongoDB的模型设计中,基于JSON文档模型,我们很容易就可以设计出一个内嵌模式的文档模型出来。

可以不夸张地说,80%~90%的场景下,我们优先都会使用内嵌对象 或 内嵌数组 的方式来设计文档模型的所谓的1-1、1-N、N-N的关系

例如下面这个Contacts的文档模型,它描述了一个联系人的关系建模:

Contacts

{
name: "Edison Zhou",
company: "CSCEC YZW",
title: ".NET Engineer",
portraits: {
mimetype: xxx,
data: xxxx
},
addresses: [
{ type: home, … },
{ type: work, … }
],
groups: [
{name: "YZW Football Assocation" },
{name: "YZW .NET Assocation" }
]
}

可以看到,所谓的内嵌类 其实 类似于 预先聚合(关联)这样的操作(引用+冗余)其实对读操作更有性能优势

但是,内嵌设计有一个大前提限制:即内嵌后文档大小不能超过16MB。

此外,如果内嵌的数组(通常是数组)的长度太大,比如数万或更多的时候,也是不适合采用内嵌模式的。

那么,此时我们应该怎么设计呢?

2 引用模式

万级长度的内嵌数组

这里我们仍然适用上面提到的Contacts模型,假设其中的groups是一个内嵌数组,这个groups的数据可能有百万级的长度,且每个Contacts文档都需要冗余这么一份数据,而且groups数据还面临着频繁修改的需求。

Contacts

{
name: "Edison Zhou",
company: "CSCEC YZW",
title: ".NET Engineer",
......
// 假设下面groups有百万级,且一个group的信息改动会引发百万级的DB操作
groups: [
{name: "YZW Football Assocation" },
{name: "YZW .NET Assocation" }
]
}

适当使用引用模式解决

解决方案很简单,就是针对groups使用单独的collection来存储,在Contancts模型中添加对group id的集合的引用。

Collection 1 - Contacts:

Contacts

{
name: "Edison Zhou",
company: "CSCEC YZW",
title: ".NET Engineer",
......
// 假设下面groups有百万级,且一个group的信息改动会引发百万级的DB操作
group_ids: [1,2,3,4,5...]
}

Collection 2 - Groups:

Groups

{
groups_id,
name
}

这样的设计其实类似于关系型数据库模型的设计,用Id来关联,我们再熟悉不过了。

但是,在MQL中,我们就需要额外使用$lookup来实现类似SQL中的关联查询了,严格来说,应该算是LEFT OUTER JOIN查询。

嗯,这又是一种聚合操作:

db.Contacts.aggregate([
{
$lookup:
{
from: "groups",
localField: "group_ids",
foreignField: "group_id",
as: "groups"
}
}]);

这个查询会得到如下图所示的结果:

.NET中的Lookup操作:

上面讲解了如何通过MQL进行操作,那么,在.NET中如何实现$lookup的效果呢?

好在MongoDB Driver已经帮我们提供了这样的一个LookUp,且看下面的代码示例:

假设我们的实体定义如下:

public class Contact
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; } public string Name { get; set; } public string Company { get; set; } public string Title { get; set; } public int[] GroupIds { get; set; } public IList<Group> Groups { get; set; }
} public class Group
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; } public int GroupId { get; set; } public string Name { get; set; }
}

那么,可以通过Driver实现以下操作:

public async Task<IList<Contact>> GetAsync()
{
return await _contacts
.Aggregate()
.Lookup<Contact, Group, Contact>(
_groups,
local => local.GroupIds,
from => from.GroupId,
result => result.Groups)
.ToListAsync();
}

完整示例github地址:https://github.com/EdisonChou/EDT.Mongo.Sample

运行结果如下所示:

什么时候使用引用模式

综上所述,当满足以下条件之一时,你可以开始考虑引用模式设计文档模型:

(1)当内嵌后的文档太大,有可能超过16MB限制的时候;

(2)内嵌的文档 或 数组元素 有可能会频繁修改的时候;

(3)内嵌数组元素 有可能会持续增长且没有封顶的时候;

引用模式设计的限制

引用模式也并非银弹,它存在以下一些限制:

(1)MongoDB对于使用引用的集合之间没有所谓的外键检查;

(2)MongoDB使用聚合框架的$lookup来模仿关联查询;

(3)$lookup只支持LEFT OUTER JOIN,且关联目标(from)不能是分片表;

db.Contacts.aggregate([
{
$lookup:
{
from: "groups", // 这里的from不能是分片表
......
}
}]);

总结

本文简单介绍了MongoDB的模型设计中的内嵌模式和引用模式,探讨了引用模式的使用、何时使用 及 使用限制。

下一篇,我们会学习MongoDB的模式设计中的一些设计模式并套用这些设计模式简化设计难度。

参考资料

唐建法,《MongoDB高手课》(极客时间)

郭远威,《MongoDB实战指南》(图书)

△推荐订阅学习

作者:周旭龙

出处:https://edisonchou.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

MongoDB入门实战教程(7)的更多相关文章

  1. Kafka入门实战教程(7):Kafka Streams

    1 关于流处理 流处理平台(Streaming Systems)是处理无限数据集(Unbounded Dataset)的数据处理引擎,而流处理是与批处理(Batch Processing)相对应的.所 ...

  2. ZooKeeper入门实战教程(一)-介绍与核心概念

    1.ZooKeeper介绍与核心概念1.1 简介ZooKeeper最为主要的使用场景,是作为分布式系统的分布式协同服务.在学习zookeeper之前,先要对分布式系统的概念有所了解,否则你将完全不知道 ...

  3. Node+Express+MongoDB+Socket.io搭建实时聊天应用实战教程(一)--MongoDB入门

    前言 本文并不是网上流传的多少天学会MongoDB那种全面的教程,而意在总结这几天使用MongoDB的心得,给出一个完整的Node+Express+MongoDB+Socket.io搭建实时聊天应用实 ...

  4. Node+Express+MongoDB + Socket.io搭建实时聊天应用实战教程(二)--node解析与环境搭建

    前言 本来开始写博客的时候只是想写一下关于MongoDB的使用总结的,后来觉得还不如干脆写一个node项目实战教程实战.写教程一方面在自己写的过程中需要考虑更多的东西,另一方面希望能对node入门者有 ...

  5. MongoDB入门必读(概念与实战并重)

    MongoDB入门必读(概念与实战并重) 一.概述 MongoDB是一个基于分布式文件存储的数据库开源项目.由C++语言编写.旨在为WEB应用提供可护展的高性能数据存储解决方案. MongoDB是一个 ...

  6. mybatis实战教程(mybatis in action),mybatis入门到精通

    转自:http://www.yihaomen.com/article/java/302.htm (读者注:其实这个应该叫做很基础的入门一下下,如果你看过hibernate了那这个就非常的简单) (再加 ...

  7. mybatis实战教程(mybatis in action),mybatis入门到精通(转)

    转自:http://www.yihaomen.com/article/java/302.htm (读者注:其实这个应该叫做很基础的入门一下下,如果你看过Hibernate了那这个就非常的简单) (再加 ...

  8. mongodb入门教程二

    title: mongodb入门教程二 date: 2016-04-07 10:33:02 tags: --- 上一篇文章说了mongodb最基本的东西,这边博文就在深入一点,说一下mongo的一些高 ...

  9. mongodb入门教程

    title: mongodb入门教程 date: 2016-04-06 14:47:18 tags: --- 为什么要认识呢,因为这玩意就一傻逼 借用一下百科的介绍 MongoDB 是一个介于关系数据 ...

  10. MongoDb 入门教程

    MongoDb 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的. 它是可扩展的高性能数据存储解决方案,经常被用于非关系型数据的存储,能存储海量的数据. 常 ...

随机推荐

  1. AI 应用思考

    之前看到过一个理论,创新技术的三个阶段:新技术创造-精英服务-平民化 技术扩散的三阶段理论模型 1. 创新垄断期(精英创造阶段)技术革命初期,创新活动高度依赖知识密集型投入.AI发展呈现"分 ...

  2. FireDAC开发DataSnap应用系统【1】-快储功能

    FireDAC是吧DataSnap服务器当成API来调用,而dbExpress使用IAppServer接口. 关键点: 1.客户端调用API要回传数据,那么FireDAC把数据已Stream的格式传递 ...

  3. Python+Selenium+unittest实例

    代码如下: # coding=utf-8 import time import unittest from selenium import webdriver class BaiduSearch(un ...

  4. RocketMQ学习与总结

    一.基本介绍 1.应用场景 消息队列是一种先进先出的数据结构,常见的应用场景: 应用解耦:系统的耦合性越高,容错性就越低 实例:用户创建订单后,耦合调用库存系统.物流系统.支付系统,任何一个子系统出了 ...

  5. 【代码审计】Emlog存在SQL注入+XSS漏洞

    1.源码简介 EMLOG 是一款轻量级开源博客和CMS建站系统,速度快.省资源.易上手,适合各种规模的站点搭建. 2.漏洞描述 EMLOG $keyword参数存在SQL注入漏洞. EMLOG art ...

  6. SpringCloud——自定义断言工厂

    目录 场景:用户的请求头中需要有指定的用户名和密码才能访问. 断言工厂 参考系统AfterRoutePredicateFactory写法. package com.zjw.factory; impor ...

  7. 操作系统综合题之“采用短进程优先调度算法(Shortest-Process-First,SPF)和先来先服务调度算法(First-Come,First-Served,FCFS)计算开始运行时间、结束时间、等待时间、周转时间、带权周转时间、平均周转时间”

    一.问题:某系统中有四个进程,他们进入系统的时间和需要服务的时间如题下表所示(表中数值均为十进制) 进程 进入系统的时间 需要服务的时间 P1 0 100 P2 10 60 P3 25 25 P4 3 ...

  8. 集合流之“anyMatch”的应用【返回boolean类型】

    判断集合中是否存在"字符串",返回boolean类型 boolean isExit = allSku.stream().map(Product::getFeatureList) . ...

  9. MySQL数据库之“字符串截取”:substring_index()用法

    1.substring_index函数的语法及其用法 (1)语法:substring_index(string,sep,num) 即substring_index(字符串,分隔符,序号) 参数说明 s ...

  10. 操作系统:Linux -- 详解socket实现与网络编程接口

    现在我们来瞧一瞧 Linux 的网络程序,不过想要入门 Linux 的网络编程,套接字也是一个绕不开的重要知识点,正是有了套接字,Linux 系统才拥有了网络通信的能力.而且网络协议的最底层也是套接字 ...