MongoDB - 命令篇(基础操作-新增修改删除查询、语法说明、聚合通道查询、MapReduce)

1,445 阅读14分钟

1. 基本操作

1.1 创建数据库

语法:如果数据库不存在,则创建数据库,否则切换到指定数据库。

use DATABASE_NAME
> use hero
switched to db hero

> db
hero

如果你想查看所有数据库,可以使用 show dbs 命令:

show dbs

可以看到,我们刚创建的数据库 hero并不在数据库的列表中, 要显示它,我们需向 hero 数据库插入一些数据。

> db.hero.insert({name:'hero',age:18,country:'china'})
WriteResult({ "nInserted" 1 })

> show dbs
admin 0.000GB
config 0.000GB
hero 0.000GB
local 0.000GB

1.2 删除数据库

语法:删除当前数据库,默认为 test,你可以使用 db 命令查看当前数据库名。

db.dropDatabase()

举个栗子:以下实例我们删除了数据库 hero。

> db.dropDatabase()
{ "dropped" : "hero""ok" : 1 }

通过 show dbs 命令数据库是否删除成功

> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB

1.3 创建集合

语法:使用 createCollection() 方法来创建集合。

db.createCollection(name, options)

参数说明:

  • name: 要创建的集合名称
  • options: 可选参数, 指定有关内存大小及索引的选项,可以是如下参数:
字段类型描述
capped(可选)布尔如果为 true,则创建固定集合。固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档,当该值为 true 时,必须指定size 参数。
autoIndexId(可选)布尔如为 true,自动在_id字段创建索引。默为 false。
size(可选)数值为固定集合指定一个最大值(以字节计)。
max(可选)数值为固定集合包含文档的最大数量
  • 在插入文档时,MongoDB 首先检查固定集合的 size 字段,然后检查 max 字段。

举个栗子:在 hero数据库中创建 mycollection1和mycollection2集合

> use hero
switched to db hero

> db.createCollection("mycollection1")
{ "ok" : 1 }

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

如果要查看已有集合,可以使用 show collections 或 show tables命令:

> show collections

mycollection1
mycollection2

1.4 删除集合

集合删除语法格式如下:

db.collection_name.drop()

举个栗子:删除 hero 数据库中的集合 mycollection1:

> db.mycollection1.drop()
true

2. 集合数据操作

MongoDB将所有文档存储在集合中。集合是具有一组共享公共索引的相关文档。集合类似于关系数据库中的表.

2.1 数据添加

2.1.1 插入单条数据

文档的数据结构和JSON基本一样。所有存储在集合中的数据都是BSON格式BSON是一种类JSON的一种二进制形式的存储格式。

db.collection.insertOne(文档)

举个栗子:这个操作会给文档增加一个"_id",然后将文档保存在MongoDB中。

db.users.insertOne(
{name"hero",age18,status"PP"}
)

验证:db.users.find()

2.1.2 插入多条数据

在大数据环境下,往往文档数据的插入是成千上万条的,如果直接使用之前学习的insert插入,每次插入都是一次TCP请求,成千上万的文档插入就意味着成千上万次的TCP请求,每一次请求都需要携带消息头,当数据量比较多的时候,消息头是非常大的,会影响数据最终的落地速度。

所以在插入成千上万的文档的时候,建议使用批量插入,一次批量插入只需要申请一次TCP请求,这样就避免了很多的零碎的请求开销,加快了数据落地速度。

db.集合名.insert([文档,文档])

举个例子

db.users.insertMany(
    [

        { name"benson", age42, status"AA", },
        { name"yilia", age22, status"AA", },
        { name"vincent", age34, status"DD", }
    ]
)

2.2 数据查询

2.2.1 语法说明

① 比较条件查询语法

db.集合名.find(条件)

操作条件格式例子RDBMS中的条件
等于{key: value}db.集合名.find({字段名:值}).pretty()where 字段名= 值
大于{key: $gt:value}db.集合名.find({字段名:{$gt:值}}).pretty()where 字段名> 值
小于{key: $lt:value}db.集合名.find({字段名:{$lt:值}}).pretty()where 字段名< 值
大于等于{key: $gte:value}db.集合名.find({字段名:{$gte:值}}).pretty()where 字段名>= 值
小于等于{key: $lte:value}db.集合名.find({字段名:{$lte:值}}).pretty()where 字段名<= 值
不等于{key: $ne:value}db.集合名.find({字段名:{$ne:值}}).pretty()where 字段名!= 值

② 逻辑条件查询语法

AND条件 db.集合名.find({key1:value1, key2:value2}).pretty()

OR条件 db.集合名.find({$or:[{key1:value1}, {key2:value2}]}).pretty()

not 条件 db.集合名.find({key:{$not:{$操作符:value}}).pretty()

③ 分页查询语法

db.集合名.find({条件}).sort({排序字段:排序方式})).skip(跳过的行数).limit(一页显示多少数据)

2.2.2 常见查询案例

初始化测试数据

use hero
db.goods.insertMany([
{ item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A"},
{ item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status:"A" },
{ item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D"},
{ item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status:"D" },
{ item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status:"A" },
{ item: "postcard", qty: 55, size: { h: 10, w: 15.25, uom: "cm" }, status:"C" }
]);

1.查询所有数据

db.goods.find()

2.按条件查询

db.goods.find({status: 'D'});

3.$in

# 如果我们想查询status带有C的 或者 带有D的:
db.goods.find(
	{status: { $in:['C','D']}}
);

4.and

# 想查询status:A 并且qty<30的
db.goods.find(
	{status:'A', qty: {$lt: 30}}
);

5.or

# 假如我们想查询status:A 或者 qty<30的数据
db.goods.find(
	{ $or : [{status:'A'}, {qty: {$lt: 30}}]}
);

6.多条件组合

# 查询:status=A并且(qty < 30 或者item中以p为开头的)
db.goods.find({
	status:'A', 
	$or : [{qty: {$lt:30}},{item: /^p/}]
});

7.嵌套查询

# 例子1:查询size: { h: 14, w: 21, uom: "cm" }这一条数据
db.goods.find(
  { size: { h: 14, w: 21, uom: "cm" }}
);

# 例子2:带点符号的嵌套查询
db.goods.find(
	{"size.uom": "in"}
)

8.查询数组

# 插入数据
db.goods_arr.insertMany([
{ item: "journal", qty: 25, tags: ["blank", "red"], dim_cm: [ 14, 21 ] },
{ item: "notebook", qty: 50, tags: ["red", "blank"], dim_cm: [ 14, 21 ] },
{ item: "paper", qty: 100, tags: ["red", "blank", "plain"], dim_cm: [ 14,
21 ] },
{ item: "planner", qty: 75, tags: ["blank", "red"], dim_cm: [ 22.85, 30 ]
},
{ item: "postcard", qty: 45, tags: ["blue"], dim_cm: [ 10, 15.25 ] }
]);
  • 查询字段tags 值是包含两个元素"blank", "red" 的数组的所有文档(顺序必须一致)
db.goods_arr.find(
	{tags: ["blank","red"]}
)
  • 查询字段tags 值是包含两个元素"blank", "red" 的数组的所有文档(不考虑顺序)
db.goods_arr.find(
	{tags: {$all:["blank","red"]}}
)
  • 查询所有doc中dim_cm数组的第二个参数大于25的所有文档
db.goods_arr.find(
	{"dim_cm.1": {$gt:25}}
)
  • 查询tags数组长度等于3的所有文档
db.goods_arr.find(
	{tags: {$size: 3}}
)

9.查询null或者丢失的字段

# 插入数据
db.goods_null.insertMany([
    { _id: 1, item: null },
    { _id: 2 }
]);

db.goods_null.find(
	{"item": null}
)

2.3 数据更新

2.3.1 语法说明:

db.集合名.update(
	<query>,
	<update>,
	{
		upsert: <boolean>,
		multi: <boolean>,
		writeConcern: <document>
	}
)

参数解释:

  1. query : update的查询条件,类似sql update查询内where后面的。
  2. update : update的对象和一些更新的操作符(如 set , set , inc ...)等,也可以理解为sql update中的set部分
    • $set: 设置字段值
    • $unset: 删除字段值
    • $inc: 对修改的值进行自增
    • 其他操作符在运算符与修饰符详解 部分介绍
  3. upsert : 可选,含义是如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。
  4. multi : 可选,默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。
  5. writeConcern :可选,用来指定对写操作的回执行为,比如:写的行为是否需要确认。
    • 包括以下字段:{ w: <value>, j:<boolean>, wtimeout:<number> }
    • w: 指定写操作传播到的成员数量,比如:
      • w = 1: 默认值,要求得到写操作已经传播到独立的Mongod实例或副本集的primary成员的确认
      • w = 0: 不要求确认写操作,可能会返回socket exceptions和 networking errors
      • w = "majority": 要求得到写操作已经传播到大多数具有存储数据具有投票的成员的确认
    • j: 要求得到MongoDB的写操作已经写到硬盘日志的确认,比如:j=true要求得到MongoDB的写操作已经写到硬盘日志的确认(w指定的实例的个数),不保证因为副本集故障而不会回滚。
    • wtimeout:指定write concern的时间限制,只适用于w > 1的情况。
      • 在超过指定时间后写操作会返回error,即使写操作最后执行成功,当这些写操作返回时,MongoDB不会撤消在wtimeout时间限制之前执行成功的数据修改。
      • 如果未指定wtimeout选项且未指定write concern级别,则写入操作将无限期阻止。
      • 指定wtimeout值为0等同于没有wtimeout选项。

其他方法:

方法描述
db.collection.updateOne()即使可能有多个文档通过过滤条件匹配到,但是也最多也只更新一个文档。
db.collection.updateMany()更新所有通过过滤条件匹配到的文档。
db.collection.replaceOne()即使可能有多个文档通过过滤条件匹配到,但是也最多也只替换一个文档。
db.collection.update()即使可能有多个文档通过过滤条件匹配到,但是也最多也只更新或者替换一个文档。 要更新多个文档,请使用 multi 选项。

注意:

  • 原子性:MongoDB中所有的写操作在单一文档层级上是原子操作
  • _id字段:一旦设定不能更新 _id 字段的值,也不能用有不同 _id 字段值的文档来替换已经存在的文档。

初始化测试数据

db.users.insertMany(
[
{_id: 7,name: "benson",age: 19,type: 1,status: "P",favorites: { artist:
"Picasso", food: "pizza" },finished: [ 17, 3 ],badges: [ "blue", "black"
],points: [{ points: 85, bonus: 20 },{ points: 85, bonus: 10 }]},
{_id: 8,name: "yilia",age: 42,type: 1,status: "A",favorites: { artist:
"Miro", food: "meringue" },finished: [ 11, 25 ],badges: [ "green" ],points: [{ points: 85,
bonus: 20 },{ points: 64, bonus: 12 }]},
{_id: 9,name: "vincent",age: 22,type: 2,status: "A",favorites: {
artist: "Cassatt", food: "cake" },finished: [ 6 ],badges: [ "blue",
"Picasso" ],points: [{ points: 81, bonus: 8 },{ points: 55, bonus: 20 }]},
{_id: 10,name: "mention",age: 34,type: 2,status: "D",favorites: {
artist: "Chagall", food: "chocolate" },finished: [ 5, 11 ],badges: [
"Picasso", "black" ],points: [{ points: 53, bonus: 15 },{ points: 51, bonus:
15 }]},
{_id: 11,name: "carol",age: 23,type: 2,status: "D",favorites: { artist:
"Noguchi", food: "nougat" },finished: [ 14, 6 ],badges: [ "orange" ],points:
[{ points: 71, bonus: 20 }]},
{_id: 12, name: "della",age: 43,type: 1,status: "A",favorites: { food:
"pizza", artist: "Picasso" },finished: [ 18, 12 ],badges: [ "black", "blue"
],points: [{ points: 78, bonus: 8 },{ points: 57, bonus: 7 }]}
]
)

2.3.2 update案例

更新操作案例

案例:下面的例子对 users 集合使用 db.users.update() 方法来更新过滤条件 favorites.artist 等于"Picasso" 匹配的第一个文档。

更新操作:

  • 使用 $set 操作符把 favorites.food 字段值更新为 "ramen" 并把 type 字段的值更新为 0。
  • 使用 $currentDate 操作符更新 lastModified 字段的值到当前日期。
    • 如果 lastModified 字段不存在, $currentDate 会创建该字段;
# 查找:favorites.artist 等于 "Picasso"
db.users.find(
	{"favorites.artist" : "Picasso"}
)

# 更新
db.users.update(
	{"favorites.artist" : "Picasso"},
	{
		$set: {"favorites.food": "ramen", type:0},
		$currentDate: {lastModified: true}
	}
)

# 使用 db.collection.update() 并包含 multi: true 选项来更新多个文档
db.users.update(
	{"favorites.artist" : "Picasso"},
	{
		$set: {"favorites.food": "ramen", type: 10},
		$currentDate: {lastModified: true}
	},
	{multi: true}
)

2.3.3 updateOne案例

更新单个文档案例

  • 使用 $set 操作符更新 favorites.food 字段的值为 "Chongqing small noodles" 并更新 type 字段的值为 3
db.users.updateOne(
	{"favorites.artist": "Picasso"},
	{
		$set: {"favorites.food": "Chongqing small noodles", type: 3},
		$currentDate: {lastModified: true}
	}
);

2.3.4 updateMany案例

更新多条文档案例

下面的例子对 users 集合使用 db.users .updateMany() 方法来更新所有根据过滤条件 favorites.artist等于 "Picasso" 匹配的文档。

更新操作:

  • 使用 $set 操作符更新 favorites.food 字段的值为 "Spicy fragrant pot" 并更新 type 字段的值为 3
db.users.updateMany(
	{"favorites.artist": "Picasso"},
	{
		$set: {"favorites.food": "Spicy fragrant pot", type: 3},
		$currentDate: {lastModified: true}
	}
)

2.3.5 replaceOne案例

文档替换案例

下面的例子对 users 集合使用 db.collection.replaceOne() 方法将通过过滤条件 name 等于 "della" 匹配到的 第一个文档替换为新文档:

更新除 _id 字段外文档的整个内容,传递一个全新的文档: db.collection.replaceOne() 或者db.collection.update() 作为第二个参数。当替换文档时,替换的文档必须仅仅由 <field> : <value>组成。

替换文档可以有不同于原文档的字段。

在替换文档中,由于 _id 字段是不变的,所以,你可以省略 _id 字段;如果你包含了 _id 字段,它的值必须和当前的值相同。

查找name 等于 "della":

db.users.find({"name""della"})

替换操作:

db.users.replaceOne(
	{"name""della"},
	{ name: "louise", age: 34, type: 2, status: "P", favorites: { "artist": "Dali", food: "donuts" }}
)

查询替换后的文件:

db.users.find({"name""louise"})

2.3.6 补充:运算符与修饰符解释

字段运算符

字段运算符解释
$inc按指定的数量增加字段值
$min仅当指定的值小于现有字段值的时候才更新字段
$max仅当指定的值大于现有字段值的时候才更新字段
$mul将字段的值乘以指定的量
$rename重命名字段
$setOnInsert如果更新导致文档插入,则设置字段的值。对修改现有文档的更新操作没有影响。

数组运算符

数组运算符解释
$充当占位符以更新与查询条件匹配的第一个元素
$[]充当占位符以更新数组中与查询条件匹配的文档中的所有元素
$addToSet仅当数组中尚不存在元素时才将元素添加到数组中
$pop删除与指定查询匹配的所有数组元素。
$pull重命名字段
$push将数据添加到数组
$pullAll从数组中删除所有匹配的值

修饰符

修饰符解释
$each修改push和addToSet运算符以附加多个项目以进行阵列更新
$position修改push运算符以指定数组中添加元素的位置
$slice修改push运算符以限制更新数组的大小
$sort修改push运算符以重新排序存储在数组中的文档。

2.4 数据删除

2.4.1 语法说明

db.collection.remove(
    <query>,
    {
        justOne: <boolean>,
        writeConcern: <document>
    }
)

参数说明:

  • query:(可选)删除的文档的条件。
  • justOne: (可选)如果设为 true 或 1,则只删除一个文档,如果不设置该参数,或使用默认值false,则删除所有匹配条件的文档。
  • writeConcern :(可选)用来指定MongoDB对写操作的回执行为。

2.4.2 删除案例

  • 根据条件删除数据
db.goods.remove(
	{status: 'A'}
)
  • 删除全部数据
db.goods.remove({})
  • 删除一条数据
db.goods.deleteOne(
	{status: 'A'}
)
  • 删除多条数据
db.goods.deleteMany(
	{status: 'A'}
)

3. 聚合操作

聚合是MongoDB的高级查询语言,它允许我们通过转化合并由多个文档的数据来生成新的在单个文档里不存在的文档信息。一般都是将记录按条件分组之后进行一系列求最大值,最小值,平均值的简单操作,也可以对记录进行复杂数据统计,数据挖掘的操作。

聚合操作的输入是集中的文档,输出可以是一个文档也可以是多个文档。

聚合操作分类:

  • 单目的聚合操作(Single Purpose Aggregation Operation)
  • 聚合管道(Aggregation Pipeline)
  • MapReduce 编程模型

下面详解一下三类操作

初始化测试数据

db.authors.insertMany([	{ "author" : "Vincent", "title" : "Java Primer", "like" : 10 },	{ "author" : "della", "title" : "iOS Primer", "like" : 30 },	{ "author" : "benson", "title" : "Android Primer", "like" : 20 },	{ "author" : "Vincent", "title" : "Html5 Primer", "like" : 40 },	{ "author" : "louise", "title" : "Go Primer", "like" : 30 },	{ "author" : "yilia", "title" : "Swift Primer", "like" : 8 }])

3.3.1 单目的聚合操作

单目的聚合命令常用的有:count()、distinct() 和group()

db.COLLECTION_NAME.find({}).count()

  • count()求数量
db.authors.count();
db.authors.count({"author":"Vincent"});
  • distinct(“field”)查询某字段并去重
db.authors.distinct("author")

3.3.2 聚合管道Aggregation Pipeline

3.3.2.1 语法说明

MongoDB中聚合(Aggregation)主要用于统计数据(如:统计平均值,求和...),并返回计算后的数据结果。

使用 db.COLLECTION.aggregate([{},...]) 方法来构建和使用聚合管道,每个文档通过一个由一个或者多个阶段(stage)组成的管道,经过一系列的处理,输出相应的结果。聚合管道将文档在一个管道处理完毕后将结果传递给下一个管道处理,管道操作可以重复。

常用操作:

  • $match :用于过滤数据,只输出符合条件的文档。$match是标准查询操作。
  • $project: 修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。
  • $group: 将集合中的文档分组,可用于统计结果。
  • $sort: 将输入文档排序后输出。
  • $limit: 用来限制聚合管道返回的文档数。

image.png

3.3.2.2 筛选、分组、排序和分页

$match:筛选案例

含义:用来筛选,通过match来筛选符合条件的文档

  • 查找出like值大于10的操作
db.authors.aggregate(
	{$match: {"like" : {$gt: 10}}}
)

$group: 分组案例

按照字段进行分组,和RDBMS的group by 类似

  • 按照id进行分组求和
db.authors.aggregate(
	{"$match": {"like" : {$gt: 10}}},
	{"$group": {"_id": "$author", "count": {$sum: 1}}}
)
  • 对多个字段进行分组
db.authors.aggregate(
	{"$match": {"like" : {$gt: 10}}},
	{"$group": {"_id": {"author": "$author", "like": "$like"}, "count": {$sum: 1}}}
)
  • 分组取最大值
db.authors.aggregate(
	{"$group": {"_id": "$author", "count": {"$max": "$like"}}}
)
  • 分组取平均值
db.authors.aggregate(
	{"$group": {"_id": "$author", "count": {"$avg": "$like"}}}
)
  • 将分组后的每个文档指定的值放在set集合中,集合不重复,无序
db.authors.aggregate(
	{"$group": {"_id": "$author", "like": {"$addToSet": "$like"}}}
)
  • 将分组后的每个文档指定的值放在数组中,允许重复,有序
db.authors.aggregate(
	{"$group": {"_id": "$author", "like": {"$push": "$like"}}}
)

$project:投射案例

作用:用来排除字段,也可以对现有的字段进行重命名

  • 字段名:0 就是不显示这个字段
  • 字段名:1 就是显示这个字段
db.authors.aggregate(
    {"$match": {"like": {"$gte" : 10} }},
    {"$project": {"_id": 0, "author":1, "title": 1}}
)


db.authors.aggregate(
	{"$match": {"like": {"$gte" : 10} }},
	{"$project": {"_id": 0, "author":1, "B-Name": "$title"}}
)

$sort:排序案例 用于对上一次处理的结果进行排序,1:升续 -1:降续

db.authors.aggregate(
    {"$match": {"like": {"$gte" : 10} }},
    {"$group": {"_id": "$author", "count": {"$sum": 1}}},
    {"$sort": {"count": -1}}
)

$limit: 限制条数案例

db.authors.aggregate(
	{"$match": {"like": {"$gte" : 10} }},
	{"$group": {"_id": "$author", "count": {"$sum": 1}}},
	{"$sort": {"count": -1}},
	{"$limit": 1}
)
3.3.2.3 算数表达式案例

主要是对其中的一些列进行加减乘除

$add

$add: [exp1, exp2, ... expN]: 对数组中的多个元素进行相加

$fieldname: 用于来引用该字段的值
  • 对like字段值进行+1操作
db.authors.aggregate(
	{"$project": {"newLike" : {"$add": ["$like",1]}}}
)

$subtract

  • 对like字段值减2操作
db.authors.aggregate(
	{"$project": {"newLike" : {"$subtract": ["$like",2]}}}
)

$multiply

  • 对数组中的多个元素相乘
db.authors.aggregate(
	{"$project": {"newLike" : {"$multiply": ["$like",2]}}}
)

$divide

  • 数组中的第一个元素除以第二个元素
db.authors.aggregate(
	{"$project": {"newLike" : {"$divide": ["$like",2]}}}
)

$mod

  • 求数组中第一个元素除以第二个元素的余数
db.authors.aggregate(
	{"$project": {"newLike" : {"$mod": ["$like",3]}}}
)

$substr

  • 字符串截取操作
db.authors.aggregate(
	{"$project": {"newLike" : {"$substr": ["$title",1,2]}}}
)

$concat

  • 字符串操作:将数组中的多个元素拼接在一起
db.authors.aggregate(
	{"$project": {"newLike" : {"$concat": ["(", "$title",")"]}}}
)

$toLower

  • 字符串转小写
db.authors.aggregate(
	{"$project": {"newTitle": {"$toLower": "$title"} }}
)

$toUpper

  • 字符串操作,转大写
db.authors.aggregate(
	{"$project": {"newTitle": {"$toUpper": "$title"} }}
)
3.3.2.4 日期表达式案例

用于获取日期中的任意一部分,年月日时分秒 星期等

$year、$month、$dayOfMonth、$dayOfWeek、$dayOfYear、$hour、$minute、$second

# 新增一个字段:
db.authors.update(
	{},
	{"$set": {"publishDate": new Date()}},
	true,
	true
)

# 查询出版月份
db.authors.aggregate(
	{"$project": {"month": {"$month": "$publishDate"}}}
)

# 返回年月日
db.authors.aggregate([
    {
        $project: {
            "dateString": {
                "$dateToString": { "format": "%Y-%m-%d", date: "$publishDate" }
            }
        }
    }
])
3.3.2.5 逻辑运算符案例

$cmp比较

$cmp: [exp1, exp2]: 

  • 等于返回 0
  • 小于返回 -1
  • 大于返回 1
db.authors.aggregate(
	{"$project": {"result": {"$cmp": ["$like", 20]} }}
)
$eq: 用于判断两个表达式是否相等

$ne: 不相等

$gt: 大于

$gte: 大于等于

$lt: 小于

$lte: 小于等于
db.authors.aggregate(
	{"$project": {"result": {"$eq": ["$author", "Vincent"]}}}
)

$and且

$and:[exp1, exp2, ..., expN]

用于连接多个条件,一假and假,全真and为真

db.authors.aggregate(
	{"$project": {
		"result": {
			"$and": [{"$eq": ["$author", "Vincent"]}, {"$gt": ["$like", 20]}]
		}
	}}
)

$or或

$or: [exp1, exp2, ..., expN]

用于连接多个条件,一真or真,全假and为假

db.authors.aggregate(
	{"$project": {
		"result": {
			"$or": [{"$eq":["$author", "Vincent"]}, {"$gt": ["$like", 20]}]
		}
	}}
)

$not取反

$not: exp

用于取反操作

db.authors.aggregate(
	{"$project": {
		"author": "$author",
		"result": {"$not" : {"$eq": ["$author", "Vincent"]}}
	}}
)

$cond三元运算符

$cond: [booleanExp, trueExp, falseExp]

db.authors.aggregate(
	{"$project" :
		{"result": {
			"$cond": [
				{"$eq": ["$author", "Vincent"]}, 111, 222
			]
		}}
	}
)

$ifNull非空

$ifNull: [expr, replacementExpr]

如果条件的值为null,则返回后面表达式的值,当字段不存在时字段的值也是null

db.authors.aggregate(
	{"$project" :
		{"result" : {"$ifNull": ["$notExistFiled", "is not exit"]}}
	}
)

3.3.3 MapReduce 编程模型

MapReduce是一种计算模型,简单的说就是将大批量的工作分解(Map)执行,然后再将结果合并成最终结果(Reduce)。 Aggregation Pipeline查询速度快于MapReduce,但是MapReduce的强大之处在于能够在多台Server上并行执行复杂的聚合逻辑

MongoDB不允许Aggregation Pipeline的单个聚合操作占用过多的系统内存,如果一个聚合操作消耗20%以上的内存,那么MongoDB直接停止操作,并向客户端输出错误消息。所以MapReduce价值之大还在Aggregation Pipeline之上。

1)语法说明

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

使用 MapReduce 要实现两个函数:Map 和 Reduce函数:

  • Map调用emit(key,value),遍历所有collection中所有的数据,并将key与value传递给reduce
  • Reduce处理Map传递过来的所有记录

参数说明:

  • map: 是JavaScript的函数,负责将每一个输入文档转换为零或多个文档,生成键值对序列,作为reduce 函数参数。
  • reduce:是JavaScript的函数,对map操作的输出做合并的化简的操作
    • 将key-value变成KeyValues,也就是把values数组变成一个单一的值value
  • out:统计结果存放集合
  • query:筛选条件,只有满足条件的文档才会调用map函数。
  • sort:和limit结合的sort排序参数(也是在发往map函数前给文档排序)可以优化分组机制
  • limit:发往map函数的文档数量的上限(没有limit单独使用sort的用处不大)
  • finalize:可以对reduce输出结果最后进行的处理
  • verbose:是否包括结果信息中的时间信息,默认为fasle

初始化测试数据

db.posts.insert({"post_text": "测试mapreduce。", "user_name": "Vincent", "status":"active"})
db.posts.insert({"post_text": "适合于大数据量的聚合操作。","user_name": "Vincent", "status":"active"})
db.posts.insert({"post_text": "this is test。","user_name": "Benson", "status":"active"})
db.posts.insert({"post_text": "技术文档。", "user_name": "Vincent", "status":"active"})
db.posts.insert({"post_text": "hello word", "user_name": "Louise", "status":"no active"})
db.posts.insert({"post_text": "lala", "user_name": "Louise", "status":"active"})
db.posts.insert({"post_text": "天气预报。", "user_name": "Vincent", "status":"no active"})
db.posts.insert({"post_text": "微博头条转发。", "user_name": "Benson", "status":"no active"})

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"
 }
)
  • result: 储存结果的collection的名字,这是个临时集合,MapReduce的连接关闭后自动就被删除了。
  • timeMillis:执行花费的时间,毫秒为单位
  • counts:
    • input:满足条件被发送到map函数的文档个数
    • emit:在map函数中emit被调用的次数,也就是所有集合中的数据总量
    • ouput:结果集合中的文档个数
    • reduce:在reduce函数被调用的次数
  • ok:是否成功,成功为1
  • err:如果失败,这里可以有失败原因,不过从经验上来看,原因比较模糊,作用不大

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

# post_total是out对应的名称
db.post_total.find();