MongoDB

掌握 MySQL、Redis、MongoDB三者基本就算够用了。

官网

官网 https://www.mongodb.com/

菜鸟教程 https://www.runoob.com/mongodb

安装

拉取MongoDB镜像

docker pull mongo

为mongodb创建Docker卷

docker volume create mongodb_data

启动mongodb容器

docker run --name mongodb -d -p 27017:27017 -v mongodb_data:/data/db -e MONGO_INITDB_ROOT_USERNAME=username -e MONGO_INITDB_ROOT_PASSWORD=password mongo

MongoDB特点

MongoDB 的一些关键特点:

NoSQL

NoSQL(NoSQL = Not Only SQL ),意即”不仅仅是SQL”。

关系型数据库遵循ACID规则

事务在英文中是transaction,和现实世界中的交易很类似,它有如下四个特性:

1、A (Atomicity) 原子性

原子性很容易理解,也就是说事务里的所有操作要么全部做完,要么都不做,事务成功的条件是事务里的所有操作都成功,只要有一个操作失败,整个事务就失败,需要回滚。

比如银行转账,从A账户转100元至B账户,分为两个步骤:1)从A账户取100元;2)存入100元至B账户。这两步要么一起完成,要么一起不完成,如果只完成第一步,第二步失败,钱会莫名其妙少了100元。

2、C (Consistency) 一致性

一致性也比较容易理解,也就是说数据库要一直处于一致的状态,事务的运行不会改变数据库原本的一致性约束。

例如现有完整性约束a+b=10,如果一个事务改变了a,那么必须得改变b,使得事务结束后依然满足a+b=10,否则事务失败。

3、I (Isolation) 独立性

所谓的独立性是指并发的事务之间不会互相影响,如果一个事务要访问的数据正在被另外一个事务修改,只要另外一个事务未提交,它所访问的数据就不受未提交事务的影响。

比如现在有个交易是从A账户转100元至B账户,在这个交易还未完成的情况下,如果此时B查询自己的账户,是看不到新增加的100元的。

4、D (Durability) 持久性

持久性是指一旦事务提交后,它所做的修改将会永久的保存在数据库上,即使出现宕机也不会丢失。

RDBMS vs NoSQL

RDBMS

NoSQL

CAP定理(CAP theorem)

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

CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,最多只能同时较好的满足两个。

因此,根据 CAP 原理将 NoSQL 数据库分成了满足 CA 原则、满足 CP 原则和满足 AP 原则三 大类:

NoSQL的优点/缺点

优点:

缺点:

BASE

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

CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,最多只能同时较好的满足两个。

BASE是NoSQL数据库通常对可用性及一致性的弱要求原则:

ACID vs BASE

ACID BASE
原子性(Atomicity) 基本可用(Basically Available)
一致性(Consistency) 软状态/柔性事务(Soft state)
隔离性(Isolation) 最终一致性 (Eventual consistency)
持久性 (Durable)  

NoSQL 数据库分类

类型 部分代表    特点
列存储 Hbase Cassandra Hypertable 顾名思义,是按列存储数据的。最大的特点是方便存储结构化和半结构化数据,方便做数据压缩,对针对某一列或者某几列的查询有非常大的IO优势。

文档存储    MongoDB CouchDB 文档存储一般用类似json的格式存储,存储的内容是文档型的。这样也就有机会对某些字段建立索引,实现关系数据库的某些功能。

key-value存储   Tokyo Cabinet / Tyrant  Berkeley DB MemcacheDB  Redis 可以通过key快速查询到其value。一般来说,存储不管value的格式,照单全收。(Redis包含了其他功能)

图存储  Neo4J FlockDB 图形关系的最佳存储。使用传统关系数据库来解决的话性能低下,而且设计使用不方便。

对象存储 db4o Versant 通过类似面向对象语言的语法操作数据库,通过对象的方式存取数据。

xml数据库 Berkeley DB XML BaseX 高效的存储XML数据,并支持XML的内部查询语法,比如XQuery,Xpath。

向量数据库 Milvus FAISS (Facebook AI Similarity Search) 专门为存储和检索高维向量数据设计的,主要用于相似性搜索、机器学习和推荐系统等场景。

MongoDB 简介

MongoDB 是一个文档型数据库,数据以类似 JSON 的文档形式存储。

MongoDB 的设计理念是为了应对大数据量、高性能和灵活性需求。

MongoDB使用集合(Collections)来组织文档(Documents),每个文档都是由键值对组成的。

MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成,文档类似于 JSON 对象,字段值可以包含其他文档,数组及文档数组:

{
    name: "sue",
    age: 26,
    status: "A",
    groups: ["news", "sports"]
}

主要特点:

We get start

进入容器

docker exec -it mongodb  bash

进入 mongosh

mongosh --username <username> --password <password> --authenticationDatabase <authDatabase>
<username>:登录的用户名。
<password>:对应用户的密码。
<authDatabase>:用户所在的认证数据库(通常是 admin)。

root@0b390d7bbbf5:/# mongosh --username admin --password password  --auth
enticationDatabase admin         
Current Mongosh Log ID: 67e0d04f1cb0159f446b140a
Connecting to:          mongodb://<credentials>@127.0.0.1:27017/?directConnection=true&serverSelectionTimeoutMS=2000&authSource=admin&appName=mongosh+2.4.2
Using MongoDB:          8.0.5
Using Mongosh:          2.4.2

For mongosh info see: https://www.mongodb.com/docs/mongodb-shell/

------
   The server generated these startup warnings when booting
   2025-03-24T03:18:38.403+00:00: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine. See http://dochub.mongodb.org/core/prodnotes-filesystem
   2025-03-24T03:18:39.904+00:00: For customers running the current memory allocator, we suggest changing the contents of the following sysfsFile
   2025-03-24T03:18:39.904+00:00: For customers running the current memory allocator, we suggest changing the contents of the following sysfsFile
   2025-03-24T03:18:39.904+00:00: We suggest setting the contents of sysfsFile to 0.
   2025-03-24T03:18:39.904+00:00: vm.max_map_count is too low
------

test> 

查看有哪些数据库

test> show dbs
admin   100.00 KiB
config   12.00 KiB
local    72.00 KiB
test> 

切换到新数据库(如果数据库不存在,会在插入数据时自动创建)

test> use myDB
switched to db myDB
myDB> 

插入数据以创建数据库

myDB> db.myCollection.insertOne({name:"example", value: 1});
{
  acknowledged: true,
  insertedId: ObjectId('67e0d1b01cb0159f446b140b')
}

查询文档

myDB> db.myCollection.find()
[
  {
    _id: ObjectId('67e0d1b01cb0159f446b140b'),
    name: 'example',
    value: 1
  }
]

更新文档

myDB> db.myCollection.updateOne({name:"example"}, {$set: {age: 31}})
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
myDB> db.myCollection.updateOne({name:"example"}, {$set: {value: 31}})
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
myDB> db.myCollection.find()
[
  {
    _id: ObjectId('67e0d1b01cb0159f446b140b'),
    name: 'example',
    value: 31,
    age: 31
  }
]

删除文档

myDB> db.myCollection.find()
[
  {
    _id: ObjectId('67e0d1b01cb0159f446b140b'),
    name: 'example',
    value: 31,
    age: 31
  }
]
myDB> db.myCollection.deleteOne({name:"example"})
{ acknowledged: true, deletedCount: 1 }
myDB> db.myCollection.find()

myDB> quit()
root@0b390d7bbbf5:/# 

概念解析

SQL术语概念 MongoDB术语概念 解释说明
database database 数据库
table collection 数据库表/集合
row document 数据记录行/文档
column field 数据字段/域
index index 索引
table joins 表连接,MongoDB不支持。MongoDB 允许在查询中使用 $lookup 操作符来实现类似 SQL 的连接操作。
primaryKey primaryKey 主键,MongoDB自动将_id字段设置为主键

MongoDB相关术语

  1. 文档(Document):MongoDB 的基本数据单元,通常是一个 JSON-like 的结构,可以包含多种数据类型。
  2. 集合(Collection):类似于关系型数据库中的表,集合是一组文档的容器。在 MongoDB 中,一个集合中的文档不需要有一个固定的模式。
  3. 数据库(Database):包含一个或多个集合的 MongoDB 实例。
  4. BSON:Binary JSON 的缩写,是 MongoDB 用来存储和传输文档的二进制形式的 JSON。
  5. 索引(Index):用于优化查询性能的数据结构,可以基于集合中的一个或多个字段创建索引。
  6. 分片(Sharding):一种分布数据到多个服务器(称为分片)的方法,用于处理大数据集和高吞吐量应用。
  7. 副本集(Replica Set):一组维护相同数据集的 MongoDB 服务器,提供数据的冗余备份和高可用性。
  8. 主节点(Primary):副本集中负责处理所有写入操作的服务器。
  9. 从节点(Secondary):副本集中的服务器,用于读取数据和在主节点故障时接管为主节点。
  10. MongoDB Shell:MongoDB 提供的命令行界面,用于与 MongoDB 实例交互。
  11. 聚合框架(Aggregation Framework):用于执行复杂的数据处理和聚合操作的一系列操作。
  12. Map-Reduce:一种编程模型,用于处理大量数据集的并行计算。
  13. GridFS:用于存储和检索大于 BSON 文档大小限制的文件的规范。
  14. ObjectId:MongoDB 为每个文档自动生成的唯一标识符。
  15. CRUD 操作:创建(Create)、读取(Read)、更新(Update)、删除(Delete)操作。
  16. 事务(Transactions):从 MongoDB 4.0 开始支持,允许一组操作作为一个原子单元执行。
  17. 操作符(Operators):用于查询和更新文档的特殊字段。
  18. 连接(Join):MongoDB 允许在查询中使用 $lookup 操作符来实现类似 SQL 的连接操作。
  19. TTL(Time-To-Live):可以为集合中的某些字段设置 TTL,以自动删除旧数据。
  20. 存储引擎(Storage Engine):MongoDB 用于数据存储和管理的底层技术,如 WiredTiger 和 MongoDB 的旧存储引擎 MMAPv1。
  21. MongoDB Compass:MongoDB 的图形界面工具,用于可视化和管理 MongoDB 数据。
  22. MongoDB Atlas:MongoDB 提供的云服务,允许在云中托管 MongoDB 数据库。

数据库

一个 MongoDB 中可以建立多个数据库。

如果在操作时没有指定数据库,MongoDB 会使用一个名为 test 的默认数据库,该数据库存储在 data 目录中。

MongoDB 的单个实例可以容纳多个独立的数据库,每一个都有自己的集合和权限,不同的数据库也放置在不同的文件中。

从 MongoDB 4.0 开始,支持在单个数据库上执行多文档事务。

root@0b390d7bbbf5:/# mongosh --username admin --password password  --authenticationDatabase admin
Current Mongosh Log ID: 67e10c18acf1cfe11a6b140a
Connecting to:          mongodb://<credentials>@127.0.0.1:27017/?directConnection=true&serverSelectionTimeoutMS=2000&authSource=admin&appName=mongosh+2.4.2
Using MongoDB:          8.0.5
Using Mongosh:          2.4.2

For mongosh info see: https://www.mongodb.com/docs/mongodb-shell/

------
   The server generated these startup warnings when booting
   2025-03-24T03:18:38.403+00:00: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine. See http://dochub.mongodb.org/core/prodnotes-filesystem
   2025-03-24T03:18:39.904+00:00: For customers running the current memory allocator, we suggest changing the contents of the following sysfsFile
   2025-03-24T03:18:39.904+00:00: For customers running the current memory allocator, we suggest changing the contents of the following sysfsFile
   2025-03-24T03:18:39.904+00:00: We suggest setting the contents of sysfsFile to 0.
   2025-03-24T03:18:39.904+00:00: vm.max_map_count is too low
------

# 查看当前所使用的数据库
test> db
test
# 查看实例中的数据库
test> show dbs
admin   100.00 KiB
config   60.00 KiB
local    72.00 KiB
myDB     24.00 KiB
# 切换数据库
test> use myDB
switched to db myDB
myDB> 

数据库也通过名字来标识。数据库名可以是满足以下条件的任意UTF-8字符串。

文档

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

{"site":"www.koyeb.app", "name":"note"}
  1. 文档中的键/值对是有序的。
  2. 文档中的值不仅可以是在双引号里面的字符串,还可以是其他几种数据类型(甚至可以是整个嵌入的文档)。
  3. MongoDB区分类型和大小写。
  4. MongoDB的文档不能有重复的键。
  5. 文档的键是字符串。除了少数例外情况,键可以使用任意UTF-8字符。

文档键命名规范:

集合

集合就是 MongoDB 文档组,类似于 RDBMS (关系数据库管理系统:Relational Database Management System)中的表格。

集合存在于数据库中,集合没有固定的结构,这意味着你在对集合可以插入不同格式和类型的数据,但通常情况下我们插入集合的数据都会有一定的关联性。

比如,我们可以将以下不同数据结构的文档插入到集合中:

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

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

合法的集合名:

Capped Collections

Capped collections 就是固定大小的collection。

它有很高的性能以及队列过期的特性(过期按照插入的顺序). 有点和 “RRD” 概念类似。

Capped collections 是高性能自动的维护对象的插入顺序。它非常适合类似记录日志的功能和标准的 collection 不同,你必须要显式的创建一个capped collection,指定一个 collection 的大小,单位是字节。collection 的数据存储空间值提前分配的。

Capped collections 可以按照文档的插入顺序保存到集合中,而且这些文档在磁盘上存放位置也是按照插入顺序来保存的,所以当我们更新Capped collections 中文档的时候,更新后的文档不可以超过之前文档的大小,这样话就可以确保所有文档在磁盘上的位置一直保持不变。

由于 Capped collection 是按照文档的插入顺序而不是使用索引确定插入位置,这样的话可以提高增添数据的效率。MongoDB 的操作日志文件 oplog.rs 就是利用 Capped Collection 来实现的。

要注意的是指定的存储大小包含了数据库的头信息。

db.createCollection("mycoll", {capped:true, size:100000})

"mycoll"
  要创建的集合名称。

capped: true:
  指定集合为固定集合。
  固定集合是一种特殊类型的集合,具有固定的大小,数据以先进先出的方式存储。
  当集合达到指定大小时,旧的数据会被自动覆盖。

size: 100000:
  指定固定集合的最大大小(以字节为单位)。
  这里表示集合的最大大小为 100,000 字节(约 100 KB)。
  如果集合大小超过此限制,旧的数据会被覆盖。

元数据

数据库的信息是存储在集合中。它们使用了系统的命名空间:

dbname.system.*

在MongoDB数据库中名字空间 <dbname>.system.* 是包含多种系统信息的特殊集合(Collection),如下:

集合命名空间 描述
dbname.system.namespaces 列出所有名字空间。
dbname.system.indexes 列出所有索引。
dbname.system.profile 包含数据库概要(profile)信息。
dbname.system.users 列出所有可访问数据库的用户。
dbname.local.sources 包含复制对端(slave)的服务器信息和状态。

对于修改系统集合中的对象有如下限制。

{{system.indexes}}插入数据,可以创建索引。但除此之外该表信息是不可变的(特殊的drop index命令将自动更新相关信息)。

{{system.users}}是可修改的。 {{system.profile}}是可删除的。

上面可能是老版本的,新版本中可能不能用

test> use myDB
switched to db myDB
# 列出所有集合名称
myDB> db.getCollectionNames()
[ 'myCollection' ]
# 列出集合的索引
myDB> db.myCollection.getIndexes()
[ { v: 2, key: { _id: 1 }, name: '_id_' } ]

MongoDB 数据类型

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

数据类型 描述
String 字符串。存储数据常用的数据类型。在 MongoDB 中,UTF-8 编码的字符串才是合法的。
Integer 整型数值。用于存储数值。根据你所采用的服务器,可分为 32 位或 64 位。
Boolean 布尔值。用于存储布尔值(真/假)。
Double 双精度浮点值。用于存储浮点值。
Min/Max keys 将一个值与 BSON(二进制的 JSON)元素的最低值和最高值相对比。
Array 用于将数组或列表或多个值存储为一个键。
Timestamp 时间戳。记录文档修改或添加的具体时间。
Object 用于内嵌文档。
Null 用于创建空值。
Symbol 符号。该数据类型基本上等同于字符串类型,但不同的是,它一般用于采用特殊符号类型的语言。
Date 日期时间。用 UNIX 时间格式来存储当前日期或时间。可以通过创建 Date 对象指定日期时间。
Object ID 对象 ID。用于创建文档的 ID。
Binary Data 二进制数据。用于存储二进制数据。
Code 代码类型。用于在文档中存储 JavaScript 代码。
Regular expression 正则表达式类。

ObjectId

ObjectId 类似唯一主键,可以很快的去生成和排序,包含 12 bytes,含义是:

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

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

admin> use myDB
switched to db myDB
myDB> var newObject = ObjectId()

myDB> newObject.getTimestamp()
ISODate('2025-03-24T08:22:33.000Z')

myDB> newObject.toString()
67e11649b2807af16b6b140b
myDB> 

字符串

BSON 字符串都是 UTF-8 编码。

时间戳

BSON 有一个特殊的时间戳类型用于 MongoDB 内部使用,与普通的 日期 类型不相关。 时间戳值是一个 64 位的值。其中:

在复制集中, oplog 有一个 ts 字段。这个字段中的值使用BSON时间戳表示了操作时间。

BSON 时间戳类型主要用于 MongoDB 内部使用。在大多数情况下的应用开发中,你可以使用 BSON 日期类型。

日期

表示当前距离 Unix新纪元(1970年1月1日)的毫秒数。日期类型是有符号的, 负数表示 1970 年之前的日期。

myDB> var mydate1 = new Date()

myDB> mydate1
ISODate('2025-03-24T08:27:22.279Z')
myDB> typeof mydate1
object
myDB> var mydate2 = ISODate()

myDB> mydate2
ISODate('2025-03-24T08:28:18.389Z')
myDB> typeof mydate2
object

你会发现上面代码怎么好像就是javascript,没错是的,mongodb与js有点关系,后面慢慢就了解到了。

myDB> var mydate1str = mydate1.toString()

myDB> mydate1str
Mon Mar 24 2025 08:27:22 GMT+0000 (Coordinated Universal Time)
myDB> typeof mydate1str
string
myDB> Date()
Mon Mar 24 2025 08:30:28 GMT+0000 (Coordinated Universal Time)

用户管理

在 MongoDB 中进行用户管理涉及用户的创建、分配角色、认证和登录等操作。

下面是一个详细的说明,包含如何使用 MongoDB Shell (mongo) 管理用户。

使用 MongoDB Shell (mongo) 管理用户

切换到目标数据库

在 MongoDB 中,用户是针对特定数据库创建的,使用 use 命令切换到你要创建用户的数据库:

use <database_name>

创建用户

使用 db.createUser 命令创建用户并分配角色。

例如,创建一个名为 testuser 的用户,密码为 password123,并赋予 readWrite 和 dbAdmin 角色:

db.createUser({
  user: "testuser",
  pwd: "password123",
  roles: [
    { role: "readWrite", db: "<database_name>" },
    { role: "dbAdmin", db: "<database_name>" }
  ]
})

# db.createUser({user:"myDbTest",pwd:"password", roles:[{role: "readWrite", db: "myDB"}, {role: "dbAdmin", db: "myDB"}]})
myDB> use admin
switched to db admin
admin> db.createUser({user:"myDbTest",pwd:"password", roles:[{role: "readWrite", db: "myDB"}, {rrole: "dbAdmin", db: "myDB"}]})
{ ok: 1 }
admin> exit
root@0b390d7bbbf5:/# mongosh --username "myDbTest"  --authenticationDatabase admin
Enter password: ********
Current Mongosh Log ID: 67e123fa9950d4d66b6b140a
Connecting to:          mongodb://<credentials>@127.0.0.1:27017/?directConnection=true&serverSelectionTimeoutMS=2000&authSource=admin&appName=mongosh+2.4.2
Using MongoDB:          8.0.5
Using Mongosh:          2.4.2

For mongosh info see: https://www.mongodb.com/docs/mongodb-shell/

test> use myDB
switched to db myDB

验证用户

创建用户后,你可以使用 db.auth 命令验证用户身份:

test> use admin
switched to db admin
admin> db.auth("myDbTest", "password")
{ ok: 1 }

启用身份验证

为了确保只有经过身份验证的用户才能访问 MongoDB,需要启用身份验证。

编辑 MongoDB 配置文件 mongod.conf,并在其中添加以下内容:

security:
  authorization: "enabled"

删除用户

使用 db.dropUser 命令删除指定用户。

例如,删除名为 testuser 的用户:

root@0b390d7bbbf5:/# mongosh --username "admin"  --authenticationDatabase admin
Enter password: ********
Current Mongosh Log ID: 67e1246ee059d2da046b140a
Connecting to:          mongodb://<credentials>@127.0.0.1:27017/?directConnection=true&serverSelectionTimeoutMS=2000&authSource=admin&appName=mongosh+2.4.2
Using MongoDB:          8.0.5
Using Mongosh:          2.4.2

For mongosh info see: https://www.mongodb.com/docs/mongodb-shell/

------
   The server generated these startup warnings when booting
   2025-03-24T03:18:38.403+00:00: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine. See http://dochub.mongodb.org/core/prodnotes-filesystem
   2025-03-24T03:18:39.904+00:00: For customers running the current memory allocator, we suggest changing the contents of the following sysfsFile
   2025-03-24T03:18:39.904+00:00: For customers running the current memory allocator, we suggest changing the contents of the following sysfsFile
   2025-03-24T03:18:39.904+00:00: We suggest setting the contents of sysfsFile to 0.
   2025-03-24T03:18:39.904+00:00: vm.max_map_count is too low
------

test> use admin
switched to db admin
admin> db.dropUser("myDbTest")
{ ok: 1 }

连接

MongoDB URI规则

mongodb://[username:password@]host1[:port1][,...hostN[:portN]][/[defaultauthdb][?options]]

mongodb://:协议头,表示使用 MongoDB。
[username:password@]:(可选)认证信息,包括用户名和密码。
host1[:port1][,...hostN[:portN]]:服务器地址和端口,可以是一个或多个 MongoDB 服务器的地址和端口。
/[defaultauthdb]:(可选)默认认证数据库。
[?options]:(可选)连接选项。
authSource:指定认证数据库。
replicaSet:指定副本集的名称。
ssl:启用 SSL 连接(true 或 false)。
readPreference:指定读偏好,如 primary, primaryPreferred, secondary, secondaryPreferred, nearest。
connectTimeoutMS:指定连接超时时间(毫秒)。
socketTimeoutMS:指定套接字超时时间(毫秒)。

连接到本地 MongoDB 实例(默认端口 27017):

mongodb://localhost

连接到本地 MongoDB 实例,指定数据库:

mongodb://localhost/mydatabase

使用用户名和密码连接到本地 MongoDB 实例:

mongodb://username:password@localhost/mydatabase

连接到远程 MongoDB 实例:

mongodb://remotehost:27017

连接到副本集(Replica Set):

mongodb://host1:27017,host2:27017,host3:27017/mydatabase?replicaSet=myReplicaSet

使用 SSL 连接到 MongoDB:

mongodb://username:password@localhost:27017/mydatabase?ssl=true

使用多个选项连接:

mongodb://username:password@localhost:27017/mydatabase?authSource=admin&ssl=true

更多连接实例

#连接本地数据库服务器,端口是默认的。
mongodb://localhost
#使用用户名fred,密码foobar登录localhost的admin数据库。
mongodb://fred:foobar@localhost
#使用用户名fred,密码foobar登录localhost的baz数据库。
mongodb://fred:foobar@localhost/baz
#连接 replica pair, 服务器1为example1.com服务器2为example2。
mongodb://example1.com:27017,example2.com:27017
#连接 replica set 三台服务器 (端口 27017, 27018, 和27019):
mongodb://localhost,localhost:27018,localhost:27019
#连接 replica set 三台服务器, 写入操作应用在主服务器 并且分布查询到从服务器。
mongodb://host1,host2,host3/?slaveOk=true
#直接连接第一个服务器,无论是replica set一部分或者主服务器或者从服务器。
mongodb://host1,host2,host3/?connect=direct;slaveOk=true
#当你的连接服务器有优先级,还需要列出所有服务器,你可以使用上述连接方式。
#安全模式连接到localhost:
mongodb://localhost/?safe=true
#以安全模式连接到replica set,并且等待至少两个复制服务器成功写入,超时时间设置为2秒。
mongodb://host1,host2,host3/?safe=true;w=2;wtimeoutMS=2000

使用mongosh尝试URI连接

mongosh mongodb://username@password@IP/myDB?authSource=admin 

创建数据库

在MongoDB中,数据库的创建是一个简单的过程,当你首次向MongoDB中插入数据时,如果数据库不存在,MongoDB会自动创建它。

我们只需选择一个数据库名称,并开始向其中插入文档即可。

当你使用 use 命令来指定一个数据库时,如果该数据库不存在,MongoDB将自动创建它。

use DATABASE_NAME
myDB> use myDB
already on db myDB
myDB> db
myDB
myDB> 

如果只是use切换数据库,但是没有像数据库内写入任何内容,则

myDB> use testDB
switched to db testDB
testDB> db
testDB
testDB> show dbs
admin   148.00 KiB
config   48.00 KiB
local    72.00 KiB
myDB     24.00 KiB
testDB> db.collection.insertOne({"name":"name"})
{
  acknowledged: true,
  insertedId: ObjectId('67e2725576db4fd0546b140b')
}
testDB> show dbs
admin   148.00 KiB
config   48.00 KiB
local    72.00 KiB
myDB     24.00 KiB
testDB    8.00 KiB
testDB> 

创建集合可以手动创建

use testDB
db.createCollection("collectioncreate")
#testDB> db.createCollection("collectioncreate")
#{ ok: 1 }

查看数据库列表

show dbs

查看当前数据库

db

删除数据库

use testDB
db.dropDatabase()

删除数据库

testDB> show dbs
admin   148.00 KiB
config   96.00 KiB
local    72.00 KiB
myDB     24.00 KiB
testDB> use myDB
switched to db myDB
myDB> db.dropDatabase()
{ ok: 1, dropped: 'myDB' }
myDB> show dbs
admin   148.00 KiB
config   96.00 KiB
local    72.00 KiB

创建集合

db.createCollection(name, options)
参数名 类型 描述 示例值
capped 布尔值 是否创建一个固定大小的集合。 true
size 数值 集合的最大大小(以字节为单位)。仅在 capped 为 true 时有效。 10485760 (10MB)
max 数值 集合中允许的最大文档数。仅在 capped 为 true 时有效。 5000
validator 对象 用于文档验证的表达式。 { $jsonSchema: { ... }}
validationLevel 字符串 指定文档验证的严格程度。
"off":不进行验证。
"strict":插入和更新操作都必须通过验证(默认)。
"moderate":仅现有文档更新时必须通过验证,插入新文档时不需要。
"strict"
validationAction 字符串 指定文档验证失败时的操作。
"error":阻止插入或更新(默认)。
"warn":允许插入或更新,但会发出警告。
"error"
storageEngine 对象 为集合指定存储引擎配置。 { wiredTiger: { ... }}
collation 对象 指定集合的默认排序规则。 { locale: "en", strength: 2 }

样例具有以下特性:

db.createCollection("myComplexCollection", {
  capped: true,
  size: 10485760,
  max: 5000,
  validator: { $jsonSchema: {
    bsonType: "object",
    required: ["name", "email"],
    properties: {
      name: {
        bsonType: "string",
        description: "必须为字符串且为必填项"
      },
      email: {
        bsonType: "string",
        pattern: "^.+@.+$",
        description: "必须为有效的电子邮件地址"
      }
    }
  }},
  validationLevel: "strict",
  validationAction: "error",
  storageEngine: {
    wiredTiger: { configString: "block_compressor=zstd" }
  },
  collation: { locale: "en", strength: 2 }
});

更新集合名

在 MongoDB 中,不能直接通过命令来重命名集合。

MongoDB 可以使用 renameCollection 方法来来重命名集合。

renameCollection 方法在 MongoDB 的 admin 数据库中运行,可以将一个集合重命名为另一个名称。

renameCollection 命令的语法:

db.adminCommand({
  renameCollection: "sourceDb.sourceCollection",
  to: "targetDb.targetCollection",
  dropTarget: <boolean>
})

注意事项:

  1. 权限要求:执行 renameCollection 命令需要具有对源数据库和目标数据库的适当权限。通常需要 dbAdmin 或 dbOwner 角色。
  2. 目标集合不存在:目标集合不能已经存在。如果目标集合存在,则会返回错误。
  3. 索引和数据:重命名集合会保留所有文档和索引。
myDB> show dbs
admin   148.00 KiB
config   96.00 KiB
local    72.00 KiB
myDB> show collections

myDB> db.myCollection.find()
[
  {
    _id: ObjectId('67e2794e1bf47fea606b140b'),
    name: 'gaowanlu',
    birth: '2001-12-21'
  }
]
myDB> db.adminCommand({renameCollection:"myDB.myCollection",to:"myDB.gaowanlu",dropTarget:false})
{ ok: 1 }
myDB> show collections
gaowanlu
myDB> 

删除集合

MongoDB 中使用 drop() 方法来删除集合。

drop() 方法可以永久地从数据库中删除指定的集合及其所有文档,这是一个不可逆的操作,因此需要谨慎使用。

db.collection.drop()
# 如果成功删除选定集合,则 drop() 方法返回 true,否则返回 false。
myDB> show collections
gaowanlu
myDB> db.gaowanlu.drop()
true
myDB> show collections

myDB> 

插入文档

所有存储在集合中的数据都是 BSON 格式。

BSON 是一种类似 JSON 的二进制形式的存储格式,是 Binary JSON 的简称。

insertOne()

insertOne() 方法用于在集合中插入单个文档。

db.collection.insertOne(document, options)
db.myCollection.insertOne({
    name: "Alice",
    age: 25,
    city: "New York"
});
{
    "acknowledged": true,
    "insertedId": ObjectId("60c72b2f9b1d8b5a5f8e2b2d")
}

insertMany()

insertMany() 方法用于在集合中插入多个文档。

db.collection.insertMany(documents, options)
db.myCollection.insertMany([
    { name: "Bob", age: 30, city: "Los Angeles" },
    { name: "Charlie", age: 35, city: "Chicago" }
]);
{
    "acknowledged": true,
    "insertedIds": [
        ObjectId("60c72b2f9b1d8b5a5f8e2b2e"),
        ObjectId("60c72b2f9b1d8b5a5f8e2b2f")
    ]
}

插入时的验证, MongoDB 会根据集合的 schema 验证规则(如果定义了)对插入的文档进行验证。如果文档不符合规则,插入操作会失败。

myDB> db.createCollection("users", {
...   validator: {
...     age: { $gt: 0 }
...   }
... });
{ ok: 1 }
myDB> db.users.insertOne({ name: "John", age: -5 });
Uncaught:
MongoServerError: Document failed validation
Additional information: {
  failingDocumentId: ObjectId('67e27cc51bf47fea606b140c'),
  details: {
    operatorName: '$gt',
    specifiedAs: { age: { '$gt': 0 } },
    reason: 'comparison failed',
    consideredValue: -5
  }
}

更新文档

mongosh> show dbs
admin   148.00 KiB
config   60.00 KiB
local    72.00 KiB
mongosh> use myDB
switched to db myDB
myDB> show collections

myDB>

在 MongoDB 中,更新文档的操作可以使用多种方法实现,常用的方法包括 updateOne()、updateMany()、replaceOne() 和 findOneAndUpdate()。

updateOne()

updateOne() 方法用于更新匹配过滤器的单个文档。

db.collection.updateOne(filter, update, options)
# filter:用于查找文档的查询条件。
# update:指定更新操作的文档或更新操作符。

# options:可选参数对象,如 upsert、arrayFilters 等。
upsert
类型: Boolean
默认值: false
作用: 如果没有找到符合条件的文档,是否插入一个新文档。
db.collection.updateMany(
  { name: "example" },
  { $set: { age: 30 } },
  { upsert: true }
);
arrayFilters
类型: Array
作用: 当更新嵌套数组中的元素时,指定过滤条件以匹配数组中的特定元素。
db.collection.updateMany(
  { "items.name": "example" },
  { $set: { "items.$[elem].price": 100 } },
  { arrayFilters: [{ "elem.name": "example" }] }
);
writeConcern
类型: Object
作用: 指定写入操作的确认级别。
db.collection.updateMany(
  { status: "pending" },
  { $set: { status: "completed" } },
  { writeConcern: { w: "majority", wtimeout: 5000 } }
);
collation
类型: Object
作用: 指定字符串比较的规则(如大小写敏感性、语言区域设置等)。
db.collection.updateMany(
  { name: "example" },
  { $set: { status: "active" } },
  { collation: { locale: "en", strength: 2 } }
);
returnDocument
类型: String
 findOneAndUpdate 中使用,指定返回更新前 ("before") 或更新后 ("after") 的文档。
db.myCollection.findOneAndUpdate(
    { name: "Charlie" },              // 过滤条件
    { $set: { age: 36 } },            // 更新操作
    { returnDocument: "after" }       // 可选参数,返回更新后的文档
);

样例

myDB> db.myCollection.updateOne(
...     { name: "Alice" },                // 过滤条件
...     { $set: { age: 26 } },            // 更新操作
...     { upsert: true }                 // 可选参数 upsert 表示没有就插入
... );
{
  acknowledged: true,
  insertedId: ObjectId('67e656a47b587329a21774fc'),
  matchedCount: 0,
  modifiedCount: 0,
  upsertedCount: 1
}
myDB> db.myCollection.find();
[
  { _id: ObjectId('67e656a47b587329a21774fc'), name: 'Alice', age: 26 }
]
myDB> db.myCollection.updateOne(
...     { name: "Alice" },                // 过滤条件
...     { $set: { age: 79 } },            // 更新操作
...     { upsert: false }                 // 可选参数
... );
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
myDB> db.myCollection.find();
[
  { _id: ObjectId('67e656a47b587329a21774fc'), name: 'Alice', age: 79 }
]

updateMany()

updateMany() 方法用于更新所有匹配过滤器的文档。

db.collection.updateMany(filter, update, options)
# filter:用于查找文档的查询条件。
# update:指定更新操作的文档或更新操作符。
# options:可选参数对象,如 upsert、arrayFilters 等。
myDB> db.myCollection.find();
[
  { _id: ObjectId('67e656a47b587329a21774fc'), name: 'Alice', age: 79 }
]
myDB> db.myCollection.updateMany(
...     { age: { $gte: 30 } },             // 过滤条件 age>=30
...     { $set: { status: "active" } },   // 更新操作
...     { upsert: false }                  // 可选参数
... );
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
myDB> db.myCollection.find();
[
  {
    _id: ObjectId('67e656a47b587329a21774fc'),
    name: 'Alice',
    age: 79,
    status: 'active'
  }
]
myDB> 

replaceOne()

replaceOne() 方法用于替换匹配过滤器的单个文档,新的文档将完全替换旧的文档。

db.collection.replaceOne(filter, replacement, options)
# filter:用于查找文档的查询条件。
# replacement:新的文档,将替换旧的文档。
# options:可选参数对象,如 upsert 等。
myDB> db.myCollection.find();
[
  {
    _id: ObjectId('67e656a47b587329a21774fc'),
    name: 'Bob',
    age: 31,
    money: 0
  }
]
myDB> db.myCollection.replaceOne(
...     { _id: ObjectId("67e656a47b587329a21774fc") },                  // 过滤条件
...     { name: "Bob", age: 32}          // 新文档
... );
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
myDB> db.myCollection.find();
[ { _id: ObjectId('67e656a47b587329a21774fc'), name: 'Bob', age: 32 } ]

findOneAndUpdate()

findOneAndUpdate() 方法用于查找并更新单个文档,可以选择返回更新前或更新后的文档。

db.collection.findOneAndUpdate(filter, update, options)
# filter:用于查找文档的查询条件。
# update:指定更新操作的文档或更新操作符。
# options:可选参数对象,如 projection、sort、upsert、returnDocument 等。
myDB> db.myCollection.find();
[ { _id: ObjectId('67e656a47b587329a21774fc'), name: 'Bob', age: 32 } ]
myDB> db.myCollection.find();
[ { _id: ObjectId('67e656a47b587329a21774fc'), name: 'Bob', age: 32 } ]
myDB> db.myCollection.findOneAndUpdate(
...     { name: "Bob" },              // 过滤条件
...     { $set: { age: 36 } },            // 更新操作
...     { returnDocument: "before" }       // 可选参数,返回更新后的文档
... );
{ _id: ObjectId('67e656a47b587329a21774fc'), name: 'Bob', age: 32 }
myDB>  db.myCollection.find();
[ { _id: ObjectId('67e656a47b587329a21774fc'), name: 'Bob', age: 36 } ]
myDB> 

删除文档

常用的删除文档方法包括 deleteOne()、deleteMany() 以及 findOneAndDelete()。

deleteOne()

deleteOne() 方法用于删除匹配过滤器的单个文档。

db.collection.deleteOne(filter, options)
# filter:用于查找要删除的文档的查询条件。
# options(可选):一个可选参数对象。

样例

myDB>  db.myCollection.find();
[ { _id: ObjectId('67e656a47b587329a21774fc'), name: 'Bob', age: 36 } ]
myDB> db.myCollection.deleteOne({name:"Bob"});
{ acknowledged: true, deletedCount: 1 }
myDB>  db.myCollection.find();

deleteMany()

deleteMany() 方法用于删除所有匹配过滤器的文档。

db.collection.deleteMany(filter, options)
# filter:用于查找要删除的文档的查询条件。
# options(可选):一个可选参数对象。
db.myCollection.updateOne(
    { name: "Alice" },                // 过滤条件
    { $set: { age: 26 } },            // 更新操作
    { upsert: true }                 // 可选参数
);
myDB> db.myCollection.find()
[
  { _id: ObjectId('67e65f237b587329a21774fd'), name: 'Alice', age: 26 }
]
myDB> db.myCollection.deleteMany({name: 'Alice'});
{ acknowledged: true, deletedCount: 1 }
myDB> db.myCollection.find()

myDB> 

findOneAndDelete()

findOneAndDelete() 方法用于查找并删除单个文档,并可以选择返回删除的文档。

db.collection.findOneAndDelete(filter, options)
#filter:用于查找要删除的文档的查询条件。
#options:可选参数对象,如 projection、sort 等。

#findOneAndDelete 返回被删除的文档,如果找不到匹配的文档,则返回 null。

#这些删除方法的 options 参数通常可以包含以下选项:

#writeConcern:指定写操作的确认级别。
#collation:指定比较字符串时使用的排序规则。
#projection(仅适用于 findOneAndDelete):指定返回的字段。
#sort(仅适用于 findOneAndDelete):指定排序顺序以确定要删除的文档。

样例

myDB>  db.myCollection.find()
[
  { _id: ObjectId('67e65f697b587329a21774fe'), name: 'Alice', age: 26 }
]
myDB> db.myCollection.findOneAndDelete(
...     { name: "Alice" },
...     { projection: { name: 1, age: 1 , _id: 0} }
... );
{ name: 'Alice', age: 26 }
myDB> db.myCollection.find()

myDB> 

查询文档

MongoDB查询文档使用find() findOne() 方法。find() 方法以非结构化的方式来显示所有文档。

find()

db.collection.find(query, projection)
# query:用于查找文档的查询条件。默认为 {},即匹配所有文档。
# projection(可选):指定返回结果中包含或排除的字段。
myDB> db.myCollection.find()
[
  { _id: ObjectId('67e668cd7b587329a21774ff'), name: 'Alice', age: 26 }
]
myDB> db.myCollection.find().pretty();
[
  { _id: ObjectId('67e668cd7b587329a21774ff'), name: 'Alice', age: 26 }
]
myDB> db.myCollection.find({age: {$gt:25}}, {name:1,age:0,_id:1}).pretty();
MongoServerError[Location31254]: Cannot do exclusion on field age in inclusion projection
# 在 projection 中,MongoDB 不允许同时对某些字段使用**包含(1)和排除(0)**操作。
# 具体来说:
# 包含(1):表示你希望返回某些字段。
# 排除(0):表示你希望不返回某些字段。
# MongoDB 的规则是:在一个查询的 projection 中,不能同时混用包含和排除字段,但 _id 是一个例外,可以单独排除。

myDB> db.myCollection.find({age: {$gt:25}}, {age:1,_id:1}).pretty();
[ { _id: ObjectId('67e668cd7b587329a21774ff'), age: 26 } ]

findOne()

findOne() 方法用于查找集合中的单个文档。如果找到多个匹配的文档,它只返回第一个。

db.collection.findOne(query, projection)
# query:用于查找文档的查询条件。默认为 {},即匹配所有文档。
# projection(可选):指定返回结果中包含或排除的字段。

样例

db.myCollection.findOne(
    { name: "Alice" },
    { name: 1, age: 1, _id: 0 }
);

查询使用比较操作符

MongoDB 支持多种比较操作符,如 $gt、$lt、$gte、$lte、$eq、$ne 等。

# 查找年龄大于 25 的文档:
db.myCollection.find({ age: { $gt: 25 } });

查询使用逻辑操作符

MongoDB 支持多种逻辑操作符,如 $and、$or、$not、$nor 等。

# 查找年龄大于 25 且城市为 "New York" 的文档:
db.myCollection.find({
    $and: [
        { age: { $gt: 25 } },
        { city: "New York" }
    ]
});

查询使用正则表达式

可以使用正则表达式进行模式匹配查询。

# 查找名字以 "A" 开头的文档:
db.myCollection.find({ name: /^A/ });

查询使用投影

投影用于控制查询结果中返回的字段。可以使用包含字段和排除字段两种方式。

# 只返回名字和年龄字段:
db.myCollection.find(
    { age: { $gt: 25 } },
    { name: 1, age: 1, _id: 0 }
);

查询使用排序

可以对查询结果进行排序。

db.myCollection.find().sort({ age: -1 });

查询使用限制与跳过

可以对查询结果进行限制和跳过指定数量的文档。

# 返回前 10 个文档:
db.myCollection.find().limit(10);
# 跳过前 5 个文档,返回接下来的 10 个文档:
db.myCollection.find().skip(5).limit(10);

样例

# 查找年龄大于 25 且城市为 "New York" 的文档,只返回名字和年龄字段,按年龄降序排序,并返回前 10 个文档。
db.myCollection.find(
    {
        $and: [
            { age: { $gt: 25 } },
            { city: "New York" }
        ]
    },
    { name: 1, age: 1, _id: 0 }
).sort({ age: -1 }).limit(10);

MongoDB 与 RDBMS Where语句比较

如果你熟悉常规的 SQL 数据,通过下表可以更好的理解 MongoDB 的条件语句查询:

操作 格式 范例 RDBMS中的类似语句
等于 {<key>:<value>} db.col.find({"by":"菜鸟教程"}).pretty() where by = '菜鸟教程'
小于 {<key>:{$lt:<value>}} db.col.find({"likes":{$lt:50}}).pretty() where likes < 50
小于或等于 {<key>:{$lte:<value>}} db.col.find({"likes":{$lte:50}}).pretty() where likes <= 50
大于 {<key>:{$gt:<value>}} db.col.find({"likes":{$gt:50}}).pretty() where likes > 50
大于或等于 {<key>:{$gte:<value>}} db.col.find({"likes":{$gte:50}}).pretty() where likes >= 50
不等于 {<key>:{$ne:<value>}} db.col.find({"likes":{$ne:50}}).pretty() where likes != 50

查询使用AND条件

MongoDB 的 find() 方法可以传入多个键(key),每个键(key)以逗号隔开,即常规 SQL 的 AND 条件。

>db.col.find({key1:value1, key2:value2}).pretty()

查询使用OR条件

MongoDB OR 条件语句使用了关键字 $or,语法格式如下:

>db.col.find(
   {
      $or: [
         {key1: value1}, {key2:value2}
      ]
   }
).pretty()

查询同时使用AND和OR条件

AND 和 OR 联合使用,类似常规 SQL 语句为: where likes>50 AND (by = '菜鸟教程' OR title = 'MongoDB 教程')

>db.col.find({"likes": {$gt:50}, $or: [{"by": "菜鸟教程"},{"title": "MongoDB 教程"}]}).pretty()
{
        "_id" : ObjectId("56063f17ade2f21f36b03133"),
        "title" : "MongoDB 教程",
        "description" : "MongoDB 是一个 Nosql 数据库",
        "by" : "菜鸟教程",
        "url" : "http://www.runoob.com",
        "tags" : [
                "mongodb",
                "database",
                "NoSQL"
        ],
        "likes" : 100
}

条件操作符

在 MongoDB 中,条件操作符用于在查询文档时进行过滤、比较和逻辑操作。

这些操作符可以分为以下几类:比较操作符、逻辑操作符、元素操作符、数组操作符、以及其他常用操作符。

比较操作符有:

操作符 描述 示例
$eq 等于 { age: { $eq: 25 } }
$ne 不等于 { age: { $ne: 25 } }
$gt 大于 { age: { $gt: 25 } }
$gte 大于等于 { age: { $gte: 25 } }
$lt 小于 { age: { $lt: 25 } }
$lte 小于等于 { age: { $lte: 25 } }
$in 在指定的数组中 { age: { $in: [25, 30, 35] } }
$nin 不在指定的数组中 { age: { $nin: [25, 30, 35] } }

$eq 等于

语法格式

{ field: { $eq: value } }

查找年龄等于25的人:

db.collection.find({ age: { $eq: 25 } })

$ne 不等于

语法格式

{ field: { $ne: value } }

查找年龄不等于 25 的人:

db.collection.find({ age: { $ne: 25 } })

&gt 大于

语法格式

{ field: { $gt: value } }

查找年龄大于25的人

db.collection.find({ age: { $gt: 25 } })

$gte 大于等于

语法格式

{ field: { $gte: value } }

查找年龄大于或等于25的人

db.collection.find({ age: { $gte: 25 } })

$lt 小于

语法格式

{ field: { $lt: value } }

查找年龄小于25的人

db.collection.find({ age: { $lt: 25 } })

$lte 小于等于

语法格式

{ field: { $lte: value } }

查找年龄小于或等于25的人

db.collection.find({ age: { $lte: 25 } })

$in 值在数组中

语法格式

{ field: { $in: [value1, value2, ...] } }

查找年龄在20、25和30岁之间的人

db.collection.find({ age: { $in: [20, 25, 30] } })

$nin 值不在数组中

语法格式

{ field: { $nin: [value1, value2, ...] } }

查找年龄不在20、25、30岁之间的人

db.collection.find({ age: { $nin: [20, 25, 30] } })

逻辑操作符

逻辑操作符有:

操作符 描述 示例
$and 逻辑与,符合所有条件 { $and: [ { age: { $gt: 25 } }, { city: "New York" } ] }
$or 逻辑或,符合任意条件 { $or: [ { age: { $lt: 25 } }, { city: "New York" } ] }
$not 取反,不符合条件 { age: { $not: { $gt: 25 } } }
$nor 逻辑或非,均不符合条件 { $nor: [ { age: { $gt: 25 } }, { city: "New York" } ] }

$and 逻辑与

{ $and: [ { condition1 }, { condition2 } ] }

查找年龄大于 25 且小于 35 的人:

db.collection.find({ $and: [{ age: { $gt: 25 } }, { age: { $lt: 35 } }] })

$or 逻辑或

{ $or: [ { condition1 }, { condition2 } ] }

查找年龄大于25或小于18的人

db.collection.find({ $or: [{ age: { $gt: 25 } }, { age: { $lt: 18 } }] })

$not 取反

{ field: { $not: { condition } } }

查找年龄不大于25的人

db.collection.find({ age: { $not: { $gt: 25 } } })

$nor 逻辑或非

没有任何条件成立

{ $nor: [ { condition1 }, { condition2 } ] }

查找年龄不大于 25 且不小于 18 的人:

db.collection.find({ $nor: [{ age: { $gt: 25 } }, { age: { $lt: 18 } }] })

元素操作符

操作符 描述 示例
$exists 字段是否存在 { age: { $exists: true } }
$type 字段的 BSON 类型 { age: { $type: "int" } }

$exists 字段是否存在

{ field: { $exists: boolean } }

查找包含 age 字段的文档:

db.collection.find({ age: { $exists: true } })

$type 字段类型

{ field: { $type: "type" } }

查找age字段为整数类型的文档

db.collection.find({ age: { $type: "int" } })

数组操作符

操作符 描述 示例
$all 数组包含所有指定的元素 { tags: { $all: ["red", "blue"] } }
$elemMatch 数组中的元素匹配指定条件 { results: { $elemMatch: { score: { $gt: 80, $lt: 85 } } } }
$size 数组的长度等于指定值 { tags: { $size: 3 } }

查找数组tags中包含 red 和 blue 的文档

db.myCollection.find({ tags: { $all: ["red", "blue"]} });

$all 数组包含指定的所有元素

{ field: { $all: [value1, value2, ...] } }

查找 tags 数组中包含 “mongodb” 和 “database” 的文档:

db.collection.find({ tags: { $all: ["mongodb", "database"] } })

$elemMatch 数组中至少有一个元素符合条件

{ field: { $elemMatch: { condition } } }

查找 items 数组中 quantity 大于 10 的元素:

db.collection.find({ items: { $elemMatch: { quantity: { $gt: 10 } } } })

$size 数组的大小

{ field: { $size: value } }

查找tags数组大小为3的文档

db.collection.find({ tags: { $size: 3 } })

其他操作符

操作符 描述 示例
$regex 匹配正则表达式 { name: { $regex: /^A/ } }
$text 进行文本搜索 { $text: { $search: "coffee" } }
$where 使用 JavaScript 表达式进行条件过滤 { $where: "this.age > 25" }
$near 查找接近指定点的文档 db.collection.find({ location: { $near: [10, 20], $maxDistance: 1000 } })
$geoWithin 查找在指定地理区域内的文档 db.collection.find({ location: { $geoWithin: { $centerSphere: [[10, 20], 1] } } })

$regex 正则表达式匹配

{ field: { $regex: "pattern" } }

查找 name 以 “John” 开头的文档:

db.collection.find({ name: { $regex: /^John/ } })

$near 接近指定点的文档

{ field: { $near: [longitude, latitude], $maxDistance: distance } }

查找距离某个位置近的文档:

db.collection.find({ location: { $near: [10, 20], $maxDistance: 1000 } })

$geoWithin 查找指定区域内的文档

{ field: { $geoWithin: { $centerSphere: [[longitude, latitude], radius] } } }

查找在指定地理区域内的文档:

db.collection.find({ location: { $geoWithin: { $centerSphere: [[10, 20], 1] } } })

$type 操作符

在 MongoDB 中,$type 操作符用于查询具有指定类型的字段的文档。

db.collection.find({ field: { $type: <type> } })
# field:要检查类型的字段。
# type:指定的 BSON 类型,可以是类型的数字代码或类型名称的字符串。

BSON类型

类型代码 类型名称
1 double
2 string
3 object
4 array
5 binData
6 undefined
7 objectId
8 bool
9 date
10 null
11 regex
12 dbPointer
13 javascript
14 symbol
15 javascriptWithScope
16 int
17 timestamp
18 long
19 decimal
255 minKey
127 maxKey

实例

查找字段类型为字符串的文档:

db.myCollection.find({ fieldName: { $type: "string" } })

或使用类型代码:

db.myCollection.find({ fieldName: { $type: 2 } })

查找字段类型为数字的文档,例如,查找 age 字段类型为整数的文档:

db.myCollection.find({ age: { $type: "int" } })
db.myCollection.find({ age: { $type: 16 } })

查找字段类型为布尔值的文档:

db.myCollection.find({ isActive: { $type: "bool" } })
db.myCollection.find({ isActive: { $type: 8 } })

查找字段类型为日期的文档:

db.myCollection.find({ createdAt: { $type: "date" } })
db.myCollection.find({ createdAt: { $type: 9 } })

查找字段类型为多种类型的文档,例如,查找 value 字段类型为字符串或整数的文档:

db.myCollection.find({ value: { $type: ["string", "int"] } })
db.myCollection.find({ value: { $type: [2, 16] } })

查找 details 字段类型为对象,并且 score 字段类型为双精度浮点数的文档:

db.myCollection.find({
    $and: [
        { details: { $type: "object" } },
        { score: { $type: "double" } }
    ]
});
db.myCollection.find({
    $and: [
        { details: { $type: 3 } },
        { score: { $type: 1 } }
    ]
});

Limit与Skip方法

limit() 方法用于限制查询结果返回的文档数量,而 skip() 方法用于跳过指定数量的文档。这两个方法通常一起使用,可以用来实现分页查询或在大型数据集上进行分批处理。

limit() 方法

limit() 方法用于限制查询结果返回的文档数量。

db.collection.find().limit(<limit>)
# <limit>:返回的文档数量。

样例

# 集合 col 中的数据如下:
{ "_id" : ObjectId("56066542ade2f21f36b0313a"), "title" : "PHP 教程", "description" : "PHP 是一种创建动态交互性站点的强有力的服务器端脚本语言。", "by" : "菜鸟教程", "url" : "http://www.runoob.com", "tags" : [ "php" ], "likes" : 200 }
{ "_id" : ObjectId("56066549ade2f21f36b0313b"), "title" : "Java 教程", "description" : "Java 是由Sun Microsystems公司于1995年5月推出的高级程序设计语言。", "by" : "菜鸟教程", "url" : "http://www.runoob.com", "tags" : [ "java" ], "likes" : 150 }
{ "_id" : ObjectId("5606654fade2f21f36b0313c"), "title" : "MongoDB 教程", "description" : "MongoDB 是一个 Nosql 数据库", "by" : "菜鸟教程", "url" : "http://www.runoob.com", "tags" : [ "mongodb" ], "likes" : 100 }
> db.col.find({},{"title":1,_id:0}).limit(2)
{ "title" : "PHP 教程" }
{ "title" : "Java 教程" }
>

注:如果你们没有指定 limit() 方法中的参数则显示集合中的所有数据。

skip() 方法

除了可以使用 limit() 方法来读取指定数量的数据外,还可以使用 skip() 方法来跳过指定数量的数据,skip() 方法同样接受一个数字参数作为跳过的记录条数。

skip() 方法用于跳过指定数量的文档,从而实现分页或分批查询。

db.collection.find().skip(<skip>)
# <skip>:要跳过的文档数量。

# 跳过前 10 个文档,返回接下来的 10 个文档
db.myCollection.find().skip(10).limit(10);

分页查询

// 第一页,每页 10 个文档
db.myCollection.find().skip(0).limit(10);

// 第二页,每页 10 个文档
db.myCollection.find().skip(10).limit(10);

// 第三页,每页 10 个文档
db.myCollection.find().skip(20).limit(10);

排序

在 MongoDB 中使用 sort() 方法对数据进行排序,sort() 方法可以通过参数指定排序的字段,并使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,而 -1 是用于降序排列。

db.collection.find().sort({ field1: 1, field2: -1 })
# { field1: 1, field2: -1 }:指定要排序的字段及排序顺序。1 表示升序,-1 表示降序。
// 按 age 字段升序排序
db.myCollection.find().sort({ age: 1 });
// 按 createdAt 字段降序排序
db.myCollection.find().sort({ createdAt: -1 });
// 先按 age 字段升序排序,再按 createdAt 字段降序排序
db.myCollection.find().sort({ age: 1, createdAt: -1 });

索引

索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。

这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可能要花费几十秒甚至几分钟,这对网站的性能是非常致命的。

索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构

在 MongoDB 中,常见的索引类型包括:

创建索引

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

db.collection.createIndex( keys, options )
# db:数据库的引用。
# collection:集合的名称。
# keys:一个对象,指定了字段名和索引的排序方向(1 表示升序,-1 表示降序)。
# options:一个可选参数,可以包含索引的额外选项。
  # options 参数是一个对象,可以包含多种配置选项,以下是一些常用的选项:
  # unique:如果设置为 true,则创建唯一索引,确保索引字段的值在集合中是唯一的。
  # background:如果设置为 true,则索引创建过程在后台运行,不影响其他数据库操作。
  # name:指定索引的名称,如果不指定,MongoDB 会根据索引的字段自动生成一个名称。
  # sparse:如果设置为 true,创建稀疏索引,只索引那些包含索引字段的文档。
  # expireAfterSeconds:设置索引字段的过期时间,MongoDB 将自动删除过期的文档。
  # v:索引版本,通常不需要手动设置。
  # weights:为文本索引指定权重。

实例

// 创建 age 字段的升序索引
db.myCollection.createIndex({ age: 1 });
// 创建 name 字段的文本索引
db.myCollection.createIndex({ name: "text" });

索引创建

// 创建唯一索引
db.collection.createIndex( { field: 1 }, { unique: true } )
// 创建后台运行的索引
db.collection.createIndex( { field: 1 }, { background: true } )
// 创建稀疏索引
db.collection.createIndex( { field: 1 }, { sparse: true } )
// 创建文本索引并指定权重
db.collection.createIndex( { field: "text" }, { weights: { field: 10 } } )
// 创建地理空间索引
// 对于存储地理位置数据的字段,可以使用 2dsphere 或 2d 索引类型来创建地理空间索引。
// 2dsphere 索引,适用于球形地理数据
db.collection.createIndex( { location: "2dsphere" } )
// 2d 索引,适用于平面地理数据
db.collection.createIndex( { location: "2d" } )

创建哈希索引

从 MongoDB 3.2 版本开始,可以使用哈希索引对字段进行哈希,以支持大范围的数值查找。

db.collection.createIndex( { field: "hashed" } )

查看索引

使用 getIndexes() 方法可以查看集合中的所有索引:

db.collection.getIndexes()

删除索引

使用 dropIndex() 或 dropIndexes() 方法可以删除索引:

// 删除指定的索引
db.collection.dropIndex( "indexName" )

// 删除所有索引
db.collection.dropIndexes()

索引策略

在创建索引时,需要考虑以下因素:

索引优化

在对索引进行优化时,可以考虑以下方法:

注意事项 索引虽然可以提高查询性能,但也会增加写操作的开销。因此,在创建索引时需要权衡查询性能和写入性能。 索引会占用额外的存储空间,特别是对于大型数据集,需要考虑索引的存储成本。 通过合理地设计和使用索引,可以大大提高 MongoDB 数据库的查询性能和响应速度,从而更好地支持应用程序的需求。

聚合

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

aggregate() 方法

MongoDB中聚合的方法使用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
},
{
   _id: ObjectId(7df78ad8902d)
   title: 'NoSQL Overview', 
   description: 'No sql database is very fast',
   by_user: 'runoob.com',
   url: 'http://www.runoob.com',
   tags: ['mongodb', 'database', 'NoSQL'],
   likes: 10
},
{
   _id: ObjectId(7df78ad8902e)
   title: 'Neo4j Overview', 
   description: 'Neo4j is no sql database',
   by_user: 'Neo4j',
   url: 'http://www.neo4j.com',
   tags: ['neo4j', 'database', 'NoSQL'],
   likes: 750
},

通过以上集合计算每个作者所写的文章数

select by_user, count(*) from mycol group by by_user
> 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
}
>

聚合的表达式

表达式 描述 实例
$sum | 计算总和。 | `db.mycol.aggregate([{$group : {_id : “$by_user", num_tutorial : {$sum :”$likes”}}}])| | $avg | 计算平均值 |db.mycol.aggregate([{$group : {_id : "$by_user”, num_tutorial : {$avg : "$likes”}}}])| | $min | 获取集合中所有文档对应值的最小值。 |db.mycol.aggregate([{$group : {_id : "$by_user”, num_tutorial : {$min : "$likes”}}}])| | $max | 获取集合中所有文档对应值的最大值。 |db.mycol.aggregate([{$group : {_id : "$by_user”, num_tutorial : {$max : "$likes”}}}])| | $push | 将值加入一个数组中,不会判断是否有重复的值。 |db.mycol.aggregate([{$group : {_id : "$by_user”, url : {$push: "$url”}}}])| | $addToSet | 将值加入一个数组中,会判断是否有重复的值。 |db.mycol.aggregate([{$group : {_id : "$by_user”, url : {$addToSet : "$url”}}}])| | $first | 根据资源文档的排序获取第一个文档数据。 |db.mycol.aggregate([{$group : {_id : "$by_user”, first_url : {$first : "$url”}}}])| | $last | 根据资源文档的排序获取最后一个文档数据。 |db.mycol.aggregate([{$group : {_id : "$by_user”, last_url : {$last : "$url”}}}])`

管道的概念

管道在Unix和Linux中一般用于将当前命令的输出结果作为下一个命令的参数。

MongoDB的聚合管道将MongoDB文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的。

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

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

// $project实例
db.article.aggregate(
    { $project : {
        title : 1 ,
        author : 1 ,
    }}
 );
// 这样的话结果中就只还有_id,tilte和author三个字段了,默认情况下_id字段是被包含的,如果要想不包含_id话可以这样:
db.article.aggregate(
    { $project : {
        _id : 0 ,
        title : 1 ,
        author : 1
    }});

// $match实例
db.articles.aggregate( [
                        { $match : { score : { $gt : 70, $lte : 90 } } },
                        { $group: { _id: null, count: { $sum: 1 } } }
                       ] );
// $match用于获取分数大于70小于或等于90记录,然后将符合条件的记录送到下一阶段$group管道操作符进行处理。

// $skip实例
db.article.aggregate(
    { $skip : 5 });
// 经过$skip管道操作符处理后,前五个文档被"过滤"掉。

复制副本集

MongoDB复制是将数据同步在多个服务器的过程。

复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性, 并可以保证数据的安全性。

复制还允许您从硬件故障和服务中断中恢复数据。

什么是复制?

MongoDB 复制原理

mongodb的复制至少需要两个节点。其中一个是主节点,负责处理客户端请求,其余的都是从节点,负责复制主节点上的数据。

mongodb各个节点常见的搭配方式为:一主一从、一主多从。

主节点记录在其上的所有操作oplog,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致。

MongoDB复制结构图如下所示:

MongoDB复制结构图

以上结构图中,客户端从主节点读取数据,在客户端写入数据到主节点时, 主节点与从节点进行数据交互保障数据的一致性。

副本集特征

MongoDB副本集设置

在本教程中我们使用同一个MongoDB来做MongoDB主从的实验, 操作步骤如下:

  1. 关闭正在运行的MongoDB服务器。

现在我们通过指定 –replSet 选项来启动mongoDB。–replSet 基本语法格式如下:

mongod --port "PORT" --dbpath "YOUR_DB_DATA_PATH" --replSet "REPLICA_SET_INSTANCE_NAME"

实例

mongod --port 27017 --dbpath "D:\set up\mongodb\data" --replSet rs0

以上实例会启动一个名为rs0的MongoDB实例,其端口号为27017。

启动后打开命令提示框并连接上mongoDB服务。

在Mongo客户端使用命令rs.initiate()来启动一个新的副本集。

我们可以使用rs.conf()来查看副本集的配置

查看副本集状态使用 rs.status() 命令

副本集添加成员

添加副本集的成员,我们需要使用多台服务器来启动mongo服务。进入Mongo客户端,并使用rs.add()方法来添加副本集的成员。

>rs.add(HOST_NAME:PORT)

假设你已经启动了一个名为mongod1.net,端口号为27017的Mongo服务。 在客户端命令窗口使用rs.add() 命令将其添加到副本集中,命令如下所示:

>rs.add("mongod1.net:27017")
>

MongoDB中你只能通过主节点将Mongo服务添加到副本集中, 判断当前运行的Mongo服务是否为主节点可以使用命令db.isMaster() 。

MongoDB的副本集与我们常见的主从有所不同,主从在主机宕机后所有服务将停止,而副本集在主机宕机后,副本会接管主节点成为主节点,不会出现宕机的情况。

分片

在Mongodb里面存在另一种集群,就是分片技术,可以满足MongoDB数据量大量增长的需求。

当MongoDB存储海量的数据时,一台机器可能不足以存储数据,也可能不足以提供可接受的读写吞吐量。这时,我们就可以通过在多台机器上分割数据,使得数据库系统能存储和处理更多的数据。

为什么使用分片

MongoDB 分片

下图展示了在MongoDB中使用分片集群结构分布:

MongoDB中使用分片集群结构分布

上图主要有如下所述三个主要组件:

分片实例

分片结构端口分布如下:

Shard Server 1:27020
Shard Server 2:27021
Shard Server 3:27022
Shard Server 4:27023
Config Server :27100
Route Process:40000

步骤一:启动Shard Server

[root@100 /]# mkdir -p /www/mongoDB/shard/s0
[root@100 /]# mkdir -p /www/mongoDB/shard/s1
[root@100 /]# mkdir -p /www/mongoDB/shard/s2
[root@100 /]# mkdir -p /www/mongoDB/shard/s3
[root@100 /]# mkdir -p /www/mongoDB/shard/log
[root@100 /]# /usr/local/mongoDB/bin/mongod --port 27020 --dbpath=/www/mongoDB/shard/s0 --logpath=/www/mongoDB/shard/log/s0.log --logappend --fork
....
[root@100 /]# /usr/local/mongoDB/bin/mongod --port 27023 --dbpath=/www/mongoDB/shard/s3 --logpath=/www/mongoDB/shard/log/s3.log --logappend --fork

步骤二:启动Config Server

[root@100 /]# mkdir -p /www/mongoDB/shard/config
[root@100 /]# /usr/local/mongoDB/bin/mongod --port 27100 --dbpath=/www/mongoDB/shard/config --logpath=/www/mongoDB/shard/log/config.log --logappend --fork

注意:这里我们完全可以像启动普通mongodb服务一样启动,不需要添加—shardsvr和configsvr参数。因为这两个参数的作用就是改变启动端口的,所以我们自行指定了端口就可以。

步骤三:启动Route Process

/usr/local/mongoDB/bin/mongos --port 40000 --configdb localhost:27100 --fork --logpath=/www/mongoDB/shard/log/route.log --chunkSize 500

mongos启动参数中,chunkSize这一项是用来指定chunk的大小的,单位是MB,默认大小为200MB.

步骤四:配置Sharding

接下来,我们使用MongoDB Shell登录到mongos,添加Shard节点

#  连接到 MongoDB 的 mongos 路由进程
[root@100 shard]# /usr/local/mongoDB/bin/mongo admin --port 40000
MongoDB shell version: 2.0.7
connecting to: 127.0.0.1:40000/admin
# 添加分片
mongos> db.runCommand({ addshard:"localhost:27020" })
{ "shardAdded" : "shard0000", "ok" : 1 }
......
# 添加更多分片
mongos> db.runCommand({ addshard:"localhost:27029" })
{ "shardAdded" : "shard0009", "ok" : 1 }
# 启用数据库的分片功能 启用 test 数据库的分片功能
mongos> db.runCommand({ enablesharding:"test" }) #设置分片存储的数据库
{ "ok" : 1 }
mongos> db.runCommand({ shardcollection: "test.log", key: { id:1,time:1}})
{ "collectionsharded" : "test.log", "ok" : 1 }

设置集合的分片键

db.runCommand({ shardcollection: "test.log", key: { id: 1, time: 1 } })
# 作用:为 test 数据库中的 log 集合设置分片键,并启用分片。
#     shardcollection:MongoDB 的命令,用于为指定集合启用分片。
# "test.log":指定要分片的集合,格式为 数据库名.集合名。
# key: { id: 1, time: 1 }:
# 指定分片键为 id 和 time,并按升序(1)排序。
# 分片键用于决定数据如何分布到不同的分片中。
# 返回结果:
# "collectionsharded" : "test.log":表示集合 test.log 已成功启用分片。
# "ok" : 1:表示命令执行成功。

步骤五: 程序代码内无需太大更改,直接按照连接普通的mongo数据库那样,将数据库连接接入接口40000

Sharding主从

  1. 创建Sharding复制集 rs0
# mkdir /data/log
# mkdir /data/db1
# nohup mongod --port 27020 --dbpath=/data/db1 --logpath=/data/log/rs0-1.log --logappend --fork --shardsvr --replSet=rs0 &

# mkdir /data/db2
# nohup mongod --port 27021 --dbpath=/data/db2 --logpath=/data/log/rs0-2.log --logappend --fork --shardsvr --replSet=rs0 &
  1. 复制集rs0配置
mongo localhost:27020 > rs.initiate({_id: 'rs0', members: [{_id: 0, host: 'localhost:27020'}, {_id: 1, host: 'localhost:27021'}]}) 
> rs.isMaster() #查看主从关系
  1. 创建Sharding复制集 rs1
# mkdir /data/db3
# nohup mongod --port 27030 --dbpath=/data/db3 --logpath=/data/log/rs1-1.log --logappend --fork --shardsvr --replSet=rs1 &
# mkdir /data/db4
# nohup mongod --port 27031 --dbpath=/data/db4 --logpath=/data/log/rs1-2.log --logappend --fork --shardsvr --replSet=rs1 &
  1. 复制集rs1配置
# mongo localhost:27030
> rs.initiate({_id: 'rs1', members: [{_id: 0, host: 'localhost:27030'}, {_id: 1, host: 'localhost:27031'}]})
> rs.isMaster() #查看主从关系
  1. 创建Config复制集 conf
# mkdir /data/conf1
# nohup mongod --port 27100 --dbpath=/data/conf1 --logpath=/data/log/conf-1.log --logappend --fork --configsvr --replSet=conf &
# mkdir /data/conf2
# nohup mongod --port 27101 --dbpath=/data/conf2 --logpath=/data/log/conf-2.log --logappend --fork --configsvr --replSet=conf &
  1. 复制集conf配置
# mongo localhost:27100
> rs.initiate({_id: 'conf', members: [{_id: 0, host: 'localhost:27100'}, {_id: 1, host: 'localhost:27101'}]})
> rs.isMaster() #查看主从关系
  1. 创建Route
# nohup mongos --port 40000 --configdb conf/localhost:27100,localhost:27101 --fork --logpath=/data/log/route.log --logappend & 
  1. 设置分片
# mongo localhost:40000
> use admin
> db.runCommand({ addshard: 'rs0/localhost:27020,localhost:27021'})
> db.runCommand({ addshard: 'rs1/localhost:27030,localhost:27031'})
> db.runCommand({ enablesharding: 'test'})
> db.runCommand({ shardcollection: 'test.user', key: {name: 1}})

备份 mongodump 与恢复 mongorestore

MongoDB数据备份

在Mongodb中使用mongodump命令来备份MongoDB数据。该命令可以导出所有数据到指定目录中 mongodump命令可以通过参数指定导出的数据量级转存的服务器。

>mongodump -h dbhost -d dbname -o dbdirectory

mongodump命令可选参数列表如下所示

语法 描述 实例
mongodump --host HOST_NAME --port PORT_NUMBER 该命令将备份所有 MongoDB 数据 mongodump --host runoob.com --port 27017
mongodump --dbpath DB_PATH --out BACKUP_DIRECTORY 备份指定数据路径到目标目录 mongodump --dbpath /data/db/ --out /data/backup/
mongodump --collection COLLECTION --db DB_NAME 该命令将备份指定数据库的集合 mongodump --collection mycol --db test

MongoDB数据恢复

mongodb使用 mongorestore 命令来恢复备份的数据。

>mongorestore -h <hostname><:port> -d dbname <path>

监控

在你已经安装部署并允许MongoDB服务后,你必须要了解MongoDB的运行情况,并查看MongoDB的性能。这样在大流量得情况下可以很好的应对并保证MongoDB正常运作。

MongoDB中提供了mongostat 和 mongotop 两个命令来监控MongoDB的运行情况。

mongostat 命令

mongostat是mongodb自带的状态检测工具,在命令行下使用。它会间隔固定时间获取mongodb的当前运行状态,并输出。如果你发现数据库突然变慢或者有其他问题的话,你第一手的操作就考虑采用mongostat来查看mongo的状态。

root@0b390d7bbbf5:/# mongostat --username admin --password password --authenticationDatabase admin
2025-03-29T12:03:23.964+0000    WARNING: On some systems, a password provided directly using --password may be visible to system status programs such as `ps` that may be invoked by other users. Consider omitting the password to provide it via stdin, or using the --config option to specify a configuration file with the password.
insert query update delete getmore command dirty used flushes vsize  res qrw arw net_in net_out conn                time
    *0    *0     *0     *0       0     1|0  0.0% 0.0%       0 2.56G 101M 0|0 0|0   112b   85.3k   13 Mar 29 12:03:26.064
    *0    *0     *0     *0       0     0|0  0.0% 0.0%       0 2.56G 101M 0|0 0|0   111b   84.3k   13 Mar 29 12:03:27.067
    *0    *0     *0     *0       0     3|0  0.0% 0.0%       0 2.56G 101M 0|0 0|0   299b   85.6k   13 Mar 29 12:03:28.063
    *0    *0     *0     *0       0     0|0  0.0% 0.0%       0 2.56G 101M 0|0 0|0   111b   84.3k   13 Mar 29 12:03:29.066
^C2025-03-29T12:03:29.710+0000  signal 'interrupt' received; forcefully terminating

mongotop 命令

mongotop也是mongodb下的一个内置工具,mongotop提供了一个方法,用来跟踪一个MongoDB的实例,查看哪些大量的时间花费在读取和写入数据。 mongotop提供每个集合的水平的统计数据。默认情况下,mongotop返回值的每一秒。

mongotop [options] [interval]
interval:指定统计信息的刷新间隔(以秒为单位)。默认值为 1 秒。
--host 指定要监控的 MongoDB 实例的主机名(默认是 localhost)。
--port 指定要监控的 MongoDB 实例的端口号(默认是 27017)。
--username 指定用于身份验证的用户名。
--password 指定用于身份验证的密码。
--authenticationDatabase 指定用于身份验证的数据库(默认是 admin)。
--ssl 使用 SSL 连接到 MongoDB 实例。
--rowcount 指定输出的行数,达到行数后停止(默认是无限输出)。

输出字段说明

root@0b390d7bbbf5:/# mongotop --username admin --password password --authenticationDatabase admin
2025-03-29T12:07:28.875+0000    WARNING: On some systems, a password provided directly using --password may be visible to system status programs such as `ps` that may be invoked by other users. Consider omitting the password to provide it via stdin, or using the --config option to specify a configuration file with the password.
2025-03-29T12:07:28.896+0000    connected to: mongodb://localhost/

                    ns    total    read    write    2025-03-29T12:07:29Z
        admin.atlascli      0ms     0ms      0ms                        
         admin.sources      0ms     0ms      0ms                        
    admin.system.users      0ms     0ms      0ms                        
  admin.system.version      0ms     0ms      0ms                        
config.system.sessions      0ms     0ms      0ms                        
   config.transactions      0ms     0ms      0ms                        
  local.system.replset      0ms     0ms      0ms                        
   myDB.collectionName      0ms     0ms      0ms                        
     myDB.myCollection      0ms     0ms      0ms                        
     testDB.collection      0ms     0ms      0ms             

Node.js MongoDB

在 Node.js 中使用 MongoDB,通常通过官方提供的 mongodb 驱动来与 MongoDB 数据库交互。以下是使用 MongoDB 的基本步骤和示例代码。

安装 MongoDB 驱动

在项目中安装官方的 MongoDB 驱动:

npm install mongodb

连接到 MongoDB

使用 MongoClient 连接到 MongoDB 数据库。

const { MongoClient } = require('mongodb');

// MongoDB 连接 URI
const uri = "mongodb://localhost:27017";

// 创建 MongoClient 实例
const client = new MongoClient(uri);

async function connectToDatabase() {
    try {
        // 连接到 MongoDB
        await client.connect();
        console.log("Connected to MongoDB!");

        // 选择数据库
        const db = client.db("test"); // 替换为你的数据库名称

        // 选择集合
        const collection = db.collection("users"); // 替换为你的集合名称

        // 示例操作:插入文档
        const result = await collection.insertOne({ name: "Alice", age: 25 });
        console.log("Inserted document:", result.insertedId);

    } catch (error) {
        console.error("Error connecting to MongoDB:", error);
    } finally {
        // 关闭连接
        await client.close();
    }
}

connectToDatabase();

常见操作

// 插入文档
const result = await collection.insertOne({ name: "Bob", age: 30 });
console.log("Inserted document ID:", result.insertedId);
// 查询文档
// 查询单个文档
const user = await collection.findOne({ name: "Alice" });
console.log("Found user:", user);

// 查询多个文档
const users = await collection.find({ age: { $gte: 25 } }).toArray();
console.log("Found users:", users);
// 更新文档
const updateResult = await collection.updateOne(
    { name: "Alice" }, // 查询条件
    { $set: { age: 26 } } // 更新操作
);
console.log("Updated documents count:", updateResult.modifiedCount);
// 删除文档
const deleteResult = await collection.deleteOne({ name: "Bob" });
console.log("Deleted documents count:", deleteResult.deletedCount);

使用环境变量管理连接信息

为了安全性,建议将 MongoDB 的连接信息(如用户名、密码、URI)存储在环境变量中。

示例

创建 .env 文件:

MONGO_URI=mongodb://localhost:27017

使用 dotenv 加载环境变量:

npm install dotenv
require('dotenv').config();
const uri = process.env.MONGO_URI;
const client = new MongoClient(uri);

使用 Mongoose(高级 ODM 库)

如果需要更高级的功能(如模式验证、模型定义等),可以使用 Mongoose。

npm install mongoose
const mongoose = require('mongoose');

// 连接到 MongoDB
mongoose.connect('mongodb://localhost:27017/test', { useNewUrlParser: true, useUnifiedTopology: true })
    .then(() => console.log("Connected to MongoDB!"))
    .catch(err => console.error("Connection error:", err));

// 定义 Schema 和 Model
const userSchema = new mongoose.Schema({
    name: String,
    age: Number
});

const User = mongoose.model('User', userSchema);

// 示例操作
async function run() {
    // 插入文档
    const user = new User({ name: "Charlie", age: 28 });
    await user.save();
    console.log("User saved:", user);

    // 查询文档
    const users = await User.find({ age: { $gte: 25 } });
    console.log("Found users:", users);
}

run();

关系

MongoDb的关系表示多个文档之间在逻辑上的相互联系。

文档间可以通过嵌入和引用来建立联系。

MongoDB中的关系可以是:

一个用户可以有多个地址,所以是一对多的关系

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"
} 

嵌入式关系

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

{
   "_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})

这种数据结构的缺点是,如果用户和用户地址在不断增加,数据量不断变大,会影响读写性能。

引用式关系

引用式关系是设计数据库时经常用到的方法,这种方法把用户数据文档和用户地址数据文档分开,通过引用文档的 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引用有两种

DBRefs vs 手动引用

考虑这样的一个场景,我们在不同的集合中 (address_home, address_office, address_mailing, 等)存储不同的地址(住址,办公室地址,邮件地址等)。

这样,我们在调用不同地址时,也需要指定集合,一个文档从多个集合引用文档,我们应该使用 DBRefs。

使用DBRefs

DBRefs的形式

{ $ref: , $id: , $db: }
{
   "_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)})

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

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

覆盖索引查询

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

由于所有出现在查询中的字段是索引的一部分, MongoDB 无需在整个数据文档中检索匹配查询条件和返回使用相同索引的查询结果。

因为索引存在于RAM中,从索引中获取数据比通过扫描文档读取数据要快得多。

覆盖索引查询(Covered Query)是指 MongoDB 在查询时,只使用索引就能满足查询条件和返回结果,而无需访问实际的文档数据。这种查询方式可以显著提高查询性能,因为索引通常存储在内存中,查询索引比扫描整个文档更快。

覆盖索引查询的条件

  1. 查询条件中的字段必须是索引的一部分:
  1. 返回的字段也必须在索引中:
  1. 查询中不能返回 _id 字段,除非_id 是索引的一部分:

使用覆盖索引查询

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

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

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

>db.users.createIndex({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})

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

{
   "_id": ObjectId("53402597d852426020000002"),
   "tags": ["tag1", "tag2", "tag3"]
}

即使为 tags 创建了索引,以下查询也无法使用覆盖索引:

db.users.find({ tags: "tag1" }, { tags: 1, _id: 0 })

原因是数组字段的索引存储方式不同,无法直接覆盖查询。

{
   "_id": ObjectId("53402597d852426020000002"),
   "address": { city: "New York", zip: "10001" }
}

即使为 address.city 创建了索引,以下查询也无法使用覆盖索引:

db.users.find({ "address.city": "New York" }, { "address.city": 1, _id: 0 })

查询分析

MongoDB查询分析可以确保我们所建立的索引是否有效,是查询语句性能分析的重要工具。

MongoDB查询分析常用函数有:explain() 和 hint()

使用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()
{
   "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
            }
         ]
      ]
   }
}

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

使用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,不会出现查询到的文档没有保存完整的情况。

原子操作数据模型

考虑下面的例子,图书馆的书籍及结账信息。

实例说明了在一个相同的文档中如何确保嵌入字段关联原子操作(update:更新)的字段是同步的。

book = {
          _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.findAndModify() 方法来判断书籍是否可结算并更新新的结算信息。

在同一个文档中嵌入的 available 和 checkout 字段来确保这些字段是同步更新的:

db.books.findAndModify ( {
   query: {
            _id: 123456789,
            available: { $gt: 0 }
          },
   update: {
             $inc: { available: -1 },
             $push: { checkout: { by: "abc", date: new Date() } }
           }
} )

原子操作常用命令

{ $set : { field : value } }
{ $unset : { field : 1} }
{ $inc : { field : value } }

把value追加到field里面去,field一定要是数组类型才行,如果field不存在,会新增一个数组类型加进去。

{ $push : { field : value } }
{ $pushAll : { field : value_array } }
{ $pull : { field : _value } }
{ $addToSet : { field : value } }
{ $pop : { field : 1 } }
{ $rename : { old_field_name : new_field_name } }
{$bit : { field : {and : 5}}}

对数组内的对象字段操作

> t.find() { "_id" : ObjectId("4b97e62bf1d8c7152c9ccb74"), "title" : "ABC", "comments" : [ { "by" : "joe", "votes" : 3 }, { "by" : "jane", "votes" : 7 } ] }
 
> t.update( {'comments.by':'joe'}, {$inc:{'comments.$.votes':1}}, false, true )
 
> t.find() { "_id" : ObjectId("4b97e62bf1d8c7152c9ccb74"), "title" : "ABC", "comments" : [ { "by" : "joe", "votes" : 4 }, { "by" : "jane", "votes" : 7 } ] }

高级索引

考虑以下文档集合 users

{
   "address": {
      "city": "Los Angeles",
      "state": "California",
      "pincode": "123"
   },
   "tags": [
      "music",
      "cricket",
      "blogs"
   ],
   "name": "Tom Benzamin"
}

以上文档包含了address子文档和tags数组。

索引数组字段

假设我们基于标签来检索用户,为此我们需要对集合中的数组 tags 建立索引。

在数组中创建索引,需要对数组中的每个字段依次建立索引。所以在我们为数组 tags 创建索引时,会为 music、cricket、blogs三个值建立单独的索引。

使用以下命令创建数组索引:

>db.users.ensureIndex({"tags":1})

创建索引后,我们可以这样检索集合的tags字段:

>db.users.find({tags:"cricket"})

为了检验我们使用使用了索引,可以使用explain命令:

>db.users.find({tags:"cricket"}).explain()

以上命令执行结果中会显示 “cursor” : “BtreeCursor tags_1” ,则表示已经使用了索引。

索引子文档字段

假设我们需要通过city、state、pincode字段来检索文档,由于这些字段是子文档的字段,所以我们需要对子文档建立索引。

为子文档的三个字段创建索引,命令如下:

>db.users.ensureIndex({"address.city":1,"address.state":1,"address.pincode":1})

一旦创建索引,我们可以使用子文档的字段来检索数据

>db.users.find({"address.city":"Los Angeles"})   

查询表达不一定遵循指定的索引的顺序,mongodb会自动优化,所以上面创建的索引支持以下查询

>db.users.find({"address.state":"California","address.city":"Los Angeles"}) 

同样支持以下查询

>db.users.find({"address.city":"Los Angeles","address.state":"California","address.pincode":"123"})

索引限制

额外开销

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

内容RAM使用

由于索引是存储在内存(RAM)中,你应该确保该索引的大小不超过内存的限制。

如果索引的大小大于内存的限制,MongoDB会删除一些索引,这将导致性能下降。

查询限制

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

db.users.createIndex({ name: 1 }); // 为 name 字段创建索引
db.users.find({ name: { $regex: /^A/ } }); // 查询以 "A" 开头的 name
db.users.find({ age: { $nin: [25, 30] } }); // 查询 age 不等于 25 或 30 的文档
db.users.find({ age: { $not: { $gt: 25 } } }); // 查询 age 不大于 25 的文档
db.users.createIndex({ age: 1 }); // 为 age 字段创建索引
db.users.find({ age: { $mod: [5, 0] } }); // 查询 age 能被 5 整除的文档
db.users.createIndex({ age: 1 }); // 为 age 字段创建索引
db.users.find({ $where: "this.age > 25" }); // 查询 age 大于 25 的文档

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

索引键限制

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

最大大小限制:

  1. 索引键的值不能超过 1024 字节(1KB)。
  2. 这个限制适用于单个索引键的值,而不是整个文档的大小。

影响范围:

  1. 如果字段的值是字符串、数组或其他类型,MongoDB 会检查该字段的值是否超过 1024 字节。
  2. 如果字段是数组,MongoDB 会检查数组中每个元素的大小。

行为:

  1. 如果某个字段的值超过了 1024 字节,MongoDB 将拒绝为该字段创建索引,并抛出错误。

插入文档超过索引键限制

如果文档的索引字段值超过了索引键的限制,MongoDB不会将任何文档转换成索引的集合。与mongorestore和mongoimport工具类似。

最大范围

Object Id

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

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

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

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

创建新的ObjectId

使用以下代码生成新的ObjectId:

>newObjectId = ObjectId()

上面的语句返回以下唯一生成的id:

ObjectId("5349b4ddd2781d08c09890f3")

你也可以使用生成的id来取代MongoDB自动生成的ObjectId

>myObjectId = ObjectId("5349b4ddd2781d08c09890f4")

创建文件的事件戳

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

>ObjectId("5349b4ddd2781d08c09890f4").getTimestamp()

以上代码将返回ISO格式的文档创建时间

ISODate("2014-04-12T21:49:17Z")

ObjectId 转换为字符串

在某些情况下,您可能需要将ObjectId转换为字符串格式。你可以使用下面的代码:

>new ObjectId().str

以上代码将返回Guid格式的字符串

5349b4ddd2781d08c09890f3

Map Reduce

Map-Reduce是一种计算模型,简单的说就是将大批量的工作(数据)分解(MAP)执行,然后再将结果合并成最终结果(REDUCE)。

MongoDB提供的Map-Reduce非常灵活,对于大规模数据分析也相当实用。

MapReduce命令

以下是MapReduce的基本语法:

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

使用 MapReduce 要实现两个函数 Map 函数和 Reduce 函数,Map 函数调用 emit(key, value), 遍历 collection 中所有的记录, 将 key 与 value 传递给 Reduce 函数进行处理。

Map 函数必须调用 emit(key, value) 返回键值对。

参数说明:

使用MapReduce

考虑以下文档结构存储用户的文章,文档存储了用户的 user_name 和文章的 status 字段:

>db.posts.insert({
   "post_text": "菜鸟教程,最全的技术文档。",
   "user_name": "mark",
   "status":"active"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
   "post_text": "菜鸟教程,最全的技术文档。",
   "user_name": "mark",
   "status":"active"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
   "post_text": "菜鸟教程,最全的技术文档。",
   "user_name": "mark",
   "status":"active"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
   "post_text": "菜鸟教程,最全的技术文档。",
   "user_name": "mark",
   "status":"active"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
   "post_text": "菜鸟教程,最全的技术文档。",
   "user_name": "mark",
   "status":"disabled"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
   "post_text": "菜鸟教程,最全的技术文档。",
   "user_name": "runoob",
   "status":"disabled"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
   "post_text": "菜鸟教程,最全的技术文档。",
   "user_name": "runoob",
   "status":"disabled"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
   "post_text": "菜鸟教程,最全的技术文档。",
   "user_name": "runoob",
   "status":"active"
})
WriteResult({ "nInserted" : 1 })

现在,我们将在 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" 
      }
)

以上 mapReduce 输出结果为:

{
        "result" : "post_total",
        "timeMillis" : 23,
        "counts" : {
                "input" : 5,
                "emit" : 5,
                "reduce" : 1,
                "output" : 2
        },
        "ok" : 1
}

结果表明,共有 5 个符合查询条件(status:“active”)的文档, 在map函数中生成了 5 个键值对文档,最后使用reduce函数将相同的键值分为 2 组。

具体参数说明:

使用 find 操作符来查看 mapReduce 的查询结果:

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

以上查询显示如下结果:

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

用类似的方式,MapReduce可以被用来构建大型复杂的聚合查询。

Map函数和Reduce函数可以使用 JavaScript 来实现,使得MapReduce的使用非常灵活和强大。

全文检索

全文检索对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户的检索方式。

这个过程类似于通过字典中的检索字表查字的过程。

MongoDB 从 2.4 版本开始支持全文检索,目前支持15种语言的全文索引。

danish
dutch
english
finnish
french
german
hungarian
italian
norwegian
portuguese
romanian
russian
spanish
swedish
turkish

启用全文检索

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

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

或者使用命令:

mongod --setParameter textSearchEnabled=true

创建全文检索

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

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

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

>db.posts.ensureIndex({post_text:"text"})

使用全文索引

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

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

以下命令返回了如下包含 runoob 关键词的文档数据:

{ 
   "_id" : ObjectId("53493d14d852429c10000002"), 
   "post_text" : "enjoy the mongodb articles on Runoob", 
   "tags" : [ "mongodb", "runoob" ]
}

使用全文索引可以提高搜索效率。

删除全文检索

删除已存在的全文索引,可以使用 find 命令查找索引名:

>db.posts.getIndexes()

通过以上命令获取索引名,本例的索引名为post_text_text,执行以下命令来删除索引:

>db.posts.dropIndex("post_text_text")

正则表达式

正则表达式是使用单个字符串来描述、匹配一系列符合某个句法规则的字符串。

许多程序设计语言都支持利用正则表达式进行字符串操作。

MongoDB 使用 $regex 操作符来设置匹配字符串的正则表达式。

MongoDB使用PCRE (Perl Compatible Regular Expression) 作为正则表达式语言。

不同于全文检索,我们使用正则表达式不需要做任何配置。

考虑以下 posts 集合的文档结构,该文档包含了文章内容和标签:

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

使用正则表达式

以下命令使用正则表达式查找包含 runoob 字符串的文章:

>db.posts.find({post_text:{$regex:"runoob"}})

以上查询也可以写为

>db.posts.find({post_text:/runoob/})

不区分大小写的正则表达式

如果检索需要不区分大小写,我们可以设置 $options 为 $i。

以下命令将查找不区分大小写的字符串 runoob:

>db.posts.find({post_text:{$regex:"runoob",$options:"$i"}})

集合中会返回所有包含字符串 runoob 的数据,且不区分大小写:

{
   "_id" : ObjectId("53493d37d852429c10000004"),
   "post_text" : "hey! this is my post on  runoob", 
   "tags" : [ "runoob" ]
} 

数组元素使用正则表达式

我们还可以在数组字段中使用正则表达式来查找内容。 这在标签的实现上非常有用,如果你需要查找包含以 run 开头的标签数据(ru 或 run 或 runoob), 你可以使用以下代码:

>db.posts.find({tags:{$regex:"run"}})

优化正则表达式查询

如果你的文档中字段设置了索引,那么使用索引相比于正则表达式匹配查找所有的数据查询速度更快。

如果正则表达式是前缀表达式,所有匹配的数据将以指定的前缀字符串为开始。例如: 如果正则表达式为 ^tut ,查询语句将查找以 tut 为开头的字符串。

这里面使用正则表达式有两点需要注意:

正则表达式中使用变量。一定要使用eval将组合的字符串进行转换,不能直接将字符串拼接后传入给表达式。否则没有报错信息,只是结果为空!实例如下:

var name=eval("/" + 变量值key +"/i"); 

以下是模糊查询包含title关键词, 且不区分大小写:

title:eval("/"+title+"/i")    // 等同于 title:{$regex:title,$Option:"$i"}   

管理工具

RockMongo是PHP5写的一个MongoDB管理工具。

通过 Rockmongo 你可以管理 MongoDB服务,数据库,集合,文档,索引等等。

它提供了非常人性化的操作。类似 phpMyAdmin(PHP开发的MySql管理工具)。

Rockmongo 下载地址:https://github.com/iwind/rockmongo

Navicat for MongoDB也是一个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集合中。

以下是简单的 fs.files 集合文档:

{
   "filename": "test.txt",
   "chunkSize": NumberInt(261120),
   "uploadDate": ISODate("2014-04-13T11:32:33.557Z"),
   "md5": "7b762939321e146569b07f72c62cca4f",
   "length": NumberInt(646)
}

以下是简单的 fs.chunks 集合文档:

{
   "files_id": ObjectId("534a75d19f54bfec8a2fe44b"),
   "n": NumberInt(0),
   "data": "Mongo Binary Data"
}

GridFS 添加文件

现在我们使用 GridFS 的 put 命令来存储 mp3 文件。 调用 MongoDB 安装目录下bin的 mongofiles工具。

>mongofiles.exe -d gridfs put song.mp3

-d gridfs 指定存储文件的数据库名称,如果不存在该数据库,MongoDB会自动创建。如果不存在该数据库,MongoDB会自动创建。Song.mp3 是音频文件名。

使用以下命令来查看数据库中文件的文档:

>db.fs.files.find()

以上命令执行后返回以下文档数据:

{
   _id: ObjectId('534a811bf8b4aa4d33fdf94d'), 
   filename: "song.mp3", 
   chunkSize: 261120, 
   uploadDate: new Date(1397391643474), md5: "e4f53379c909f7bed2e9d631e15c1c41",
   length: 10401959 
}

我们可以看到 fs.chunks 集合中所有的区块,以下我们得到了文件的 _id 值,我们可以根据这个_id 获取区块(chunk)的数据:

>db.fs.chunks.find({files_id:ObjectId('534a811bf8b4aa4d33fdf94d')})

以上实例中,查询返回了 40 个文档的数据,意味着mp3文件被存储在40个区块中。

固定集合

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

创建固定集合

我们通过createCollection来创建一个固定集合,且capped选项设置为true:

>db.createCollection("cappedLogCollection",{capped:true,size:10000})

还可以指定文档个数,加上max:1000属性:

>db.createCollection("cappedLogCollection",{capped:true,size:10000,max:1000})

判断集合是否为固定集合:

>db.cappedLogCollection.isCapped()

如果需要将已存在的集合转换为固定集合可以使用以下命令:

>db.runCommand({"convertToCapped":"posts",size:10000})

以上代码将我们已存在的 posts 集合转换为固定集合。

固定集合查询

固定集合文档按照插入顺序储存的,默认情况下查询就是按照插入顺序返回的,也可以使用$natural调整返回顺序。

>db.cappedLogCollection.find().sort({$natural:-1})

固定集合的功能特点

可以插入及更新,但更新不能超出collection的大小,否则更新失败,不允许删除,但是可以调用drop()删除集合中的所有行,但是drop后需要显式地重建集合。

在32位机子上一个cappped collection的最大值约为482.5M,64位上只受系统文件大小的限制。

固定集合属性及用法

属性

属性1:对固定集合进行插入速度极快 属性2:按照插入顺序的查询输出速度极快 属性3:能够在插入最新数据时,淘汰最早的数据

用法

用法1:储存日志信息 用法2:缓存一些少量的文档

自动增长

MongoDB 没有像 SQL 一样有自动增长的功能, MongoDB 的 _id 是系统自动生成的12字节唯一标识。

但在某些情况下,我们可能需要实现 ObjectId 自动增长功能。

由于 MongoDB 没有实现这个功能,我们可以通过编程的方式来实现,以下我们将在 counters 集合中实现_id字段自动增长。

使用counter集合

考虑以下 products 文档。我们希望 _id 字段实现 从 1,2,3,4 到 n 的自动增长功能。

{
  "_id":1,
  "product_name": "Apple iPhone",
  "category": "mobiles"
}

为此,创建 counters 集合,序列字段值可以实现自动长:

>db.createCollection("counters")

现在我们向 counters 集合中插入以下文档,使用 productid 作为 key:

{
  "_id":"productid",
  "sequence_value": 0
}

sequence_value 字段是序列通过自动增长后的一个值。

使用以下命令插入 counters 集合的序列文档中:

>db.counters.insert({_id:"productid",sequence_value:0})

创建Javascript函数

现在,我们创建函数 getNextSequenceValue 来作为序列名的输入, 指定的序列会自动增长 1 并返回最新序列值。在本文的实例中序列名为 productid 。

>function getNextSequenceValue(sequenceName){
   var sequenceDocument = db.counters.findAndModify(
      {
         query:{_id: sequenceName },
         update: {$inc:{sequence_value:1}},
         "new":true
      });
   return sequenceDocument.sequence_value;
}

使用 Javascript 函数

接下来我们将使用 getNextSequenceValue 函数创建一个新的文档, 并设置文档 _id 自动为返回的序列值:

>db.products.insert({
   "_id":getNextSequenceValue("productid"),
   "product_name":"Apple iPhone",
   "category":"mobiles"})

>db.products.insert({
   "_id":getNextSequenceValue("productid"),
   "product_name":"Samsung S3",
   "category":"mobiles"})

就如你所看到的,我们使用 getNextSequenceValue 函数来设置 _id 字段。

为了验证函数是否有效,我们可以使用以下命令读取文档:

>db.products.find()

以上命令将返回以下结果,我们发现 _id 字段是自增长的:

{ "_id" : 1, "product_name" : "Apple iPhone", "category" : "mobiles"}

{ "_id" : 2, "product_name" : "Samsung S3", "category" : "mobiles" }