Jade Dungeon

MongoDB

定位

MongoDB的写入性能是比较差,因为全局锁+mmap的磁盘IO处理方式,线上测试情况, 每秒只能在200~300次的写入(UPS)性能,这样的一个特性决定了它更适合做离线业务, 在线业务方面很多项目都吃亏很多(包括我们之前项目)。比较好的解决方案是采用 TokuMX发行版来替换原生MongoDB,写入性能能够提升20~30倍

TokuMX

文档 > 集合 > 数据库

管理

帮助文件

  • 在客户端中输入help查看Shell的帮助文档。
  • 使用db.help()查看数据库级别的帮助文档。
  • 使用db.集合名称.help()查看集合相关的文档。
  • 输入函数时不输入(),就可以查看函数的源代码,了解细节了。
  • http://api.mongodb.org/js可以看到Shell相关的JavaScript函数API。

启动服务

#!/bin/bash
cd /opt/morganstudio/database/mongodb
bin/mongod --dbpath /media/file-data/mongodb-data/data --logfile /var/mongo.log

默认监听27017端口,28017也有HTTP服务可以查看服务状态。

以修复模式启动

mongod --repair

实际很可能数据库数据损坏或数据状态不一致,导致无法正常启动MongoDB服务器,根据 启动信息可以看到需要进行修复。或者执行:

mongod -f /etc/mongodb.conf --repair

以认证模式启动

  1. 和用户管理相关的操作基本都要在admin数据库下运行,要先use admin;
  2. 如果在某个单一的数据库下,那只能对当前数据库的权限进行操作;
  3. db.addUser是老版本的操作,现在版本也还能继续使用,创建出来的user是带有root role的超级管理员。
添加用户
use admin
db.createUser( {
    user: "buru",
    pwd: "12345678",
    roles: [{role: "userAdminAnyDatabase", db: "admin"}]
  })

切换到admin下,查看刚才创建的用户:

show users

// 或

db.system.users.find()
重启mongo服务

怎么关闭 mongoDB?千万不要kill -9 pid,可以kill -2 piddb.shutdownServer()

下面使用 –auth 参 数,重新启动 mongoDB:

mongod --auth -f mongodb-linux-i686-3.0.0/mongodb.conf
以验证方式登录

再次打开 mongo shell:

mongo
use admin
db.auth("buru","12345678") #认证,返回1表示成功

或

mongo -u buru -p 12345678 --authenticationDatabase admin

此时show collections报错,因为没有权限。用户buru只有「用户管理」的权限。

创建普通用户

下面创建用户,用户都跟着库走,创建的用户都是

use databaseName1
db.createUser({user: "bao", pwd: "12345678",
   roles: [{role: "readWrite", db: "databaseName1" }]
})

use databaseName2 
db.createUser({
   user: "bao", pwd: "12345678",
   roles: [{role: "read",      db: "databaseName2" },]
})
   
use databaseName3
db.createUser({ user: "bao", pwd: "12345678",
   roles: [{role: "dbOwner" }]
})

use databaseName2 
db.createUser({user: "bao",pwd: "12345678",
   roles: [{role: "root" }]
})

查看刚刚创建的用户。

show users

{ "_id" : "tianhe.bao", "user" : "bao", "db" : "tianhe",
  "roles" : [
    { "role" : "readWrite", "db" : "tianhe" },
    { "role" : "read", "db" : "tianhe2" } ]
}

查看整个mongoDB全部的用户:

use admin
db.system.users.find()

{ "_id" : "admin.buru", "user" : "buru", "db" : "admin", "credentials" : 
	{ "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "gwVwuA/dXvxgSHavEnlyvA==", "storedKey" : "l2QEVTEujpkCuqDEKqfIWbSv4ms=", "serverKey" : "M1ofNKXg2sNCsFrBJbX4pXbSgvg=" } }, "roles" : [ 
	{ "role" : "userAdminAnyDatabase", "db" : "admin" } ] }
{ "_id" : "tianhe.bao", "user" : "bao", "db" : "tianhe", "credentials" : 
	{ "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "//xy1V1fbqEHC1gzQqZHGQ==", "storedKey" : "ZS/o54zzl/FdcXLQJ98KdAVTfF0=", "serverKey" : "iIpNYz2Gk8KhyK3zgz6muBt0PI4=" } }, "roles" : [ 
	{ "role" : "readWrite", "db" : "tianhe" }, { "role" : "read", "db" : "tianhe2" } ] }
切换到对应的用户

先auth:

db.auth("bao","12345678")

或

mongo -u bao -p 12345678 --authenticationDatabase admin
创建一个不受访问限制的超级用户
use admin
db.createUser({user:"superuser", pwd:"pwd", roles:["root"]})
修改密码
use admin
db.changeUserPassword("username", "xxx")
查看用户信息
db.runCommand({usersInfo:"userName"})
修改密码和用户信息
db.runCommand({updateUser:"username", pwd:"xxx", customData:{title:"xxx"}})

终止服务器进程

db.shutdownServer()

终止数据库服务器进程。或者,可以直接kill掉mongod进程即可。

备份

1、备份全部数据库

mkdir testbak
cd testbak
mongodump
说明:默认备份目录及数据文件格式为./dump/[databasename]/[collectionname].bson

2、备份指定数据库

mongodump -d pagedb

说明:备份数据库pagedb中的数据。

3、备份一个数据库中的某个集合

mongodump -d pagedb -c page

说明:备份数据库pagedb的page集合。

4、恢复全部数据库

cd testbak
mongorestore --drop

说明:将备份的所有数据库恢复到数据库,--drop指定恢复数据之前删除原来数据库数据 ,否则会造成回复后的数据中数据重复。

5、恢复某个数据库的数据

mongorestore --drop -d pagedb --directoryperdb ./dump/pagedb

说明:将备份的pagedb的数据恢复到数据库。

6、恢复某个数据库的某个集合的数据

cd testbak
mongorestore --drop -d pagedb -c page --directoryperdb ./dump/pagedb

说明:将备份的pagedb的的page集合的数据恢复到数据库。

7、向MongoDB导入数据

mongoimport -d pagedb -c page --type csv --headerline --drop < csvORtsvFile.csv

说明:将文件csvORtsvFile.csv的数据导入到pagedb数据库的page集合中, 使用cvs或tsv文件的列名作为集合的列名。需要注意的是,使用--headerline选项时, 只支持csv和tsv文件。

--type支持的类型有三个:csv、tsv、json

具体查看mongoimport --help

8、从向MongoDB导出数据

mongoexport -d pagedb -c page -q {} -f _id,title,url,spiderName,pubDate \
	--csv > pages.csv

说明:将pagedb数据库中page集合的数据导出到pages.csv文件,其中各选项含义:

  • -f指定cvs列名为_id,title,url,spiderName,pubDate
  • -q指定查询条件

注意: 如果上面的选项-q指定一个查询条件,需要使用单引号括起来,如下所示:

mongoexport -d page -c Article -q '{"spiderName": "mafengwoSpider"}' \ 
	-f _id,title,content,images,publishDate,spiderName,url \
	--jsonArray > mafengwoArticle.txt  

具体查看帮助:mongoexport --help

登录

#!/bin/bash
/opt/morganstudio/database/mongodb/bin/mongo mysql.local-vm:27017 

默认连接test数据库。client中通过help查看最顶层的帮助文档:

> help
HELP
   Show dbs                 显示数据库名
   show collections         显示当前数据库中的集合集
   show users               显示当前数据库的用户
   show profile             显示最后系统用时大于1ms的系统概要
   use  <db name>           切换到数据库
   db.help()                help on DB methods
   db.foo.help()            help on collection methods
   db.foo.find()            list objects in collection foo
   db.foo.find({a : 1})     list objects in foo where a == 1
   it                       迭代下一页记录

带用户名与密码:

mongo -u admin -p admin 192.168.0.197:27017/pagedb  

基于MongoDB支持的javascript实现远程连接

当你已经连接到一个远程的MongoDB数据库服务器 (例如,通过mongo连接到192.168.0.184), 现在想要在这个会话中连接另一个远程的数据库服务器(192.168.0.197), 可以执行如下命令:

var x = new Mongo('192.168.0.197:27017')  
var ydb = x.getDB('pagedb');  
use ydb  
ydb.page.findOne()

上述通过MongoDB提供的JavaScript脚本,实现对另一个远程数据库服务器进行连接, 操作指定数据库pagedb的page集合。

如果启用了安全认证模式,可以在获取数据库连接实例时,指定认证账号,例如:

var x = new Mongo('192.168.0.197:27017')  
var ydb = x.getDB('pagedb', 'shirdrn', '(jkfFS$343$_\=\,.F@3');  
use ydb  

数据库操作

基础操作

查看帮助:

db.help()

显示当前数据库服务器上的数据库

show dbs

切换数据库(如果数据库不存在直接创建):

use dbName

查看数据库服务器的状态:

db.serverStatus()  

查询指定数据库统计信息

use fragment
db.stats()

查询指定数据库的集合当前可用的存储空间

db.baseSe.storageSize()

查询指定数据库的集合分配的存储空间

db.baseSe.totalSize()

删除数据库

db.dropDatabase()

修复数据库

db.repairDatabase()

设置记录profiling,0=off, 1=slow,2=all

db.setProfilingLevel(1)

查看profiling

show profile

拷贝数据库

db.copyDatabase('mail_addr','mail_addr_tmp')

加锁

不允许执行写数据操作,一般在执行数据库备份时有用。

db.runCommand({fsync:1,lock:1})

{  
	"info" : "now locked against writes, use db.$cmd.sys.unlock.findOne() to unlock",  
	"ok" : 1  
}  

查看当前锁状态

db.currentOp()
{  
     "inprog" : [ ],  
     "fsyncLock" : 1,  
     "info" : "use db.$cmd.sys.unlock.findOne() to terminate the fsync write/snapshot lock"  
}  

返回信息中的fsyncLock为1表示MongoDB的fsync进程(负责将写入改变同步到磁盘) 不允许其他进程执行写数据操作

解锁

use admin
db.$cmd.sys.unlock.findOne()

集合操作

显示数据库中所有的集合(collection)

show collections

询指定数据库包含的集合名称列表

db.getCollectionNames()

成员访问

通过.直接访问数据库的成员,先找成员,没有再返回集合。:

db.version           // version成员方法
db.blog              // blog集合

通过getCollection("collectionName")方法,只取得集合:

db.version           // version集合,不是方法

遍历集合,因为JavaScript中x.yx['y']等价,所以:

var collection = ["aa","bb","cc"];

for (i in collections) {
	doStuff(db.blog[collections[i]])
}

而不是:

doStuff(db.blog.aa);
doStuff(db.blog.bb);
doStuff(db.blog.cc);

成员方法

查看集合帮助

db.songs.help();

查看所有集合的状态

db.printCollectionStats();

查看当前集合状态

db.music.stats();

集合重命名

db.music.renameCollection("musics");

查看集合总数据量

db.songs.count();

查看表空间大小

db.songs.dataSize();

查看集合所在数据库

db.songs.getDB();

创建集合(集合中必须产生过数据)

db.music;
db.music.insert({"id":1,"name":"love","orders":1});
db.music.count();

集合复制

db.musics.copyTo("music");

删除集合

db.musics.drop();

清空集合

db.musics.remove(); 

重命名字段

db.musics.update({}, {$rename : {"id" : "m_id"}}, false, true);

查看不存在id字段的文档数量

db.musics.find({"id":{$exists:false}}).count();;
db.musics.find({"id":{$ne:null}}).count();;

数据类型

ObjectId

_id类型默认为ObjectId。12 Byte格式:

0 1 2 3 4 5 6 7 8 9 10 11
时间戳 主机 PID 计数器

推荐显式提供_id,减轻数据库生成ID的负担。

数字类型

默认会把类型转为float,所以整数要写明是NumberInt(0), 长整形要写明是NumberLong(0)

例:

db.data.update({'name': 'zero'}, {'$set': {'value': NumberInt(0)}})

日期

类型是Date,创建时一定要调用new Date( ... )来创建。用Date( ... )创建出来的 是一个字符串而不是日期。

数组

{"favorite book" : ["cat's cradle", "foundation trilogy", "ender's game"]}

CRUD

  • MongoDB默认CRUD操作都是异步的,也不关心返回结果。
  • 为了保证操作成功,完成操作以后用getLastError命令检查。

插入

插入单个文档

db.foo.insert({"bar":"baz"})
  • 单个文档最大大4MB(《战争与和平》也只有3.14MB)。
  • 启动mongodb时用--objectCheck选项打开文档结构检查(会影响性能)。

批量插入

  • 单个TCP请求插入多个文档到一个集合可节约重复的开销。
  • 不能把多个文档批量插入到多个集合里。
  • 如果目标是导入数据库,不应该用批量插入,而应该用mongoimport等工具。

删除

db.foo.remove({"opt-out":true})

如果要删除集合里所有的数据,直接删除集合比批量删除更加快。 但是要注意这样会把索引也一直删除。

更新文档:update

update方法是原子性的,它有两个参数:

  1. 查询条件。
  2. 修改器(modifier)。

替换整个文档

如果要对一个文档做出比较大的修改,可以用$update替换掉整个文档:

var joe = db.blog.findOne({"_id":ObjectId("53b426cd024df0e8f06f5921")})

joe.title = "Article 00";

db.blog.update({"_id":ObjectId("53b426cd024df0e8f06f5921")}, joe)

使用修改器修改文档的属性

修改器:set 更新属性的值

$set用来指定一个key的值(如果没有就创建):

> db.blog.find();
{ "_id" : ObjectId("53b55a9c35116a9ce3cc1933"), "title" : "Article 01" }

// 创建新的key:
> db.blog.update({"_id" : ObjectId("53b55a9c35116a9ce3cc1933")}, 
		{"$set" : {"author.name" : "Jade"}})

// 改变key的值:
> db.blog.update({"_id" : ObjectId("53b55a9c35116a9ce3cc1933")}, 
		{"$set" : {"author.email" : "Jade@mailserver.com"}})

// key值的类型也可以改
> db.blog.update({"_id" : ObjectId("53b55a9c35116a9ce3cc1933")}, 
		{"$set" : {"author.email" : [
				"Jade@mailserver.com", 
				"Adda@mailserver.com", 
				"Teo@mailserver.com"]}})
修改器:unset去除属性
> db.blog.find();
{ "_id" : ObjectId("53b55a9c35116a9ce3cc1933"), "title" : "Article 01", 
	"author" : { "name" : "Jade", "email" : "Jade@mailserver.com" } }

> db.blog.update({"_id" : ObjectId("53b55a9c35116a9ce3cc1933")}, 
		{"$unset" : {"author.email" : ""}})

> db.blog.update({"_id" : ObjectId("53b55a9c35116a9ce3cc1933")}, 
		{"$unset" : {"author.name" : ""}})

> db.blog.update({"_id" : ObjectId("53b55a9c35116a9ce3cc1933")}, 
		{"$unset" : {"author" : ""}})

> db.blog.find();
{ "_id" : ObjectId("53b55a9c35116a9ce3cc1933"), "title" : "Article 01" }
修改器:inc增加数字

原子增加操作,只作用于数字。也可以用来创建一个原来没有的属性。

> db.blog.update({"_id" : ObjectId("53b55a9c35116a9ce3cc1933")}, 
		{"$inc" : {"score" : 1}})

> db.blog.find();
{ "_id" : ObjectId("53b55a9c35116a9ce3cc1933"), 
	"title" : "Article 01", "score" : 1 }
数组修改器:push添加一个元素

回到到尾部,如果没有会新建一个数组:

> db.blog.update({"_id" : ObjectId("53b55aa135116a9ce3cc1935")}, 
		{$push : {"comments" : {"user" : "aa", "content" : "aaa"}}})
> db.blog.update({"_id" : ObjectId("53b55aa135116a9ce3cc1935")}, 
		{$push : {"comments" : {"user" : "bb", "content" : "bbb"}}})

> db.blog.find()
{ "_id" : ObjectId("53b55aa135116a9ce3cc1935"), "title" : "Article 03", 
	"comments" : [ { "user" : "aa", "content" : "aaa" }, 
								{ "user" : "bb", "content" : "bbb" } ] }
数组修改器:addToSet

$addToSet在不重复的情况下添加:

> db.test.find()
{ "_id" : ObjectId("5617833823d1f1ede8d0f59c"), 
	"user" : "jade", "books" : [ "book1", "book2", "book3" ] }

> db.test.update({"user":"jade"},{"$addToSet": {"books":"book4"}});

> db.test.find()
{ "_id" : ObjectId("5617833823d1f1ede8d0f59c"), 
	"user" : "jade", "books" : [ "book1", "book2", "book3", "book4" ] }

这样的操作相当于检查成员不存在(用$ne检查),然后再添加:

> db.test.find();
{ "_id" : ObjectId("5617833823d1f1ede8d0f59c"), "user" : "jade", 
	"books" : [ "book1", "book2", "book3", "book4", "book5" ] }

> db.test.update({"books": {"$ne": "book3"}}, {$push: {"books": "book3"}});

> db.test.find();
{ "_id" : ObjectId("5617833823d1f1ede8d0f59c"), "user" : "jade", 
	"books" : [ "book1", "book2", "book3", "book4", "book5" ] }

> db.test.update({"books": {"$ne": "book6"}}, {$push: {"books": "book6"}});

> db.test.find();
{ "_id" : ObjectId("5617833823d1f1ede8d0f59c"), "user" : "jade", 
	"books" : [ "book1", "book2", "book3", "book4", "book5", "book6" ] }

\(addToSet组合\)each一次添加多个:

> db.test.update({"user":"jade"}, 
		{"$addToSet": {"books": 
			{"$each":["book1", "book3", "book5","book6"]}}});

> db.test.find()
{ "_id" : ObjectId("5617833823d1f1ede8d0f59c"), 
	"user" : "jade", 
	"books" : [ "book1", "book2", "book3", "book4", "book5", "book6" ] }
数组修改器:pop删除一个元素
  • {$pop : {key : 1}}:从数组尾部删除一个元素。
  • {$pop : {key : -1}}:从数组头部删除一个元素。
数组修改器:pull 按条件删除元素

$pull可以根据条件来删除元素,而不是根据位置:

db.lists.insert({"todo":[1, 1, 2, 1]})

如果"$pull" : 1,就只剩下2了:

db.lists.update({}, {"$pull": 1})
数组定位修改器

数组下标从0开始,可以用下标来选择元素:

> db.blob.posts.findOnd()
{
	"_id" : ...... ,
	"content" : .... ,
	"comments" : [
		{ "comment" : "good post", "author" : "John",    "votes" : 3 },
		{ "comment" : "good post", "author" : "Adda",    "votes" : 2 },
		{ "comment" : "good post", "author" : "Freeman", "votes" : 1 }
	]
}

使用下标0给第一个评论增加投票:

> db.blog.update({"post" : post_id}, {"$inc" : {"comments.0.votes" : 1}})

在不知道下标的时候,使用定位符$匹配符合条件的记录。

比如修改用户名为John的第一条评论:

> db.blog.update({"comments.author" : "John"}, 
		{"$set" : {"comments.$.author" : "Jim}})

注意:定位符只会匹配查找到的第一条记录。

修改器的性能

对于某些不需要改变文档大小的修改器,运行速度是非常快的。例如:

from pymongo import Connection

import time

db = Connection().performace_test
db.drop_collection("update")
collection = db.update

collection.insert({"x": 1})

# make shure insert is complete before timing
collection.find_on3()

start = time.time()

for i in range(100000):
	collection.update({}, {"$inc": {"x": 1}})

# make sure the updates are complete before we stop timing
collection.find_one()

print time.time() - start

如果是push这类会改变文档大小的操作,速度就差了很多:

for i in range(100000):
	collection.update({}, {"$push": {"x": 1}})

在很多场景下,\(push还是合适的修改器,如果\)push让性能成为瓶颈, 可以把内嵌的数组独立出来,作为一个新的集合。

更新或新增

upsert

update函数可以通过第三个函数(true)来指定如果没有符合条件的,就创建新的文档。 这操作是原子性的:

> db.test.update({"user": "Neo"}, {"$inc": {"count": 3}}, true);
WriteResult({
        "nMatched" : 0,
        "nUpserted" : 1,
        "nModified" : 0,
        "_id" : ObjectId("561a071212acab473f0bc93a")
})

> db.test.find();
{ "_id" : ObjectId("561a071212acab473f0bc93a"), "user" : "Neo", "count" : 3 }
save程序

save是一个shell函数,当指定文档不存在时插入,如果已经存在就更新。

save函数只有一个参数,就是文档。如果文档有_id,执行upsert, 否则就执行插入。

> var x = db.test.findOne();

> x.num = 42

> db.test.save(x)

> db.test.find();
{ "_id" : ObjectId("5617833823d1f1ede8d0f59c"), "user" : "jade", 
	"books" : [ "book1", "book2", "book3", "book4", "book5", "book6" ], 
	"num" : 42 }

更新多个文档

默认update只更新第一个符合的记录,如果要更新所有匹配的文档,设置update函数 的第4个参数为true

注意:

  • 为了防止以后默认的行为变了默认更新多个,建议每次都指定第3第4个参数。
  • 多文档更新只用用于$开关的修改器工具。

命令getlastError可以用来查看空间更新了多少条记录,属性n的值就是数量:

> db.test.update({"user": "Neo"}, {$inc: {"count": 2}}, false, true);

> db.runCommand({getLastError: 1});
{
        "connectionId" : 1,
        "updatedExisting" : true,     // 是对已有记录的修改
        "n" : 3,                      // 更新了3条记录
        "syncMillis" : 0,
        "writtenTo" : null,
        "err" : null,
        "ok" : 1
}

返回已经更新的文档:findAndModify

update函数不管执行是否成功,而命令findAndModify会等待数据库响应, 还具有原子性。

格式:

ps = db.runCommand({
		"findAndModify" : "collName",      // 集合名称,字符串类型
		"query"         : { ... },         // 查询条件
		"sort"          : { ... },         // 结果排序方式
		"update"        : { ... },         // 修改器
		"new"           : true/false,      // 是否删除文档
		"remove"        : true/false       // 返回的是新文档还是更新前的文档
	})

例子:

有一个进程数据库processes结构:

  • status:状态,READYRUNNINGDONE
  • priority:优先级,数值型。

程序逻辑:

  • 把所以READY的进程按优先级排序。
  • 把最高优先级的更新为RUNNING,运行进程函数。
  • 运行完以后,更新状态为DONE
> db.processes.find();
{ "_id" : ObjectId("561a1580d9352240db58ad03"), "status" : "READY", "priority" : 1 }
{ "_id" : ObjectId("561a1583d9352240db58ad04"), "status" : "READY", "priority" : 2 }
{ "_id" : ObjectId("561a1586d9352240db58ad05"), "status" : "READY", "priority" : 3 }
{ "_id" : ObjectId("561a1588d9352240db58ad06"), "status" : "READY", "priority" : 3 }
{ "_id" : ObjectId("561a158ad9352240db58ad07"), "status" : "READY", "priority" : 2 }
{ "_id" : ObjectId("561a1590d9352240db58ad08"), "status" : "READY", "priority" : 5 }
> var ps = db.runCommand({"findAndModify": "processes",
... "query": {"status": "READY"},
... "sort" : {"priority": -1},
... "update" : {"$set": {"status": "RUNNING"}}}).value

> do_something(ps)        // 执行进程任务

> db.process.update({"_id": ps._id}, {"$set": {"status": "DONE"}}); // 标记完成















查询

查询一条记录

> db.storeCollection.findOne({'version':'3.5'})  
{  
	"_id" : ObjectId("4ef970f23c1fc4613425accc"),  
	"version" : "3.5",  
	"segment" : "e3ol6"  
} 

查询多条记录

db.foo.find({a : 1})

查看全部符合的数量:

db.foo.find({a : 1}).count()

条件运算符

  • $gt : >
  • $lt : <
  • $gte: >=
  • $lte: <=
  • $ne : !=、<>
  • $in : in
  • $nin: not in
  • $all: all
  • $not: 反匹配(1.3.3及以上版本)
查询 name <> "bruce" and age >= 18 的数据 
db.users.find({name: {$ne: "bruce"}, age: {$gte: 18}}); 

查询 creation_date > '2010-01-01' and creation_date <= '2010-12-31' 的数据 
db.users.find({creation_date:{$gt:new Date(2010,0,1), $lte:new Date(2010,11,31)}); 

查询 age in (20,22,24,26) 的数据 
db.users.find({age: {$in: [20,22,24,26]}}); 

查询 age取模10等于0 的数据 
db.users.find('this.age % 10 == 0'); 
或者 
db.users.find({age : {$mod : [10, 0]}}); 

匹配所有 
db.users.find({favorite_number : {$all : [6, 8]}}); 
可以查询出{name: 'David', age: 26, favorite_number: [ 6, 8, 9 ] } 
可以不查询出{name: 'David', age: 26, favorite_number: [ 6, 7, 9 ] } 

查询不匹配name=B*带头的记录 
db.users.find({name: {$not: /^B.*/}}); 
查询 age取模10不等于0 的数据 
db.users.find({age : {$not: {$mod : [10, 0]}}}); 

#返回部分字段 
选择返回age和_id字段(_id字段总是会被返回) 
db.users.find({}, {age:1}); 
db.users.find({}, {age:3}); 
db.users.find({}, {age:true}); 
db.users.find({ name : "bruce" }, {age:1}); 
0为false, 非0为true 

选择返回age、address和_id字段 
db.users.find({ name : "bruce" }, {age:1, address:1}); 

排除返回age、address和_id字段 
db.users.find({}, {age:0, address:false}); 
db.users.find({ name : "bruce" }, {age:0, address:false}); 

数组元素个数判断 
对于{name: 'David', age: 26, favorite_number: [ 6, 7, 9 ] }记录 
匹配db.users.find({favorite_number: {$size: 3}}); 
不匹配db.users.find({favorite_number: {$size: 2}}); 

$exists判断字段是否存在 
查询所有存在name字段的记录 
db.users.find({name: {$exists: true}}); 
查询所有不存在phone字段的记录 
db.users.find({phone: {$exists: false}}); 

$type判断字段类型 
查询所有name字段是字符类型的 
db.users.find({name: {$type: 2}}); 
查询所有age字段是整型的 
db.users.find({age: {$type: 16}}); 

对于字符字段,可以使用正则表达式 
查询以字母b或者B带头的所有记录 
db.users.find({name: /^b.*/i}); 

$elemMatch(1.3.1及以上版本) 
为数组的字段中匹配其中某个元素 

Javascript查询和$where查询 
查询 age > 18 的记录,以下查询都一样 
db.users.find({age: {$gt: 18}}); 
db.users.find({$where: "this.age > 18"}); 
db.users.find("this.age > 18"); 
f = function() {return this.age > 18} db.users.find(f); 

排序sort() 
以年龄升序asc 
db.users.find().sort({age: 1}); 
以年龄降序desc 
db.users.find().sort({age: -1}); 

限制返回记录数量limit() 
返回5条记录 
db.users.find().limit(5); 
返回3条记录并打印信息 
db.users.find().limit(3).forEach(function(user) {print('my age is ' + user.age)}); 
结果 
my age is 18 
my age is 19 
my age is 20 

限制返回记录的开始点skip() 
从第3条记录开始,返回5条记录(limit 3, 5) 
db.users.find().skip(3).limit(5); 

查询记录条数count() 
db.users.find().count(); 
db.users.find({age:18}).count(); 
以下返回的不是5,而是user表中所有的记录数量 
db.users.find().skip(10).limit(5).count(); 
如果要返回限制之后的记录数量,要使用count(true)或者count(非0) 
db.users.find().skip(10).limit(5).count(true); 

分组group() 
假设test表只有以下一条数据 
{ domain: "www.mongodb.org" 
, invoked_at: {d:"2009-11-03", t:"17:14:05"} 
, response_time: 0.05 
, http_action: "GET /display/DOCS/Aggregation" 
} 
使用group统计test表11月份的数据count:count(*)、total_time:sum(response_time)、avg_time:total_time/count; 
db.test.group( 
{ cond: {"invoked_at.d": {$gt: "2009-11", $lt: "2009-12"}} 
, key: {http_action: true} 
, initial: {count: 0, total_time:0} 
, reduce: function(doc, out){ out.count++; out.total_time+=doc.response_time } 
, finalize: function(out){ out.avg_time = out.total_time / out.count } 
} ); 

[ 
{ 
"http_action" : "GET /display/DOCS/Aggregation", 
"count" : 1, 
"total_time" : 0.05, 
"avg_time" : 0.05 
} 
]


索引

查看索引

db.system.indexes.find()  

创建索引

格式:

ensureIndex(keypattern [, options])

例子:

db.page.ensureIndex({'title':1, 'url':-1})  

查询索引

db.page.getIndexes()  

删除索引

两种方法:

db.mycoll.dropIndex(name)  
db.mycoll.dropIndexes()

索引重建

db.page.reIndex()