原文地址

接上一篇

四、模型树结构

父引用的模型树结构

这个数据模型描述了一个树形结构,在子节点中存储父节点的引用。

模式

父引用模式存储每个树节点到文档中,除了树节点外,文档还存储了父节点的id。

考虑以下目录的层级关系。

以下为应用实例

db.categories.insert( { _id: "MongoDB", parent: "Databases" } )
db.categories.insert( { _id: "dbm", parent: "Databases" } )
db.categories.insert( { _id: "Databases", parent: "Programming" } )
db.categories.insert( { _id: "Languages", parent: "Programming" } )
db.categories.insert( { _id: "Programming", parent: "Books" } )
db.categories.insert( { _id: "Books", parent: null } )

查询一个节点的父节点变得快速且直观

db.categories.findOne( { _id: "MongoDB" } ).parent

还可以对parent字段创建索引以提高查询速度

db.categories.createIndex( { parent: 1 } )

通过parent字段查询其直接子节点

db.categories.find( { parent: "Databases" } )

这种父引用模式是实现树存储的简单方法,缺点是获取子树时则需要多个查询。

子引用的模型树结构

这个数据模型描述了树形结构,存储子节点的引用到父节点中。

模式

子引用模式存储每个树节点到一个文档中,除了树节点,文档还存储了包含其子节点id的数组。

考虑与上一个图中相同的目录层级关系,其子引用模式的实现如下

db.categories.insert( { _id: "MongoDB", children: [] } )
db.categories.insert( { _id: "dbm", children: [] } )
db.categories.insert( { _id: "Databases", children: [ "MongoDB", "dbm" ] } )
db.categories.insert( { _id: "Languages", children: [] } )
db.categories.insert( { _id: "Programming", children: [ "Databases", "Languages" ] } )
db.categories.insert( { _id: "Books", children: [ "Programming" ] } )

查询获取一个节点的直接子节点则变得快速且直观

db.categories.findOne( { _id: "Databases" } ).children

创建children字段上的索引以实现快速查询

db.categories.createIndex( { children: 1 } )

通过children信息查询其父节点以及兄弟节点信息

db.categories.find( { children: "MongoDB" } )

子引用模式提供了一种树存储的合适方案,只要不需要对子树操作就行(单个查询无法保证获取一个节点的所有子节点)。对于存储图像,其中一个节点可能有多个父节点,那这个模式也是很好的方案。

祖先数组的模型树结构

这个数据模型描述了树形结构,使用对父节点的引用和一个数组来存储所有的祖先节点。

模式

文档中除了存储每个树节点,还存储了一个数组,其他暴露了所有祖先节点的id或者路径(祖先节点的id按顺序排序组成路径)。

考虑跟上面图中相同的目录层级关系

以下实际实例中,文档除了ancestors字段,还有parent字段,parent字段存储了直接父节点的引用。

db.categories.insert( { _id: "MongoDB", ancestors: [ "Books", "Programming", "Databases" ], parent: "Databases" } )
db.categories.insert( { _id: "dbm", ancestors: [ "Books", "Programming", "Databases" ], parent: "Databases" } )
db.categories.insert( { _id: "Databases", ancestors: [ "Books", "Programming" ], parent: "Programming" } )
db.categories.insert( { _id: "Languages", ancestors: [ "Books", "Programming" ], parent: "Programming" } )
db.categories.insert( { _id: "Programming", ancestors: [ "Books" ], parent: "Books" } )
db.categories.insert( { _id: "Books", ancestors: [ ], parent: null } )

查询获取一个节点的祖先节点或者路径则变得快速而直观

db.categories.findOne( { _id: "MongoDB" } ).ancestors

对ancestors字段创建索引提高查询速度

db.categories.createIndex( { ancestors: 1 } )

查询ancestors字段以查找它的所有后代节点

db.categories.find( { ancestors: "Programming" } )

祖先数组模式提供了查询某节点的后代节点以及某节点的祖先节点的有效方案,所以当需要对子树节点进行操作时,这个模式是一个很好的选择。

祖先数组模式比具体化路径(Materialized Paths)模式稍慢,但是使用更直观。

具体化路径(Materialized Paths)的模型树结构

这个数据模型描述了树形结构,存储文档间的全关系路径。

模式

具体化路径中,文档除了存储树节点之外,还存储了节点的祖先的id或者路径。尽管具体化路径模式要求额外的步骤处理字符串和正则表达式,这个模式也提供了处理路径的灵活性,如通过部分路径查找节点。

考虑与上面的图中相同的目录层级关系

具体化路径模式中,存储路径到path字段中,路径使用逗号作为分隔符

db.categories.insert( { _id: "Books", path: null } )
db.categories.insert( { _id: "Programming", path: ",Books," } )
db.categories.insert( { _id: "Databases", path: ",Books,Programming," } )
db.categories.insert( { _id: "Languages", path: ",Books,Programming," } )
db.categories.insert( { _id: "MongoDB", path: ",Books,Programming,Databases," } )
db.categories.insert( { _id: "dbm", path: ",Books,Programming,Databases," } )

通过path字段查询所有的节点

db.categories.find().sort( { path: 1 } )  // 按path的升序

对path字段使用正则表达式查找Programming的后代

db.categories.find( { path: /,Programming,/ } ) // 路径包含",Programming,"

查找Books的后代,因为Books是最高节点,故正则表达式以",Books,"开头

db.categories.find( { path: /^,Books,/ } )

对path字段创建索引

db.categories.createIndex( { path: 1 } )

根据具体查询,这个索引可能会提高性能:

  • 对于根节点Books的子树上的查询(例如,/^,Books,/ 或者 /^,Books,Programming,/),path字段的索引会明显提高查询性能。
  • 对于未提供到根节点的路径的子树上的查询(例如, /,Databases,/),或者类似的子树查询,节点在加索引的字符串的中间,这时查询必须扫描所有索引。对这些查询来说,如果索引比整个集合小很多,则索引会提高查询性能。

内嵌集(Nested Sets)的模型树结构

这个数据模型描述了树形结构,优化了查找子树的性能,但是也会导致树的易变性的增加。

模式

内嵌集模式对树作一个往返遍历并标示树中每个节点为停留点。应用程序对每个节点访问两次,第一次为去往遍历,第二次为返回遍历。内嵌集模式中文档除了存储树节点,还存储了其父节点的id,存储其去往停留点到left字段,以及存储返回停留点到right字段。

考虑如下目录的层级关系

下面代码演示了内嵌集的实现

db.categories.insert( { _id: "Books", parent: 0, left: 1, right: 12 } )
db.categories.insert( { _id: "Programming", parent: "Books", left: 2, right: 11 } )
db.categories.insert( { _id: "Languages", parent: "Programming", left: 3, right: 4 } )
db.categories.insert( { _id: "Databases", parent: "Programming", left: 5, right: 10 } )
db.categories.insert( { _id: "MongoDB", parent: "Databases", left: 6, right: 7 } )
db.categories.insert( { _id: "dbm", parent: "Databases", left: 8, right: 9 } )

查询获取一个节点的后代

var databaseCategory = db.categories.findOne( { _id: "Databases" } );
db.categories.find( { left: { $gt: databaseCategory.left }, right: { $lt: databaseCategory.right } } );

内嵌集模式提供了为查找子树的一个快速并有效率的方案,但是在修改树结构时较为麻烦。故这种模式适合不会改变结构的静态树。

五、模型特定的程序上下文

原子操作的模型数据

MongoDB中的写操作,例如db.collection.update(),db.collection.findAndModify(),db.collection.remove()在单个文档级别是原子的。对于那些需要一起被更新的字段,将字段嵌入到文档中可以确保对这些字段的更新是原子的。

例如,考虑这样一种情况,当需要维护书的信息时,包括可以检出的书数量以及当前检出信息,如何确定这两个操作整体的原子性?

可获得的书和检出信息必须同步。这样,将available字段和checkout字段嵌入相同的文档以确保更新文档是原子性的。

{
_id: 123456789,
title: "MongoDB: The Definitive Guide",
author: [ "Kristina Chodorow", "Mike Dirolf" ],
published_date: ISODate("2010-09-24"),
pages: 216,
language: "English",
publisher_id: "oreilly",
available: 3,
checkout: [ { by: "joe", date: ISODate("2012-10-15") } ]
}

那么,根据新的检出信息,可以使用db.collection.update()进行更新,并且对available字段和checkout字段的更新是原子性的。

db.books.update (
{ _id: 123456789, available: { $gt: 0 } }, //查询条件
{
$inc: { available: -1 },    //available字段减1
$push: { checkout: { by: "abc", date: new Date() } }  // checkout字段数组增加一个元素
}
)

以上操作返回一个WriteResult()对象,包含了操作的有关信息

WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

nMatched字段表明1个文档匹配这个更新条件,nModified表明这个操作更新了一个文档。

支持关键字查询的模型数据

关键字搜索与文本搜索或者全文搜索不同,它不提供词干提取或者其他文本处理特性。

此模式描述了支持关键字搜索的方法,以支持程序搜索功能,这个搜索方法使用存储在相同文档中的数组中的关键字,而这个数组作为这个文档的文本字段。与多键索引结合后,这个模式可以支持程序的关键字搜索操作。

模式

为了向文档中增加结构以支持基于关键字的查询,首先向文档中创建一个数组字段,并将关键字以字符串形式添加到数组中。然后可以对这个数组创建多键索引,并创建查询从这个数组中选择数值。

实例:

给定一个图书卷的集合,并想提供一个机遇主题的搜索。对每个卷而言,我们增加topics数组,并尽可能的对这个卷添加关键字到数组中。对Moby-Dick卷可以由如下文档表示,

{ title : "Moby-Dick" ,
author : "Herman Melville" ,
published : 1851 ,
ISBN : 0451526996 ,
topics : [ "whaling" , "allegory" , "revenge" , "American" ,
"novel" , "nautical" , "voyage" , "Cape Cod" ]
}

然后对topics数组创建多键索引

db.volumes.createIndex( { topics: 1 } )

多键索引对topics数组中的每个关键字创建索引项。例如,索引中有一个是关于"whaling"的索引项,另一个是关于"allegory"的索引项。

然后基于关键字的查询示例如下

db.volumes.findOne( { topics : "voyage" }, { title: 1 } )

提示:数组中的元素数量很大时,如有几百或上千的关键字,那插入操作会导致加索引花费很大。

关键字索引的限制

使用特定的数据模型和多键索引,MongoDB可以支持关键字搜索。然而,这些关键字索引在以下方面与全文搜索比较则显得不足或者无法相比:

  • 词干提取。关键字查询无法解析关键字为根或者有关词语。
  • 同义。关键字搜索特性必须在应用层提供同义支持或相关查询。
  • 排序。关键字查询不提供判断结果权重的方式。
  • 异步索引。MongoDB同步创建索引,这意味着为关键字使用的索引总是处于当前的并可实时操作。然而,异步创建的索引在某种内容和工作负荷下效率更高。

MongoDB数据模型(二)的更多相关文章

  1. MongoDB(二)-- Java API 实现增删改查

    一.下载jar包 http://central.maven.org/maven2/org/mongodb/mongo-java-driver/ 二.代码实现 package com.xbq.mongo ...

  2. 第二课 MongoDB 数据模型

    1.课程大纲 本课程主要介绍MongoDB数据模型相关知识.包含文档.集合与数据库的基本概念.用法及命名规则:MongoDB主要的数据类型介绍以及MongoDB Shell的简单介绍与使用. 文档 ( ...

  3. MongoDB数据模型和索引学习总结

    MongoDB数据模型和索引学习总结 1. MongoDB数据模型: MongoDB数据存储结构: MongoDB针对文档(大文件採用GridFS协议)採用BSON(binary json,採用二进制 ...

  4. MongoDB Sharding(二) -- 搭建分片集群

    在上一篇文章中,我们基本了解了分片的概念,本文将着手实践,进行分片集群的搭建 首先我们再来了解一下分片集群的架构,分片集群由三部分构成: mongos:查询路由,在客户端程序和分片之间提供接口.本次实 ...

  5. MongoDB数据模型(三)

    六.数据模型引用 文档 我们已经知道MongoDB以文档的形式存储数据,而文档是JSON风格的数据结构,由一系列的“字段名-值”对组成,如下所示 { "item": "p ...

  6. MongoDB数据模型(一)

    原文地址 一.数据模型介绍 MongoDB中的数据有着灵活的架构.与SQL数据库不同,因为SQL数据库必须先定义表结构,然后才能向其中插入数据,而MongoDB的集合不强制任何文档结构.这个灵活性方便 ...

  7. MongoDB系列二(介绍).

    一.特点 学习一个东西,至少首先得知道它能做什么?适合做什么?有什么优缺点吧? 传统关系型数据库,遵循三大范式.即原子性.唯一性.每列与主键直接关联性.但是后来人们慢慢发现,不要把这些数据分散到多个表 ...

  8. Mongodb数据模型

    描述表关系的方式: 方式一:嵌入式 > db.person.find({name:'zjf'}).pretty() { "_id" : ObjectId("592f ...

  9. MongoDB之二基础入门(安装启动)

    mongodb中有三元素:数据库,集合,文档,其中“集合” 就是对应关系数据库中的“表”,“文档”对应“行”. 一. 下载 上MongoDB官网 ,我们发现有32bit和64bit,这个就要看你系统了 ...

随机推荐

  1. Erlang虚拟机的启动

    Erlang虚拟机的启动 erl实际上是一个shell脚本,设置几个环境变量之后,调用执行erlexec.erlexec的入口点在 otp_src_R15B01/erts/etc/common/erl ...

  2. swift3 UIColor扩展

    //返回一个RGB颜色 class func getColor(r:CGFloat,g:CGFloat,b:CGFloat,l:CGFloat = 1) ->UIColor{ let color ...

  3. XTU 1250 Super Fast Fourier Transform

    $2016$长城信息杯中国大学生程序设计竞赛中南邀请赛$H$题 排序,二分. 对$a$数组,$b$数组从小到大进行排序. 统计每一个$a[i]$作为较大值的时候与$b[i]$对答案的贡献.反过来再统计 ...

  4. dev Gridcontrol根据其cell里面的值显示不同颜色

    要改变cell值得颜色 需要用到cellTemplate和convert <DataTemplate x:Key="PercentageCellColorTemplate"& ...

  5. 使用SLT工具从SAP导入数据到SAP HANA

    在配置完备的情况下,SLT工具的Replicate 工作是在SAP HANA Data Provisioning中完成的 1. Log on to the SAP HANA Studio 2. Cal ...

  6. sql第二天

    --基本格式 select * from tblclass --对于列进行限制 --格式一:取指定列 select cid,cname from TblClass select cname from ...

  7. unity笔录

    ----------------------------unity项目在启动splash的时候黑屏 原因不明------------------测试复制项目  用剔除法测试 笔录开始 先用原版本打包 ...

  8. 【git】TurtoiseGit使用手册

    A,创建本地分支 操作:Create Branch ->输入本地分支名 -> OK B,创建本地分支和远程分支的映射 操作: 1,拉取远程分支:Fetch -> 选中Arbitray ...

  9. HDOJ-1052 田忌赛马(贪心)

    田忌赛马 时间限制:3000 ms | 内存限制:65535 KB 难度:3 描述: Here is a famous story in Chinese history. "That was ...

  10. PHP奇怪现象

    <?php $a = 0.29; $b = (int)($a*100); echo $b; //输出28,PHP版本5.6.24 echo 0.1 + 0.2 - 0.3; //输出5.5511 ...