Mongodb 3.0+操作手册 纯手打

Mongodb操作手册

权限控制·· 3

用户概念··· 3

内建角色··· 3

角色权限··· 4

开启授权机制··· 5

用户授权详解··· 6

命名规范·· 7

文档··· 7

集合··· 8

数据库··· 9

SHELL中的基本操作·· 9

创建··· 10

读取··· 10

更新··· 11

删除··· 11

数据类型·· 12

增删改查·· 14

添加数据··· 14

删除数据··· 15

修改数据··· 16

查询数据··· 22

索引·· 26

索引的种类··· 26

索引的属性··· 33

聚合·· 35

聚合框架··· 35

管道操作符··· 37

数学表达式··· 38

日期表达式··· 40

字符串表达式··· 41

逻辑表达式··· 42

分组去重··· 44

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

权限控制

用户概念

Mongodb的用户是由 用户名+所属库名组成

例如:

登录mongo testdb1 ,创建用户testuser

登录mongo testdb2 ,创建用户testuser

那上面创建的用户分别是:testuser@testdb1,testuser@testdb2

也就是说在哪个库下面创建用户,这个用户就是哪个库的

内建角色

Mongodb的授权采用了角色授权的方法,每个角色包括一组权限。

Mongodb已经定义好了的角色叫内建角色,我们也可以自定义角色。

这儿主要介绍内建角色,Mongodb内建角色包括下面几类:

1. 数据库用户角色:read、readWrite;

2. 数据库管理角色:dbAdmin、dbOwner、userAdmin;

3. 集群管理角色:clusterAdmin、clusterManager、clusterMonitor、hostManager;

4. 备份恢复角色:backup、restore;

5. 所有数据库角色:readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、dbAdminAnyDatabase

6. 超级用户角色:root

这里还有几个角色间接或直接提供了系统超级用户的访问(dbOwner 、userAdmin、userAdminAnyDatabase)

7. 内部角色:__system

角色权限

数据库管理角色:

read:该角色只有读取数据的权限。只需读权限可以备份数据库

readWrite:该角色有数据的读写权限,但不能创建删除索引,也不能创建和删除数据库。读写权限可以还原数据库备份

dbAdmin:该角色拥有数据库管理权限(比如更改数据库类型)

dbOwner:是read、readWrite、dbAdmin这三者角色的集合体

userAdmin:该角色可以对其他角色权限进行管理

集群管理角色:

clusterAdmin:提供了最大的集群管理功能。相当于clusterManager,clusterMonitor,

and hostManager和dropDatabase的权限组合

clusterManager:提供了集群和复制集管理和监控操作。拥有该权限的用户可以操作config和local数据库(即分片和复制功能)

clusterMonitor:仅仅监控集群和复制集

hostManager:提供了监控和管理服务器的权限,包括shutdown节点,logrotate, repairDatabase等。

所有数据库角色:

readAnyDatabase:具有read每一个数据库权限。但是不包括应用到集群中的数据库。

readWriteAnyDatabase:具有readWrite每一个数据库权限。但是不包括应用到集群中的数据库。

userAdminAnyDatabase:具有userAdmin每一个数据库权限,但是不包括应用到集群中的数据库。

dbAdminAnyDatabase:提供了dbAdmin每一个数据库权限,但是不包括应用到集群中的数据库。

超级管理员权限:

root: dbadmin到admin数据库、useradmin到admin数据库以及UserAdminAnyDatabase。

但它不具有备份恢复、直接操作system.*集合的权限,但是拥有root权限的超级用户

可以自己给自己赋予这些权限。

开启授权机制

1、找到mongodb配置文件,设置noauth=true

重启Mongodb后,登录admin账号,创建一个超级权限用户

语法:

use admin

db.createUser({user:'root',pwd:'root',roles:[{ "role" : "root", "db" : "admin" }]});

2、关闭mongodb

3、启用认证参数

要保证权限认证生效,需要在mongodb配置文件中加入auth=true,同时删掉noauth=true

4、启动Mongodb

5、认证登录

(1)在连接期间进行身份验证:

mongo 127.0.0.1:27017/db -u username  -p  passwordxxx

(2)连接后验证

切换到身份验证数据库(在这里为test),并使用db.auth(<username>,<pwd>)方法进行身份验证:

use test

db.auth("myTester", "xyz123" )

用户授权详解

1、创建用户并授权

语法:

db.createUser({user:"UserName",pwd:"Password",roles:[{role:"RoleName",db:"Target_DBName"}]})

首先选择在哪个库创建用户,如test:use test;

创建用户有3项需要提供:用户名,密码,角色列表

例如我要在test下面创建用testuser,密码为testpwd,角色列表包括test库的readWrite角色和userAdmin角色:

db.createUser({user:"testuser",pwd:"testpwd",roles:[{role:"readWrite",db:"test"},{role:"userAdmin",db:"test"}]})

用户登录:db.auth(‘testuser’,’testpwd’)

2、修改密码

首先进入目标库:

use test

db.changeUserPassword('testuser','testPWD');

3、添加角色

首先进入目标库:

use test

db.grantRolesToUser(  "testuser",  [    { role: "read",db:"admin"}  ] )

4、回收角色权限

首先进入目标库:

use test

db.revokeRolesFromUser("testuser",[   { role: "read",db:"admin"}  ] )

5、删除用户

首先进入目标库:

use test

db.dropUser("testuser")

6、注意

在该库下创建的帐号,不能直接在其他库验证,只能在帐号创建库下认证,再去其他库进行操作。

命名规范

文档

       文档就是键值对的一个有序集。例如:{“greeting”:”hello, world! ”,“foo”:“3”},其中“greeting”和“foo”是键,“hello,world!”和“3”是他们对应的值。

       文档的键是字符串。除了少数例外情况,键可以使用任意UTF-8字符。

       1、键不能含有\0(空字符)。这个字符用于表示键的结尾。

       2、.和$具有特殊意义,只能在特定环境下使用。通常这两个字符是被保留的;如果使用不当的话,驱动程序会有提示。

       3、MongoDB的文档不能有重复的键,且区分类型和大小写。

       4、文档中的键/值对是有序的:{“x”:1,“y”:2}与{“y”:2,“x”:1}是不同的。

集合

       集合就是一组文档。如果将MongoDB的一个文档比喻为关系型数据库中的一行,那么一个集合就相当于一张表。例如,下面两个文档可以存储在同一个集合里面:

{“greeting”:“hello,world!”}

{“foo”:5 }

       集合使用名称进行标识。集合名可以是满足下列条件的任意UTF-8字符串。

1、集合名不能是空字符串(””)。

2、集合名不能包含\0字符(空字符),这个字符表示集合名的结束。

3、集合名不能以“system.”开头,这是为系统集合保留的前缀。例如,system.users这个集合保存着数据库的用户信息,而system.namespaces集合保存着所有数据库集合的信息。

4、用户创建的集合不能在集合名中包含保留字符‘$’。因为某些系统生成的集合中包含$,很多驱动程序确实支持在集合名里包含该字符。除非你要访问这种系统创建的集合,否则不应该在集合命中包含$。

子集

MongoDB还有一个子集合的概念,就是一个集合包含几个集合,这样也是便于管理,比如将people的基本信息和国籍信息这样放在一个people集合中有点勉强,可以直接写成一个文档放进去,也可以用两个子集合,一个表示基本信息baseinfo,一个表示国籍信息countryinfo。

数据库

       在MongoDB中,多个文档组成集合,而多个集合可以组成数据库。

       数据库通过名称来标识,这点预计和类似。数据库名可以是满足以下条件的任意UTF-8字符串:

       1、不能是空字符串(“”)。

       2、不得含有“/”、“\”、”、*、“<”、“>”、“:”、“|”、“?”、$(一个空格)、\0(空字符)。基本上,只能使用ASCII中的字母和数字。

       3、数据库名区分大小写,即便是在不区分大小写的文件系统中也是如此。简单起见,数据库名迎全部小写。

       4、数据库名最多为64字节。

       5、保留数据库名有:admin、local、config。

SHELL中的基本操作

在shell中查看或操作数据会用到4个基本操作:创建、读取、更新和删除(即CRUD操作)。

创建

       Insert函数可将一个文档添加到集合中。举一个存储博客文章的例子。首先,创建一个名为post的局部变量,这是一个JavaScript对象,用于表示我们的文档。它会有几个键:“title”、“content”和“date”(发布日期)。

这个对象是个有效的MongoDB文档,所以可以用insert方法将其保存到blog集合中:

也可这样插入:

db.blog.insert({“title”:“my blog post”,“content”:“test”,“date”:“2017-10-26”})

这篇文章已被存到数据库中。要查看它可用调用集合的find方法:

>db.blog.find()

读取

Find和findOne方法可以用于查询集合里的文档。若只想查看一个文档,可用findOne:

Find和findOne可以接受一个查询文档作为限定条件。这样就可以查询符合一定条件的文档。使用find时,shell会自动显示最多20个匹配的文档,也可获取更多文档。后续介绍。

更新

使用update修改博客文章。Update接受至少 两个参数:第一个是限定条件(用于匹配待更新的文档),第二个是新的文档。假设我们要给先前写的文章增加评论功能,就需要增加一个新的键,用于保存评论数组。

首先,修改变量post,增加“comments”键:

然后执行update操作,用新版本的文档替换标题为“My Blog Post”的文章:

现在,文档已经有了“comments”键。再用find查看一下,可以看到新的键:

删除

使用remove方法可将文档从数据库中永久删除。如果没有使用任何参数,它会将集合内的所有文档全部删除。它可以接受一个作为限定条件的文档作为参数。例如,下面的命令会删除刚刚创建的文章:

现在,集合又是空的了。

数据类型

MongoDB有着非常丰富的数据类型,如上面的name字段是字符串,age字段是数字,当然不止这两种。JSON只有6中数据类型,null,布尔,数字,字符串,数组和对象。MongoDB在JSON的基础上添加了一些其他的数据类型,下面来看看MongoDB的数据类型:

null:用于表示控制或者不存在的字段,如:{"x": null}。

     布尔:只有两个值true和false。

     32位整数:shell中这个类型不可用,javascript仅支持64位浮点数,所以32位整数会被自动转换。

     64位整数:shell中这个类型不可用,shell会使用一个特殊的内嵌文档来显示64位整数。

     64位浮点数:shell中的数字都是这个类型,{"x": 3.14}和{"x" : 3}都是浮点数。

     因为javascript只有一种数字类型就是64位浮点型,所以MongoDB中从shell的来的数字都被当做64位浮点型,而MongoDB中支持三种数字类型,所以用shell修改过数据库中的数据后都会被转换成64位浮点型。64位整数并不能精确的表示64位浮点型,如果MongoDB中存入了一个64位整数,在shell中查看时,如果能够表示64位浮点型那就用一个键的内置文档显示而且这个值是精确的,否则,他会显示一个多键内嵌文档,表示可能不精确。

     如果是64位整数3,那么在shell中查询显示会是这个样子:

db.nums.findOne() 

        "_id" : ObjectId("4c0beecfd096a2580fe6fa08"), 

        "myInteger" : { 

                "floatApprox" : 3 

    } 

}

如果是64位整数9223372036854775807,那么在shell中查询显示会是这个样子:

db.nums.findOne()

{

"_id" : ObjectId("4c0beecfd096a2580fe6fa08"),

"myInteger" : {

"floatApprox" : 9223372036854775807,

"top" : 2147483647,

"bottom" : 4294967295

}

}

topbottom分别表示高32位和低32位。

字符串:UTF-8字符串都可表示为字符串类型的数据,如:{"name" : "Mary"}。

符号:shell不支持这种类型,shell会将数据库中的符号类型转换成字符串。

对象id:对象id是文档的12字节的唯一ID。

MongoDB中存储的文档必须有一个键"_id",这个键可以是任意类型的,默认是ObjectId对象,当我们存入文档时不指定该键,那么MongoDB会自动添加这样一个键值对,这个值是唯一标识,ObjectId使用12字节的存储空间。

日期:日期类型存储的是从标准纪元开始的毫秒数,不存储时区,如:{"x": new Date()}。

javascript中Date对象用作MongoDB的日期类型,创建日期对象要用new Date()而不是Date(),返回的是对日期的字符串表示,而不是真正的Date对象。

正则表达式:文档中可以包含正则表达式,采用javascript的正则表达式语法,如:{"x" : /foobar/i}。

代码:文档中还可以包含javascript代码,如:{"x" : function(){/*...*/}}。

二进制数据:二进制数据可以由任意字节的串组成,不过shell中无法使用。

最大值:BSON包括一个特殊类型,表示可能的最大值,shell中没有这个类型。

最小值:BSON包括一个特殊类型,表示可能的最小值,shell中没有这个类型。

未定义:文档中也可以使用未定义类型,如:{"x" :undefined}

数组:值的集合或者列表可以表示成数组,数组中的元素可以是不同类型的数据,如:{"x": ["a", "b", "c", 20]}。

内嵌文档:文档可以包含别的文档,也可以作为值嵌入到父文档中,如:{"x": {"foo" : "bar"}}。

在我目前用的情况,布尔,数字,字符串,日期,数组和内嵌文档是用的最多的。

增删改查

添加数据

insert添加时如果主键重复则报错,而save添加时主键重复则覆盖

db.cname.insert({name:"zhangsan", age:23})

db.cname.save({name:"zhangsan", age:23})

删除数据

删除数据比较简单:

1.带条件删除

>db.user.remove({"name":"zhangshan"});

2.删除所有数据

>db.user.remove({})

3.删除集合

>db.user.drop()

4.删除整个数据库

>show dbs;

>db.user.getDB()

>db.dropDatabase()

删除文档是永久性的,不能撤销,也不能恢复的。因此,在执行remove()函数前先用find()命令来查看下是否正确。

mongodb删除集合后磁盘空间不释放,用db.repairDatabase()去修复才能释放。但是在修复的过程中如果出现了非正常的mongodb的挂掉,再次启动时启动不了的,需要先修复才可以,可以利用./mongod--repair --dbpath=/data/mongo/,如果你是把数据库单独的放在一个文件夹中指定dbpath时就指向要修复的数据库就可以,修复可能要花费很长的时间,在使用db.repairDatabase()去修复时一定要停掉读写,并且mongodb要有备机才可以,不然千万不要随便使用db.repairDatabase()来修复数据库,切记。

修改数据

1). update()

db.collection.update( criteria, objNew,upsert, multi )四个参数的说明如下:

criteria: update的查询条件,类似sql update查询内where后面的

objNew: update的对象和一些更新的操作符(如$,$inc...)等,也可以理解为sql update查询内set后面的

upsert: 这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。

multi: mongodb默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。

几个查询例子如下:

db.mytest.update({count:{$gt:1}},{$set:{name:"ok"}})              

只更新第一条记录

db.mytest.update({count:{$gt:3}},{$set:{name:"ok"}},false,true)    

大于3的全部更新了

db.mytest.update({count:{$gt:4}},{$set:{name:"ok123"}},true,false)  

只更新了一条

db.mytest.update({count:{$gt:6}},{$set:{name:"ok123"}},true,true)  

大于6的全部更新了

2). save()

db.collection.save(x):  x是要插入的对象,效果与上面的insert命令一样。save与insert的区别是这样的:

在进行插入数据的操作中,当遇到_id相同的情况下,save完成保存操作,insert则会保存;即_id相同情况下,save相当于更新操作。 

3). $inc

用法:{$inc:{field:value}}   意思是对一个数字字段field增加value:

[plain] 

> db.mytest.find({_id:1})       

{ "_id" : 1, "name" : "test1", "count" : 1 }  

> db.mytest.update({_id:1},{$inc:{count:1}})  

> db.mytest.find({_id:1})  

{ "_id" : 1, "name" : "test1", "count" : 2 }  //count字段加1  

value的值也可以为负,就相当于减一个值:

[plain] 

> db.mytest.update({_id:1},{$inc:{count:-2}})  

> db.mytest.find({_id:1})  

{ "_id" : 1, "name" : "test1", "count" : 0 }  //值从2减到0  

4). $set命令

用法:{$set:{field:value}}

相当于在关系型数据库中sql的setfield=value,全部数据类型都支持$set操作

[plain] 

> db.mytest.update({_id:1},{$set:{count:111}})  

> db.mytest.find({_id:1})  

{ "_id" : 1, "name" : "test1", "count" : 111 }   //修改数值型  

> db.mytest.update({_id:1},{$set:{name:"MongoDB"}})  

> db.mytest.find({_id:1})  

{ "_id" : 1, "count" : 111, "name" : "MongoDB" }   //修改字符型  

5). $unset

用法:{$unset:{field:1}}

> db.mytest.find({_id:1})  

{ "_id" : 1, "count" : 111, "name" : "MongoDB" }  

> db.mytest.update({_id:1},{$unset:{name:1}})  

> db.mytest.find({_id:1})  

{ "_id" : 1, "count" : 111 }  //删除了字段name  

 6).$push

用法:{$push:{field:value}}

把value追加到field中取,field一定是数据类型才行,如果field不存在,会新增一个数组类型加进去:

[plain] 

>db.mytest.update({_id:15},{$set:{array:["aaa","bbb"]}}) 

> db.mytest.find({_id:15})  

{ "_id" : 15,"array" : [  "aaa",  "bbb" ],"count" : 15, "name" : "ok123" }  

使用push追加数据:

[plain] 

> db.mytest.update({_id:15},{$push:{array:"ccc"}})  

> db.mytest.find({_id:15})  

{ "_id" : 15, "array" : [  "aaa",  "bbb",  "ccc" ], "count" : 15, "name" : "ok123" }  

push一次只能追加一个值,如果需要追加多个值,则需要使用$pushAll:

[plain] 

> db.mytest.update({_id:15},{$pushAll:{array:["ddd","eee","fff"]}})  

> db.mytest.find({_id:15})  

{ "_id" : 15, "array" : [  "aaa",  "bbb",  "ccc",  "ddd",  "eee",  "fff" ], "count" : 15, "name" : "ok123" }  

7). $addToSet

用法:{$addToSet:{field:value}}

增加一个值到数组内,而且只有当这个值不在数组内才增加:

[plain] 

> db.mytest.update({_id:15},{$addToSet:{array:"123"}})  

> db.mytest.find({_id:15})  

{ "_id" : 15, "array" : [  "aaa",  "bbb",  "123" ], "array2" : [  "mmm",  "nnn"], "count" : 15, "name" : "ok123" }  

> db.mytest.update({_id:15},{$addToSet:{array:"aaa"}})  

> db.mytest.find({_id:15})  

{ "_id" : 15, "array" : [  "aaa",  "bbb",  "123" ], "array2" : [  "mmm",  "nnn"], "count" : 15, "name" : "ok123" }  

 8). $pop

删除数组内的一个值,删除最后一个值:{$pop:{field:1}} ,删除第一个值:{$pop:{field:-1}}

[plain] 

> db.mytest.find({_id:15})  

{ "_id" : 15, "array" : [  "aaa",  "bbb",  "123" ], "array2" : [  "mmm",  "nnn"], "count" : 15, "name" : "ok123" }  

> db.mytest.update({_id:15},{$pop:{array:1}})  

> db.mytest.find({_id:15})  

{ "_id" : 15, "array" : [  "aaa",  "bbb" ], "array2" : [  "mmm",  "nnn" ], "count" : 15, "name" : "ok123" }  

 9).$pull

用法:$pull:{field:value}   从数组中删除一个等于value的值:

[plain] 

> db.mytest.find({_id:15})  

{ "_id" : 15, "array" : [  "aaa",  "bbb" ], "array2" : [  "mmm",  "nnn" ], "coun  

t" : 15, "name" : "ok123" }  

> db.mytest.update({_id:15},{$pull:{array:"aaa"}})  

> db.mytest.find({_id:15})  

{ "_id" : 15, "array" : [  "bbb" ], "array2" : [  "mmm",  "nnn" ], "count" : 15,  

 "name" : "ok123" }  

 10).$pullAll

用法同$pull,可以一次删除数组内的多个值:

[plain] 

> db.mytest.find({_id:15})  

{ "_id" : 15, "array" : [  "bbb" ], "array2" : [  "mmm",  "nnn" ], "count" : 15,"name" : "ok123" }  

> db.mytest.update({_id:15},{$pullAll:{array2:["mmm","nnn"]}})  

> db.mytest.find({_id:15})  

{ "_id" : 15, "array" : [  "bbb" ], "array2" : [ ], "count" : 15, "name" : "ok123" }  

11). $

可以理解为数组定位器,看一个官方文档的例子:

[plain] 

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

> t.find()  

{ "_id" : ObjectId("4b97e62bf1d8c7152c9ccb74"), "title" : "ABC", "comments" : [ { "by" : "joe", "votes" : 4 }, { "by" : "jane", "votes" : 7 } ] }  

需要注意的是,$只会找到第一条数组项,后面的就不管了:

[plain] 

> db.mytest.find({_id:16})  

{ "_id" : 16, "x" : [  1,  2,  3,  1 ] }  

> db.mytest.update({x:1},{$inc:{"x.$":1}})  

> db.mytest.find({_id:16})  

{ "_id" : 16, "x" : [  2,  2,  3,  1 ] }  

还有一点需要注意,当$配合$unset使用的时候,会留下一个null的数组项,这个问题可以使用{$pull:{x:null}}解决:

[plain] 

> db.mytest.find({_id:16})  

{ "_id" : 16, "x" : [  2,  2,  3,  1 ] }  

> db.mytest.update({x:3},{$unset:{"x.$":1}})  

> db.mytest.find({_id:16})  

{ "_id" : 16, "x" : [  2,  2,  null,  1 ] }  

> db.mytest.update({_id:16},{$pull:{x:null}})  

> db.mytest.find({_id:16})  

{ "_id" : 16, "x" : [  2,  2,  1 ] }  

查询数据

第一种,mongodb的数据查询可以通过find()来查询,有几种常见的查询方式:

db.user.find() 查询出所有对象,这应该是最简单的全部查找的写法

第二种,使用游标来查询

var cursor=db.user.find();

while(cursor.hasNext()) printjson(cursor.next());

第三种,普通查询,游标可以看成为数组,可以查询数组上特定位置的值。

Var cursor = db.user.find();

While(cursor.hasNext())   printjson(cursor[4])

第四种,数组:

Var arr = db.user.find().toArray();

Printjson(arr[3]);

条件数据查询

1,条件数据查询

就是在查询中加入过滤条件,mongodb的精确过滤条件是制定查询数据中json数据。 例如 db.user.find({“age”;”20”}) 相当于sql中的 select *from user where age = ‘20’

2,findOne() 

mongodb为了减少游标的内存开销提供的方法,可以加过滤条件

. db.user.findOne()

. db.user.findOne({“name”:”wpz”})

这两种写法都是合法的。

3,mongodb还提供limit来限制条数

. db.user.find().limit(2)

. db.user.find({“name”:”wpz”}).limit(2)

4,条件符查询

mongodb支持< <= > >= 四种运算符查询

db.user.find({“age”:{$gt:30}}) age大于30

db.user.find({“age”:{$lt:30}}) age小于30

db.user.find({“age”:{$gte:30}}) age大于或等于30

db.user.find({“age”:{$lte:30}}) age小于或等于30

多条件查询

db.user.find({“age”:{,gt:10,lte:30}})

5,匹配所有

$all 这个操作符号类似于sql中的in运算符,但是不同的是in只需要满足 一个值,但是alll需要满足所有值。

db.user.find({“age”:{$all:[6,8]}});

6,查询某一个字段是否存在:$exists

db.user.find({“password”:{$exists:true}}); password存在的记录

db.user.find({“password”:{$exists:false}}); password不存在的记录

7,null值得处理

null处理比较奇怪,因为mongodb中的数据集合不能指定特定的格式,没有sql中的字段的概念,就是说,在同一个集合中有的字段在一条数据中存在,在另一条数据中不存在,所以,要找出改字段是不是为空,先要判断这个字段是不是存在才行。

db.user.find({age:{“in":[null],"exists”:true}});

8,取模运算 $mod

查询所有age取模10之后为0 的数据,即查询age为10的倍数的字段:

db.user.find({age:{$mod:[10,0]}});

10,不等于 $ne –> (not equals)

查询所有age不等于10 的数据

db.user.find({age:{$ne:10}});

11,包含 $in

查询所有age等于10 或者20 的数据

db.user.find({age:{$in:[10,20]}});

12,不包含 $nin

查询所有age不等于10 或者20 的数据

db.user.find({age:{$nin:[10,20]}});

13,数组元素的个数 $size

查询age数据元素个数为3的数据

db.user.find({age:{$size:3}});

14,正则表达式匹配查询

name不以wpz开头的数据

db.user.find({“name”:{$not:/^wpz.*/}});

15,count查询条数

- db.user.find().count();

16,skip 设置查询数据的起点

查询从第三条数据之后的五条数据

- db.user.find().skip(3).limit(5);

17,排序 sort

- db.user.find().sort({age:1}); 按照age升序

- db.user.find().sort({age:-1}); 按照age降序

18,极值操作符

下面的四个操作符可用于得到数据集合中的“边缘”值。

(1)“$max”:expr

返回组内的最大值

(2)“$min”:expr

返回组内的最小值

(1)“$first”:expr

返回分组的第一个值,忽略后面所有的值。只有排序之后,明确知道数据顺序时这个操作才有意义。

(1)“$last”:expr

与first相反,返回分组的最后一个值。

“$max”和“$min”会查看每一个文档,以便得到极值。因此,若数据是无序的,这两个操作符也可以有效工作;如果数据是有序的,这两个操作符就会有些浪费。假设有一个存有学生考试成绩的数据集,需要找到其中的最高分与最低分:

 另一方面,如果数据集是按照希望的字段排序过,那么first和last更高效,且结果相同。

 

索引

索引的种类

1:_id索引:是绝大多数集合默认建立的索引,对于每个插入的数据,MongoDB都会自动生成一条唯一的_id字段 
2:单键索引:是最普通的索引

与_id索引不同,单键索引不会自动创建 如:一条记录,形式为:{x:1,y:2,z:3}

db.imooc_2.getIndexes()//查看索引

db.imooc_2.ensureIndex({x:1})//创建索引,索引可以重复创建,若创建已经存在的索引,则会直接返回成功。

db.imooc_2.find()//查看数据

3:多键索引 
多键索引与单键索引创建形式相同,区别在于字段的值。 
1)单键索引:值为一个单一的值,如字符串,数字或日期。 
2)多键索引:值具有多个记录,如数组。

db.imooc_2.insert({x:[1,2,3,4,5]})//插入一条数组数据

4:复合索引:查询多个条件时,建立复合索引 
例如{x:1,y:2,z:3}这样一条数据,要按照x与y的值进行查询,就需要创建复合索引。

db.imooc_2.ensureIndex({x:1,y:1}) #1升序,-1降序

db.imooc_2.find({x:1,y:2}) #使用复合索引查询

5:过期索引 
在一段时间后会过期的索引 
在索引过期后,相应的数据会被删除 
适合存储在一段时间之后会失效的数据,比如用户的登录信息、存储的日志等。 

db.imooc_2.ensureIndex({time:1},{expireAfterSeconds:10}) #创建过期索引,time-字段,expireAfterSeconds在多少秒后过期,单位:秒

db.imooc_2.ensureIndex({time:1},{expireAfterSeconds:30}) #time索引30秒后失效

db.imooc_2.insert({time:new Date()}) #new Date()自动获取当前时间,ISODate

db.imooc_2.find() #可看到刚才insert的值

过30秒后再find,刚才的数据就已经不存在了。

过期索引的限制: 
(1)存储在过期索引字段的值必须是指定的时间类型,必须是ISODate或者ISODate数组,不能使用时间戳,否则不能自动删除。 
例如 >db.imooc_2.insert({time:1}),这种是不能被自动删除的 
(2)如果指定了ISODate数组,则按照最小的时间进行删除。 
(3)过期索引不能是复合索引。因为不能指定两个过期时间。 
(4)删除时间是不精确的。删除过程是由MongoDB的后台进程每60s跑一次的,而且删除也需要一定时间,所以存在误差。

6:全文索引:对字符串与字符串数组创建全文课搜索的索引 。
不适用全文索引:查找困难,效率低下,需要正则匹配,逐条扫描。 
使用全文索引:简单查询即可查询需要的结果。

创建方式:

db.articles.ensureIndex({key:"text"}) #key-字段名,value-固定字符串text

上述指令表示,在articles这个集合的key字段上创建了一个全文索引

db.articles.ensureIndex({key1:"text",key2:"text"}) #在多个字段上创建全文索引

对于nosql数据库,每个记录存储的key可能都是不同的,如果要在所有的key上建立全文索引,一个一个写很麻烦,mongodb可以通过下面指令完成:

db.articles.ensureIndex({"$**":"text"}) #给所有字段建立全文索引

全文索引的创建: 
1:可以为一个字段创建全文索引 
2:可以为多个字段创建全文索引 
3:可以为集合中所有的字段创建全文索引 
注意:上面三种创建全文索引的方式,前两个方式类似,第三个需要一个特殊的字符串来表示——”$**”,我想如果集合中就两三个字段,也可以使用2来创建这样的全文索引,如果这个集合总就一个字段使用1也是可以的,3仅仅是为了统一化而已。

全文索引的查找: 
1:使用全文索引查询不需要指定全文索引的字段名字——直接使用$text,$search即可 
2:在MongoDB中每个数据集合只允许创建一个全文索引,不过这个全文索引可以针对一个、多个、全部的数据集合的字段来创建。 
3:查询多个关键词,可以使用空格将多个关键词分开——空格——或的关系 
4:指定不包含的字段使用-来表示—— -:非的关系 
5:引号包括起来代表与的关系—— \”\”:与的关系

db.articles.find({$text:{$search:"coffee"}})

db.articles.find({$text:{$search:"aa bb cc"}}) #空格代表或操作,aa或bb或cc

db.articles.find({$text:{$search:"aa bb -cc"}}) #-号为非操作,即不包含cc的

db.articles.find({$text:{$search: "\"aa\" \"bb\" \"cc\""}}) #加双引号可以提供与关系操作

相似度查询:

搜索排序,查询结果与你查询条件越相关的越排在前面。 
MongoDB中可以使用$meta操作符完成,格式:

{score:{$meta: "textScore"}}

在全文搜索的格式中加入这样一个条件,如下:

db.imooc_2.find({$text:{$search:"aa bb"}},{score:{$meta:"textScore"}})

搜索出的结果会多出一个score字段,这个得分越高,相关度越高。 
还可以对查询出的结果根据得分进行排序:

db.imooc_2.find({$text:{$search:"aabb"}},{score:{$meta:"textScore"}}).sort({score:{$meta:"textScore"}})

加上.sort方法即可。

全局索引的限制: 
1.每次查询,只能指定一个$text查询 
2.$text查询不能出现在$nor查询中 
3. 查询中如果包含了$text, hint不再起作用(强制指定索引hint) 
4. MongoDB全文索引还不支持中文

7:地理位置索引 
将一些点的位置存储在MongoDB中,创建索引后,可以按照位置来查找其他点。

地理位置索引分为两类: 
1.2D索引,用于存储和查找平面上的点。 
2.2Dsphere索引,用于存储和查找球面上的点。 
例如: 
查找距离某个点一定距离内的点。 
查找包含在某区域内的点。

分为2种:2D平面地理位置索引和 2D sphere 2D球面地里位置索引 2者的区别在于计算距离时使用的计算方式不同(平面距离还是球面距离)

2D地理位置索引创建方式

db.collection.ensureIndex({w:”2d”})

2D地理位置索引的取值范围以及表示方法经纬度[经度,纬度] 
经纬度取值范围 经度[-180,180] 纬度[-90,90]

db.collection.insert({w:[180,90]})

2D地理位置查询有2种 
1.使用$near 查询距离某个点最近的点 ,默认返回最近的100个点

db.collection.find({w:{$near:[x,y]}})

可以使用$maxDistance:x 限制返回的最远距离

db.collection.find({w:{$near:[x,y],$maxDistance:z}})

2.使用$geoWithin 查询某个形状内的点 
形状的表示方式:

1. $box 矩形,使用{$box:[[x1,y1],[x2,y2]]}

2. $center 圆形,使用 {$center:[[x,y],r]}

3. $polygon 多边形,使用 {$polygon:[[x1,y1],[x2,y2],[x3,y3]]}

mongodb geoWithin 查询

查询矩形中的点

db.collection.find({w:{$geoWithin:{$box:[[0,0],[3,3]]}}})

查询圆中的点

db.collection.find({w:{$geoWithin:{$center:[[0,0],5]}}})

查询多边形中的点

db.collection.find({w:{$geoWithin:{$polygon:[[0,0],[0,1],[2,5],[6,1]]}}})

mongodb geoNear 查询

geoNear 使用 runCommand命令操作

db.runCommand({

geoNear:"collection名称",

near:[x, y],

minDistance:10(对2d索引无效,对2Dsphere有效')

maxDistance:10

num:1 返回数量

})

可返回最大距离和平均距离等数据.

返回的数据: 
results:查询到的数据;dis:距离,obj:数据记录 
stats:查询参数,maxDistance最大距离和avgDistance平均距离 
ok:1,查询成功

mongodb 2Dsphere索引详解

2Dsphere index create method 
use command:

db.collection.ensureindex({key: '2dsphere'})

2Dsphere位置表示方式: 
GeoJSON:描述一个点,一条直线,多边形等形状。 
格式:

{type:'', coordinates:[list]}

GeoJSON查询可支持多边形交叉点等,支持MaxDistance 和 MinDistance

索引的属性

创建索引的格式:

db.collection.ensureIndex({indexValue},{indexProperty})

其中:indexProperty比较重要的有 
1:名字

db.collection.ensureIndex({indexValue},{name:})

MongoDB会自动的创建,规则是key_1 或者 key_-1 1或者-1代表排序方向,一般影响不大,长度一般有限制125字节 
为了见名知意我们可以自己来命名

自定义索引名称方式:

db.imooc_2.ensureIndex({x:1,y:1,z:1,m:1},{name:"normal_index"})

删除索引

db.imooc_dropIndex(indexName)

删除索引时,可以通过我们定义的名字来删除索引

db.imooc_2.dropIndex("normal_index")

2:唯一性:不允许在同一个集合上插入具有同一唯一性的数据。

db.imooc_2.ensureIndex({x:1,y:1,z:1,m:1},{unigue:true)

3:稀疏性

db.collection.ensureIndex({},{sparse:true/false}) #指定索引是否稀疏

MongoDB索引默认是不稀疏的。 
稀疏性的不同代表了MongoDB在处理索引中存在但是文档中不存在的字段的两种不同的方法。 
例如,我们为一个collection的x字段指定了索引,但这个collection中可以插入如{y:1,z:1}这种不存在x字段的数据, 
如果索引为不稀疏的,mongodb依然会为这个数据创建索引,如果在创建索引时指定为稀疏索引,那么就可以避免这件事情发生了。

db.imooc_2.insert({"m":1})

db.imooc_2.insert({"n":1})

通过$exists可以判断字段是否存在,如

db.imooc_2.find({m:{$exists:true}}) #筛选出有m字段的文档

给这个文档的m字段创建一个稀疏索引:

db.imooc_2.ensureIndex({m:1},{sparse:true})

第二条文档不存在m字段,所以不会创建这个索引 
如果使用稀疏索引查找不存在稀疏索引字段的文档,mongodb则不会使用这个索引查找 
例如:

db.imooc_2.find({m:{$exists:false}}) #可以查到数据

但如果我们通过hint强制使用索引,就不会查到数据了

db.imooc_2.find({m:{$exists:false}}).hint("m_1") #查不出数据,因为n上并没有m字段的索引

聚合

       MongoDB中的聚合aggregate主要用于处理数据计算。

聚合框架

使用聚合框架可以对集合中的文档进行变换和组合。基本上,可以用多个构件创建一个管道(pipeline),用于对一连串的文档进行处理。这些构架包括筛选(filtering)、投射(projecting)、分组(grouping)、限制(limiting)和跳过(skipping)。

例如一个保存着动物类型的集合,希望找出最多的那种动物,假设每种动物被保存为一个mongodb文档,可以按照以下步骤创建管道。

1)将每个文档的动物名称映射出来。

2)安装名称排序,统计每个名称出现的次数。

3)将文档按照名称出现的次数降序排列。

4)将返回结果限制为前五个。

具体操作符:

1){"$porject",{"name" : 1}}

类似于查询阶段的字段选择器,指定"fieldname": 1选定需要的字段,"fieldname" : 0排除不需要的字段,"_id"字段自动显示。结果保存在内存中,不会写入磁盘。

db.test_collection.aggregate({"$project" : {"name" : 1}});    =>

{ "_id" : ObjectId("535a2d3c169097010b92fdf6"), "name" : "snake" }

2){"$group", {"_id" : "$name","count" : {"$sum" : 1}}}

首先指定了分组的字段"name",该操作执行完后,每个name只对应一个结果,所有可以将name指定为唯一标识符"_id"。第二个字段表明分组内的每个文档"count"字段加1。新加入的文档中不会有count字段。

db.test_collection.aggregate({"$project" : {"name" : 1}}, {"$group" : {"_id" : "$name", "count" : {"$sum" : 1}}});

=>

{ "_id" : "bird", "count" : 8344 }

{ "_id" : "snake", "count" : 8443 }

{ "_id" : "cat", "count" : 8183 }

{ "_id" : "rabbit", "count" : 8206 }

{ "_id" : "tiger", "count" : 8329 }

{ "_id" : "cow", "count" : 8309 }

{ "_id" : "horse", "count" : 8379 }

{ "_id" : "dog", "count" : 8406 }

{ "_id" : "dragon", "count" : 8372 }

{ "_id" : "elephant", "count" : 8264 }

{ "_id" : "pig", "count" : 8403 }

{ "_id" : "lion", "count" : 8362 }

3){"$sort" :{"count" : -1}}

对结果集中的文档根据count字段做降序排列。

4){"$limit" : 5}

将返回结果限制为5个文档。

将上述结果综合起来:

 

db.test_collection.aggregate(

{

"$project" : {"name" : 1}},

{"$group" : {"_id" : "$name", "count" : {"$sum" : 1}}},

{"$sort" : {"count" : -1}},

{"$limit" : 5}

);

aggregate会返回一个文档数组,内容为出现次数最多的5个动物:

{ "_id" : "snake", "count" : 8443 }

{ "_id" : "dog", "count" : 8406 }

{ "_id" : "pig", "count" : 8403 }

{ "_id" : "horse", "count" : 8379 }

{ "_id" : "dragon", "count" : 8372 }

调试过程中。可以逐一对管道符进行排查。
聚合框架不能对集合进行写入操作,所有结果返回给客户端,聚合结果必须限制在16M以内。

管道操作符

每个操作符都会接受一连串的文档,对这些文档进行类型转换,最后得到的文档作为结果传递给下一操作符。

不同的管道操作符可以将任意顺序组合在一起使用,而且可以被重复任意多次。

1、 $match

$match用于对文档集合进行筛选,之后得到的文档子集做聚合。

"$match"支持所有的常规查询操作符("$gt","$lt","$ne")等,不能使用地理空间操作符。

实际操作中尽量将"$match"放在管道的前面部分,一方面可以提快速将不需要的文档过滤掉,另外在映射和分组前筛选,查询可以使用索引。

2、 $project

使用"$project"可以提取字段,可以重命名字段,

db.foo.aggregate({"$project" : {"city" : 1, "_id" : 0}})    =>

{ "city" : "NEW WORK" }

可以将投射过的字段重命名:

db.foo.aggregate({"$project" : {"newcity" : "$city", "_id" : 0}})    =>

{ "newcity" : "NEW WORK" }

使用"$fieldname"语法为了在聚合框架中引用fieldname字段,例如上面"$city"会被替换为"NEW WORK"。

对字段重命名后,Mongdb不会记录其记录字段的历史名称,所以应该在修改字段名称前使用索引。

数学表达式

数学表示式用来操作数据运算。

db.foo.aggregate(

  {"$project" :

    {"total" :

      {"$add" : ["$age", "$year"]},

      "_id" : 0

    }

  }

)

{"total" : 15}

可以将多个表达式组合为更为复杂的表达式:

db.foo.aggregate(

  {"$project" :

    {"sub" :

      {"$subtract" : [{"$add" : ["$age", "$year"]}, 7]},

      "_id" : 0

    }

  }

)

{ "sub" : 8 }

操作符语法:

1)将表达式相加:"$add" : [expr1,[, expr2, ..., exprN]]

2)达式1减去表达式2:"$subtract": [expr1, expr2]

3)将表达式相乘:"$multiply" :[expr1, [, expr2, ..., exprN]]

4)表达式1除以表达式2得到商:"$divide": [expr1, expr2]

5)表达式1除以表达式2得到余数:"$mod": [expr1, expr2]

日期表达式

用于提取日期信息的表达式:"$year","$month","$week","$dayOfMonth","$dayOfweek","$hour","$minute","$second"。只能对日期类型的字段进行日期操作,不能对数值类型进行日期操作。

db.bar.insert({"name" : "pipi", "date" : new Date()})

db.bar.aggregate(

  {"$project" :

    {"birth-month" :

      {"$month" : "$date"},

      "_id" : 0

    }

  }

)

{ "birth-month" : 4 }

也可以使用字面量日期。

db.bar.aggregate(

  {"$project" :

    {"up-to-now" :

      {"$subtract" : [{"$minute" : new Date()}, {"$minute" : "$date"}]},

      "_id" : 0

    }

  }

)

{ "up-to-now" : 18 }

字符串表达式

操作符语法:

1)"$substr": [expr,startOffset, numoReturn]

接受字符串,起始位置以后偏移N个字节,截取字符串。

2)"$concat": [expr1[,expr2, ..., exprN]]

将给定的表达式连接在一起作为返回结果。

3)"$toLower": expr

返回参数的小写形式

4)"$toUpper": expr

返回参数的大写形式

例如:

db.foo.insert({"firstname" : "caoqing", "lastname" : "lucifer"})

db.foo.aggregate(

{

  "$project" : {

    "email" : {

      "$concat" : [

        {"$substr" : ["$firstname", 0, 1]},

        ".",

        "$lastname",

        "@gmail.com"

          ]

        },

        "_id" : 0

      }

  }

)

{ "email" : "c.lucifer@gmail.com" }

逻辑表达式

操作符语法:

1)"$cmp": [expr1,expr2]

比较两个参数,相等返回0,大于返回整数,小于返回负数。

2)"$strcasecmp": [string1,string2]

比较字符串,区分大小写

3)"$eq"/"$ne"/"$gt"/"$gte"/"lt"/"lte": [expr1,expr2]

比较字符串,返回结果(true or false)

4)"$and": [expr1[,expr2, ..., exprN]]

所有值为true返回true,否则返回false。

5)"$or": [expr1[,expr2, ..., exprN]]

任意表达式为true返回true,否则返回false

6)"$not": expr

对表示式取反

还有两个控制语句

"$cond": [booleanExpr,trueExpr, falseExpr]

如果为true,返回trueExpr,否则,返回falseExpr。

"$ifFull": [expr,replacementExpr]

如果expr为null,返回replacementExpr,否则返回expr。

通过这些操作符,就可以在聚合中使用更复杂的逻辑,可以对不同数据执行不同的代码,得到不同的结果。

管道对于输入数据的形式有特定要求,所以这些操作符在传入数据时要特别注意。算术操作符必须接受数值,日期操作符必须接受日期,字符串操作符必须接受字符串。如果有字符缺失,这些操作符就会报错。如果你的数据及不一致,可以通过这个条件来检测缺失的值,并且进行填充。

7)一个提取的例子

假如有个教授想通过某种比较复杂的计算为学生打分:出勤率占10%,平时作业占30%,考试成绩占60%(如果是老师宠爱的学生,那么分数就是100分)。可以使用如下代码:

分组去重

Distinct

Distinct用来找出给定键的所有不同值。使用时必须指定集合和键。

假设集合中有如下文档:

如果对“age”键使用distinct,会得到所有不同的年龄:

Group

使用group可以执行更复杂的聚合。先选定分组所依据的键,而后MongoDB就会降级和依据选定键的不同值分成若干组。然后可以对每一个分组内的文档进行聚合,得到一个结果文档。

插入示例数据:

var name = ["Caoqing", "Spider-man", "Garfield"]

for (var i = 0; i < 10000; i++) {

  iname = name[Math.floor(Math.random() * name.length)];

  date = new Date().getTime();

  number = Math.floor(100 * Math.random());

  db.coll.insert({_id : i, name : iname, time : date, age : number});

}

生成的列表中包含最新的时间和最新的时间对应的年纪。
可以安装name进行分组,然后取出每个分组中date最新的文档,将其加入结果集。

db.runCommand({"group" : {

  "ns" : "coll",

  "key" : {"name" : true},

  "initial" : {"time" : 0},

  "$reduce" : function(doc, prev) {

    if (doc.time > prev.time) {

      prev.age = doc.age;

      prev.time = doc.time;

    }

  }

}})

(1)"ns": "coll"
指定进行分组的集合。
(2)"key" :{"name" : true}
指定分组依据的键。
(3)"initial" :{"time" : 0}
初始化time值,作为初始Wednesday传递给后续过程。每组成员都会使用这个累加器。

如果有文档不存在指定分组的键,这些文档会单独分为一组,缺失的键会使用name:null这样的形式。如下:

db.coll.insert({age : 5, time : new Date().getTime()})

返回结果:

   {

      "name" : null,

      "time" : 1399180685288,

      "age" : 5

    }

    "count" : 10001,

    "keys" : 4,

    ...

为了排除不包含指定用于分组的键的文档,可以在"condition"中加入"name":{"$exists" : true}。

db.runCommand({"group" : {

  "ns" : "coll",

  "key" : {"name" : true},

  "initial" : {"time" : 0},

  "$reduce" : function(doc, prev) {

    if (doc.time > prev.time) {

      prev.age = doc.age;

      prev.time = doc.time;

    }

  },

  "condition" : {"name" : {"$exists" : true}}

}})

使用完成器
完成器(finalizer)用于精简从数据库传到用户的数据,因为group命令的输出结果需要能够通过单次数据库响应返回给用户。

将函数作为键使用
分组条件可以非常复杂,不是单个键,例如分组时按照类别分组dog和DOG是两个完全不同的组,为了消除大小写差异,可以定义一个函数决定文档分组所依据的键。
定义分组函数需要用到"$keyf"键,

db.foo.group({

  "ns" : "foo",

  "$keyf" : function(x) { return x.category.toLowerCase(); };

  "initial" : ...,

  ......

})

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值