MongoDB进阶

一、MongoDB简介

MongoDB 是一个基于分布式文件存储的数据库。由 C++ 语言编写。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。

MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。

二、NoSQL

2.1 RDBMS vs NoSQL

RDBMS

  • 高度组织化结构化数据
  • 结构化查询语言(SQL) (SQL)
  • 数据和关系都存储在单独的表中。
  • 数据操纵语言,数据定义语言
  • 严格的一致性
  • 基础事务

NoSQL

  • 代表着不仅仅是SQL
  • 没有声明性查询语言
  • 没有预定义的模式
    -键 - 值对存储,列存储,文档存储,图形数据库
  • 最终一致性,而非ACID属性
  • 非结构化和不可预知的数据
  • CAP定理
  • 高性能,高可用性和可伸缩性
2.2 CAP定理(CAP theorem)

在计算机科学中, CAP定理(CAP theorem), 又被称作 布鲁尔定理(Brewer's theorem), 它指出对于一个分布式计算系统来说,不可能同时满足以下三点:

  • 一致性(Consistency) (所有节点在同一时间具有相同的数据)
  • 可用性(Availability) (保证每个请求不管成功或者失败都有响应)
  • 分隔容忍(Partition tolerance) (系统中任意信息的丢失或失败不会影响系统的继续运作)

CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,最多只能同时较好的满足两个。因此,根据 CAP 原理将 NoSQL 数据库分成了满足 CA 原则、满足 CP 原则和满足 AP 原则三 大类:

  • CA - 单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大。
  • CP - 满足一致性,分区容忍性的系统,通常性能不是特别高。
  • AP - 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。
2.3 NoSQL的优点/缺点

优点:

  • 高可扩展性
  • 分布式计算
  • 低成本
  • 架构的灵活性,半结构化数据
  • 没有复杂的关系

缺点:

  • 没有标准化
  • 有限的查询功能(到目前为止)
  • 最终一致是不直观的程序
2.4 BASE

BASE:Basically Available, Soft-state, Eventually Consistent。 由 Eric Brewer 定义。

CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,最多只能同时较好的满足两个。BASE是NoSQL数据库通常对可用性及一致性的弱要求原则:

  • Basically Available --基本可用
  • Soft-state --软状态/柔性事务。 "Soft state" 可以理解为"无连接"的, 而 "Hard state" 是"面向连接"的
  • Eventually Consistency -- 最终一致性, 也是 ACID 的最终目的。
2.5 ACID vs BASE
ACID vs BASE
2.6 NoSQL 数据库分类
NoSQL 数据库分类

三、MongoDB概念

3.1 MongoDB基本概念
MongoDB基本概念
3.2 数据库

一个MongoDB中可以建立多个数据库。MongoDB的默认数据库为"db",该数据库存储在data目录中。MongoDB的单个实例可以容纳多个独立的数据库,每一个都有自己的集合和权限,不同的数据库也放置在不同的文件中。

3.3 文档(Document)

文档是一组键值(key-value)对(即 BSON)。MongoDB 的文档不需要设置相同的字段,并且相同的字段不需要相同的数据类型,这与关系型数据库有很大的区别,也是 MongoDB 非常突出的特点。

RDBMS 与 MongoDB 对应的术语
3.4 集合

集合就是 MongoDB 文档组,类似于 RDBMS (关系数据库管理系统:Relational Database Management System)中的表格。集合存在于数据库中,集合没有固定的结构,这意味着你在对集合可以插入不同格式和类型的数据,但通常情况下我们插入集合的数据都会有一定的关联性。比如,我们可以将以下不同数据结构的文档插入到集合中:

{"site":"www.baidu.com"}
{"site":"www.google.com","name":"Google"}
{"site":"www.runoob.com","name":"菜鸟教程","num":5}

当第一个文档插入时,集合就会被创建。

合法的集合名

  • 集合名不能是空字符串""。
  • 集合名不能含有\0字符(空字符),这个字符表示集合名的结尾。
  • 集合名不能以"system."开头,这是为系统集合保留的前缀。
  • 用户创建的集合名字不能含有保留字符。有些驱动程序的确支持在集合名里面包含,这是因为- 某些系统生成的集合中包含该字符。除非你要访问这种系统创建的集合,否则千万不要在名字里出现$。
3.5 MongoDB 数据类型

下表为MongoDB中常用的几种数据类型。

MongoDB中常用的几种数据类型
3.5.1 ObjectId

ObjectId 是一个12字节 BSON 类型数据,有以下格式:

  • 前4个字节表示时间戳
  • 接下来的3个字节是机器标识码
  • 紧接的两个字节由进程id组成(PID)
  • 最后三个字节是随机数。
ObjectId

MongoDB中存储的文档必须有一个"_id"键。这个键的值可以是任何类型的,默认是个ObjectId对象。

在一个集合里面,每个文档都有唯一的"_id"值,来确保集合里面每个文档都能被唯一标识。

MongoDB采用ObjectId,而不是其他比较常规的做法(比如自动增加的主键)的主要原因,因为在多个 服务器上同步自动增加主键值既费力还费时。

对象由于 ObjectId 中保存了创建的时间戳,所以你不需要为你的文档保存时间戳字段,你可以通过 getTimestamp 函数来获取文档的创建时间:

> var newObject = ObjectId()
> newObject.getTimestamp()

四、MongoDB连接

标准 URI 连接语法

mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]
  • mongodb:// 这是固定的格式,必须要指定。

  • username:password@ 可选项,如果设置,在连接数据库服务器之后,驱动都会尝试登录这个数据库

  • host1 必须的指定至少一个host, host1 是这个URI唯一要填写的。它指定了要连接服务器的地址。如果要连接复制集,请指定多个主机地址。

  • portX 可选的指定端口,如果不填,默认为27017

  • /database 如果指定username:password@,连接并验证登录指定数据库。若不指定,默认打开 test 数据库。

  • ?options 是连接选项。如果不使用/database,则前面需要加上/。所有连接选项都是键值对name=value,键值对之间通过&或;(分号)隔开

五、MongoDB $type 操作符

$type操作符是基于BSON类型来检索集合中匹配的数据类型,并返回结果。MongoDB 中可以使用的类型如下表所示:

MongoDB类型

如果想获取 "col" 集合中 title 为 String 的数据,你可以使用以下命令:

db.col.find({"title" : {$type : 2}})
或
db.col.find({"title" : {$type : 'string'}})

六、MongoDB 聚合

MongoDB 中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。有点类似 SQL 语句中的count(*)

6.1 aggregate() 方法

aggregate() 方法的基本语法格式如下所示:

>db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)

集合中的数据:

{
   _id: ObjectId(7df78ad8902c)
   title: 'MongoDB Overview', 
   description: 'MongoDB is no sql database',
   by_user: 'runoob.com',
   url: 'http://www.runoob.com',
   tags: ['mongodb', 'database', 'NoSQL'],
   likes: 100
}

计算每个作者所写的文章数,使用aggregate()计算结果如下:

> db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : 1}}}])
{
   "result" : [
      {
         "_id" : "runoob.com",
         "num_tutorial" : 2
      },
      {
         "_id" : "Neo4j",
         "num_tutorial" : 1
      }
   ],
   "ok" : 1
}
>

以上实例类似sql语句:

select by_user, count(*) from mycol group by by_user

聚合的表达式:


聚合的表达式
6.2 管道的概念

管道在Unix和Linux中一般用于将当前命令的输出结果作为下一个命令的参数。MongoDB的聚合管道将MongoDB文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的。

表达式:处理输入文档并输出。表达式是无状态的,只能用于计算当前聚合管道的文档,不能处理其它的文档。

这里我们介绍一下聚合框架中常用的几个操作:

  • $project:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。

  • $match:用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作。

  • $limit:用来限制MongoDB聚合管道返回的文档数。

  • $skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。

  • $unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。

  • $group:将集合中的文档分组,可用于统计结果。

  • $sort:将输入文档排序后输出。

  • $geoNear:输出接近某一地理位置的有序文档。

6.2.1 管道操作符实例

1、$project实例

db.article.aggregate(
    { $project : {
        title : 1 ,
        author : 1 ,
    }}
 );

这样的话结果中就只还有_id,tilte和author三个字段了,默认情况下_id字段是被包含的,如果要想不包含_id话可以这样:

db.article.aggregate(
    { $project : {
        _id : 0 ,
        title : 1 ,
        author : 1
    }});

2.$match实例

db.articles.aggregate( [
  { $match : { score : { $gt : 70, $lte : 90 } } },
  { $group: { _id: null, count: { $sum: 1 } } }
] );

$match用于获取分数大于70小于或等于90记录,然后将符合条件的记录送到下一阶段$group管道操作符进行处理。

3.$skip实例

db.article.aggregate(
    { $skip : 5 });

经过$skip管道操作符处理后,前五个文档被"过滤"掉。

七、MongoDB 复制(副本集)

MongoDB复制是将数据同步在多个服务器的过程。复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性, 并可以保证数据的安全性。复制还允许您从硬件故障和服务中断中恢复数据。

7.1 MongoDB复制原理

MongoDB的复制至少需要两个节点。其中一个是主节点,负责处理客户端请求,其余的都是从节点,负责复制主节点上的数据。MongoDB各个节点常见的搭配方式为:一主一从、一主多从。主节点记录在其上的所有操作oplog,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致。

副本集特征:

  • N 个节点的集群
  • 任何节点可作为主节点
  • 所有写入操作都在主节点上
  • 自动故障转移
  • 自动恢复

八、MongoDB 分片

8.1 分片

在Mongodb里面存在另一种集群,就是分片技术,可以满足MongoDB数据量大量增长的需求。当MongoDB存储海量的数据时,一台机器可能不足以存储数据,也可能不足以提供可接受的读写吞吐量。这时,我们就可以通过在多台机器上分割数据,使得数据库系统能存储和处理更多的数据。

8.2 为什么使用分片
  • 复制所有的写入操作到主节点
  • 延迟的敏感数据会在主节点查询
  • 单个副本集限制在12个节点
  • 当请求量巨大时会出现内存不足。
  • 本地磁盘不足
  • 垂直扩展价格昂贵

九、MongoDB 关系

9.1 关系类型

MongoDB 的关系表示多个文档之间在逻辑上的相互联系。文档间可以通过嵌入和引用来建立联系。MongoDB 中的关系可以是:

  • 1:1 (1对1)
  • 1: N (1对多)
  • N: 1 (多对1)
  • N: N (多对多)

接下来我们来考虑下用户与用户地址的关系。一个用户可以有多个地址,所以是一对多的关系。

以下是 user 文档的简单结构:

{
   "_id":ObjectId("52ffc33cd85242f436000001"),
   "name": "Tom Hanks",
   "contact": "987654321",
   "dob": "01-01-1991"
}

以下是 address 文档的简单结构:

{
   "_id":ObjectId("52ffc4a5d85242602e000000"),
   "building": "22 A, Indiana Apt",
   "pincode": 123456,
   "city": "Los Angeles",
   "state": "California"
} 
9.2 嵌入式关系

使用嵌入式方法,我们可以把用户地址嵌入到用户的文档中:

{
   "_id":ObjectId("52ffc33cd85242f436000001"),
   "contact": "987654321",
   "dob": "01-01-1991",
   "name": "Tom Benzamin",
   "address": [
      {
         "building": "22 A, Indiana Apt",
         "pincode": 123456,
         "city": "Los Angeles",
         "state": "California"
      },
      {
         "building": "170 A, Acropolis Apt",
         "pincode": 456789,
         "city": "Chicago",
         "state": "Illinois"
      }]
} 

以上数据保存在单一的文档中,可以比较容易的获取和维护数据。 你可以这样查询用户的地址:

>db.users.findOne({"name":"Tom Benzamin"},{"address":1})

注意:以上查询中 db 和 users 表示数据库和集合。这一句中的 findOne 不能写成 find,因为 find 返回的数据类型是数组,findOne 返回的数据类型是对象。如果这一句使用了 find,那么下面一句应该改写为:

var addresses = db.address.find({"_id":{"$in":result[0]["address_ids"]}})
9.3 引用式关系

引用式关系是设计数据库时经常用到的方法,这种方法把用户数据文档和用户地址数据文档分开,通过引用文档的 id 字段来建立关系。

{
   "_id":ObjectId("52ffc33cd85242f436000001"),
   "contact": "987654321",
   "dob": "01-01-1991",
   "name": "Tom Benzamin",
   "address_ids": [
      ObjectId("52ffc4a5d85242602e000000"),
      ObjectId("52ffc4a5d85242602e000001")
   ]
}

以上实例中,用户文档的 address_ids 字段包含用户地址的对象id(ObjectId)数组。我们可以读取这些用户地址的对象id(ObjectId)来获取用户的详细地址信息。这种方法需要两次查询,第一次查询用户地址的对象id(ObjectId),第二次通过查询的id获取用户的详细地址信息。

>var result = db.users.findOne({"name":"Tom Benzamin"},{"address_ids":1})
>var addresses = db.address.find({"_id":{"$in":result["address_ids"]}})

十、MongoDB 数据库引用

10.1 DBRefs vs 手动引用

MongoDB 引用有两种:

  • 手动引用(Manual References)
  • DBRefs

考虑这样的一个场景,我们在不同的集合中 (address_home, address_office, address_mailing, 等)存储不同的地址(住址,办公室地址,邮件地址等)。这样,我们在调用不同地址时,也需要指定集合,一个文档从多个集合引用文档,我们应该使用 DBRefs。

10.2 使用 DBRefs

DBRef的形式:

{ $ref : , $id : , $db :  }

三个字段表示的意义为:

  • $ref:集合名称
  • $id:引用的id
  • $db:数据库名称,可选参数

以下实例中用户数据文档使用了 DBRef, 字段 address:

{
   "_id":ObjectId("53402597d852426020000002"),
   "address": {
   "$ref": "address_home",
   "$id": ObjectId("534009e4d852427820000002"),
   "$db": "runoob"},
   "contact": "987654321",
   "dob": "01-01-1991",
   "name": "Tom Benzamin"
}

address DBRef 字段指定了引用的地址文档是在 runoob 数据库下的 address_home 集合,id 为 534009e4d852427820000002。以下代码中,我们通过指定 $ref 参数(address_home 集合)来查找集合中指定id的用户地址信息:

>var user = db.users.findOne({"name":"Tom Benzamin"})
>var dbRef = user.address
>db[dbRef.$ref].findOne({"_id":(dbRef.$id)})

在 MongoDB4.0 版本是这样写:

>db[dbRef.$ref].findOne({"_id":ObjectId(dbRef.$id)})

以上实例返回了 address_home 集合中的地址数据:

{
   "_id" : ObjectId("534009e4d852427820000002"),
   "building" : "22 A, Indiana Apt",
   "pincode" : 123456,
   "city" : "Los Angeles",
   "state" : "California"
}

十一、MongoDB 索引

11.1 createIndex() 方法

MongoDB使用 createIndex() 方法来创建索引。

注意在 3.0.0 版本前创建索引方法为 db.collection.ensureIndex(),之后的版本使用了 db.collection.createIndex() 方法,ensureIndex() 还能用,但只是 createIndex() 的别名。

语法
createIndex()方法基本语法格式如下所示:

>db.collection.createIndex(keys, options)

语法中 Key 值为你要创建的索引字段,1 为指定按升序创建索引,如果你想按降序来创建索引指定为 -1 即可。

>db.col.createIndex({"title":1})

createIndex() 方法中你也可以设置使用多个字段创建索引(关系型数据库中称作复合索引)。

>db.col.createIndex({"title":1,"description":-1})

createIndex() 接收可选参数,可选参数列表如下:


可选参数
11.2 索引操作

1、查看集合索引

db.col.getIndexes()

2、查看集合索引大小

db.col.totalIndexSize()

3、删除集合所有索引

db.col.dropIndexes()

4、删除集合指定索引

db.col.dropIndex("索引名称")

十二、MongoDB 索引限制

12.1 额外开销

每个索引占据一定的存储空间,在进行插入,更新和删除操作时也需要对索引进行操作。所以,如果你很少对集合进行读取操作,建议不使用索引。

12.2 内存(RAM)使用

由于索引是存储在内存(RAM)中,你应该确保该索引的大小不超过内存的限制。如果索引的大小大于内存的限制,MongoDB会删除一些索引,这将导致性能下降。

12.3 查询限制

索引不能被以下的查询使用:

  • 正则表达式及非操作符,如 nin,not, 等。
  • 算术运算符,如 $mod, 等。
  • $where 子句。

所以,检测你的语句是否使用索引是一个好的习惯,可以用explain来查看。

12.4 索引键限制

从2.6版本开始,如果现有的索引字段的值超过索引键的限制,MongoDB中不会创建索引。

12.5 插入文档超过索引键限制

如果文档的索引字段值超过了索引键的限制,MongoDB不会将任何文档转换成索引的集合。

12.6 最大范围
  • 集合中索引不能超过64个
  • 索引名的长度不能超过128个字符
  • 一个复合索引最多可以有31个字段

十三、MongoDB 覆盖索引查询

13.1 覆盖索引查询

官方的MongoDB的文档中说明,覆盖查询是以下的查询:

  • 所有的查询字段是索引的一部分
  • 所有的查询返回字段在同一个索引中

由于所有出现在查询中的字段是索引的一部分, MongoDB 无需在整个数据文档中检索匹配查询条件和返回使用相同索引的查询结果。因为索引存在于RAM中,从索引中获取数据比通过扫描文档读取数据要快得多。

13.2 使用覆盖索引查询

为了测试覆盖索引查询,使用以下 users 集合:

{
   "_id": ObjectId("53402597d852426020000002"),
   "contact": "987654321",
   "dob": "01-01-1991",
   "gender": "M",
   "name": "Tom Benzamin",
   "user_name": "tombenzamin"
}

我们在 users 集合中创建联合索引,字段为 gender 和 user_name :

>db.users.ensureIndex({gender:1,user_name:1})

现在,该索引会覆盖以下查询:

>db.users.find({gender:"M"},{user_name:1,_id:0})

也就是说,对于上述查询,MongoDB的不会去数据库文件中查找。相反,它会从索引中提取数据,这是非常快速的数据查询。由于我们的索引中不包括 _id 字段,_id在查询中会默认返回,我们可以在MongoDB的查询结果集中排除它。下面的实例没有排除_id,查询就不会被覆盖:

>db.users.find({gender:"M"},{user_name:1})

但是也可以使用 hint 来强制 MongoDB 使用一个指定的索引。

>db.users.find({gender:"M"},{user_name:1}).hint({gender:1,user_name:1})

最后,如果是以下的查询,不能使用覆盖索引查询:

  • 所有索引字段是一个数组
  • 所有索引字段是一个子文档

十四、MongoDB 高级索引

文档内容

{
   "address": {
      "city": "Los Angeles",
      "state": "California",
      "pincode": "123"
   },
   "tags": [
      "music",
      "cricket",
      "blogs"
   ],
   "name": "Tom Benzamin",
   "gender": "1"
}
14.1 单个字段建索引
db.users.createIndex({name:1})
db.users.createIndex({gender:1})
14.2 创建联合索引
db.users.createIndex({gender:1,name:1})
14.3 数组字段建索引
db.users.createIndex({"tags":1})
14.4 子文档字段建索引
db.users.ensureIndex({"address.city":1,"address.state":1,"address.pincode":1})

十五、MongoDB 查询分析

MongoDB 查询分析可以确保我们所建立的索引是否有效,是查询语句性能分析的重要工具。MongoDB 查询分析常用函数有:explain()hint()

15.1 使用 explain()

explain 操作提供了查询信息,使用索引及查询统计等。有利于我们对索引的优化。接下来我们在 users 集合中创建 gender 和 user_name 的索引:

>db.users.ensureIndex({gender:1,user_name:1})

现在在查询语句中使用 explain :

>db.users.find({gender:"M"},{user_name:1,_id:0}).explain()

以上的 explain() 查询返回如下结果:

{
   "cursor" : "BtreeCursor gender_1_user_name_1",
   "isMultiKey" : false,
   "n" : 1,
   "nscannedObjects" : 0,
   "nscanned" : 1,
   "nscannedObjectsAllPlans" : 0,
   "nscannedAllPlans" : 1,
   "scanAndOrder" : false,
   "indexOnly" : true,
   "nYields" : 0,
   "nChunkSkips" : 0,
   "millis" : 0,
   "indexBounds" : {
      "gender" : [
         [
            "M",
            "M"
         ]
      ],
      "user_name" : [
         [
            {
               "$minElement" : 1
            },
            {
               "$maxElement" : 1
            }
         ]
      ]
   }
}

现在,我们看看这个结果集的字段:

  • indexOnly: 字段为 true ,表示我们使用了索引。

  • cursor:因为这个查询使用了索引,MongoDB 中索引存储在B树结构中,所以这是也使用了 BtreeCursor 类型的游标。如果没有使用索引,游标的类型是 BasicCursor。这个键还会给出你所使用的索引的名称,你通过这个名称可以查看当前数据库下的system.indexes集合(系统自动创建,由于存储索引信息,这个稍微会提到)来得到索引的详细信息。

  • n:当前查询返回的文档数量。

  • nscanned/nscannedObjects:表明当前这次查询一共扫描了集合中多少个文档,我们的目的是,让这个数值和返回文档的数量越接近越好。

  • millis:当前查询所需时间,毫秒数。

  • indexBounds:当前查询具体使用的索引。

15.2 使用 hint()

虽然MongoDB查询优化器一般工作的很不错,但是也可以使用 hint 来强制 MongoDB 使用一个指定的索引。这种方法某些情形下会提升性能。 一个有索引的 collection 并且执行一个多字段的查询(一些字段已经索引了)。如下查询实例指定了使用 gender 和 user_name 索引字段来查询:

>db.users.find({gender:"M"},{user_name:1,_id:0}).hint({gender:1,user_name:1})

可以使用 explain() 函数来分析以上查询:

>db.users.find({gender:"M"},{user_name:1,_id:0}).hint({gender:1,user_name:1}).explain()

十六、MongoDB 原子操作

MongoDB不支持事务,所以,在你的项目中应用时,要注意这点。无论什么设计,都不要要求mongodb保证数据的完整性。

但是MongoDB提供了许多原子操作,比如文档的保存,修改,删除等,都是原子操作。

所谓原子操作就是要么这个文档保存到Mongodb,要么没有保存到Mongodb,不会出现查询到的文档没有保存完整的情况。

十七、MongoDB 全文检索-实现模糊搜索

全文检索对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户的检索方式。MongoDB 从 2.4 版本开始支持全文检索,目前支持15种语言的全文索引。

17.1 启用全文检索

MongoDB 在 2.6 版本以后是默认开启全文检索的,如果你使用之前的版本,你需要使用以下代码来启用全文检索:

>db.adminCommand({setParameter:true,textSearchEnabled:true})

或者使用命令:

mongod --setParameter textSearchEnabled=true
17.2 创建全文索引

考虑以下 posts 集合的文档数据,包含了文章内容(post_text)及标签(tags):

{
   "post_text": "enjoy the mongodb articles on Runoob",
   "tags": [
      "mongodb",
      "runoob"
   ]
}

我们可以对 post_text 字段建立全文索引,这样我们可以搜索文章内的内容:

>db.posts.ensureIndex({post_text:"text"})
17.3 使用全文索引

现在我们已经对 post_text 建立了全文索引,我们可以搜索文章中的关键词 runoob:

>db.posts.find({$text:{$search:"runoob"}})
17.4 性能

全文索引和正则表达式都可以实现模糊搜索,但全文索引性能更高,因为用正则表达式的方式不会走索引,是全表扫描。

十八、MongoDB 正则表达式-实现模糊搜索

正则表达式是使用单个字符串来描述、匹配一系列符合某个句法规则的字符串。许多程序设计语言都支持利用正则表达式进行字符串操作。MongoDB 使用 $regex 操作符来设置匹配字符串的正则表达式。MongoDB使用PCRE (Perl Compatible Regular Expression) 作为正则表达式语言。不同于全文检索,我们使用正则表达式不需要做任何配置。

文档如下:

{
    "_id": {
        "$oid": "6139dd9cff909b83c9633afc"
    },
    "ackid": "315c56d540c14acfb7f65fd8fd5faafa",
    "id": "1631182236919074660",
    "sid": "159190488252417_156192831766529",
    "sender": "159190488252417",
    "sendername": "Alan Chen",
    "mid": "156192831766529",
    "gid": "",
    "type": 0,
    "body": "{\"msg\":{\"isAddBlacklist\":false,\"isInvitation\":0,\"size\":4,\"totalCount\":0,\"type\":2,\"url\":\"https://www.jianshu.com/\"},\"timestamp\":1631182236843,\"type\":2}",
    "status": 0,
    "create_time": {
        "$numberLong": "1631182236920"
    },
     "tags" : [ "runoob" ]
}
18.1 文档字段模糊查询
db.posts.find({sendername:{$regex:"Alan"}})

以上查询也可以写为

db.posts.find({sendername:/Alan/})
18.2 模糊查询不区分大小写
db.posts.find({sendername:{$regex:"alan",$options:"$i"}})
18.3 子文档字段模糊查询

目标:查询body属性里type为2的文档

db.posts.find({body:{$regex:'\"type\":2'}})
18.4 模糊查询数组元素
db.posts.find({tags:{$regex:"run"}})

十九、MongoDB Map Reduce

Map-Reduce是一种计算模型,简单的说就是将大批量的工作(数据)分解(MAP)执行,然后再将结果合并成最终结果(REDUCE)。MongoDB提供的Map-Reduce非常灵活,对于大规模数据分析也相当实用。

19.1 MapReduce 命令
>db.collection.mapReduce(
   function() {emit(key,value);},  //map 函数
   function(key,values) {return reduceFunction},   //reduce 函数
   {
      out: collection,
      query: document,
      sort: document,
      limit: number
   }
)
19.2 样例

现在,我们将在 posts 集合中使用 mapReduce 函数来选取已发布的文章(status:"active"),并通过user_name分组,计算每个用户的文章数:

>db.posts.mapReduce( 
   function() { emit(this.user_name,1); }, 
   function(key, values) {return Array.sum(values)}, 
      {  
         query:{status:"active"},  
         out:"post_total" 
      }
).find()

结果

{ "_id" : "mark", "value" : 4 }
{ "_id" : "runoob", "value" : 1 }

用类似的方式,MapReduce可以被用来构建大型复杂的聚合查询。Map函数和Reduce函数可以使用 JavaScript 来实现,使得MapReduce的使用非常灵活和强大。

二十、MongoDB GridFS

GridFS 用于存储和恢复那些超过16M(BSON文件限制)的文件(如:图片、音频、视频等)。

GridFS 也是文件存储的一种方式,但是它是存储在MonoDB的集合中。

GridFS 可以更好的存储大于16M的文件。

GridFS 会将大文件对象分割成多个小的chunk(文件片段),一般为256k/个,每个chunk将作为MongoDB的一个文档(document)被存储在chunks集合中。

GridFS 用两个集合来存储一个文件:fs.files与fs.chunks。

每个文件的实际内容被存在chunks(二进制数据)中,和文件有关的meta数据(filename,content_type,还有用户自定义的属性)将会被存在files集合中。

二十一、MongoDB 固定集合(Capped Collections)

MongoDB 固定集合(Capped Collections)是性能出色且有着固定大小的集合,对于大小固定,我们可以想象其就像一个环形队列,当集合空间用完后,再插入的元素就会覆盖最初始的头部的元素。

21.1 固定集合的功能特点

可以插入及更新,但更新不能超出collection的大小,否则更新失败,不允许删除,但是可以调用drop()删除集合中的所有行,但是drop后需要显式地重建集合。在32位机子上一个cappped collection的最大值约为482.5M,64位上只受系统文件大小的限制。

21.2 固定集合属性及用法

属性

  • 属性1:对固定集合进行插入速度极快

  • 属性2:按照插入顺序的查询输出速度极快

  • 属性3:能够在插入最新数据时,淘汰最早的数据

用法

  • 用法1:储存日志信息

  • 用法2:缓存一些少量的文档

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容