一、MongoDB之CRUD

1、获取帮助

db.help //数据库帮助信息

    db.dropdatabase()
    db.getName()
    db.stats()
    db.serverStatus() //server状态
    db.getCollectionNames() //所有的集合

db.col.help //col是collection的名字,collection帮助信息
sh.help //sharding帮助
rs.help //副本集帮助
help admin
help connect //db连接帮助
help keys //快捷键帮助
help misc //编码信息
help mr //mapreduce

2、显示类命令

show dbs //查看所有的数据库
show collections //查看当前db的collections(类似RDBMS的表)
show users //当前db的用户
show profile //查看最新system.profile entries
show logs //查看最近的日志名称
show log [name] //查看最近的内存log的段,默认是'global'

3、MongoDB的CRUD:

create,read,update,delete
JSON-link style.
"_id" 相当于MySQL的主键。文档之间可以相互引用。
多个文档组合成一个collections,组合具有相似功能的文档。
MongoDB数据存储在collections,collection像MySQL的表,但是schema free、支持全文索引

4、DB的创建与删除

use DATABASE_NAME //如果数据库不存在,则创建数据库,否则切换到指定数据库。
db 命令验证当前在用的db
show dbs //查看所有db

db.testdb.insert({"name":"firsttime"}) //第一次创建的db,使用show dbs看不到,插入一条数据后就可以看到了
> use test
switched to db test
> db.dropDatabase() //删除数据库
{ "dropped" : "test", "ok" : 1 }
> show dbs
admin   0.000GB
col     0.000GB
config  0.000GB
local   0.000GB

> show collections
col
coll
> db.coll.drop()  //删除collections 
true
> show collections  //使用show tables也可以的
col

5、集合的创建与删除

db.createCollection(name, options)
name: 要创建的集合名称
options: 可选参数, 指定有关内存大小及索引的选项

options 可以是如下参数:
图1:
MongoDB详解(二)_第1张图片
在插入文档时,MongoDB 首先检查固定集合的 size 字段,然后检查 max 字段。

> db.createCollection("col1")
{ "ok" : 1 }
> show collections
col1

> db.createCollection("col2",{ capped:true,autoIndexId:true,size:6142800,max:10000})
{
    "note" : "the autoIndexId option is deprecated and will be removed in a future release",
    "ok" : 1
}

在 MongoDB 中,你不需要创建集合。当你插入一些文档时,MongoDB 会自动创建集合。

> db.col2.insert({"name":"testcoll"})
WriteResult({ "nInserted" : 1 })
> show collections
col2

db.collection.drop() //删除
show tables //也可以看到collections

6、插入文档(insert)

所有存储在集合中的数据都是BSON格式。
BSON是一种类json的一种二进制形式的存储格式,简称Binary JSON。

db.COLLECTION_NAME.insert(document)
db.col.insert({title: 'MongoDB 教程', 
    description: 'MongoDB 是一个 Nosql 数据库',
    by: '菜鸟教程',
    url: 'http://www.runoob.com',
    tags: ['mongodb', 'database', 'NoSQL'],
    likes: 100
})

//如果当前db中没有col这个集合,MongoDB将会创建该集合并插入文档
db.col.find() //db.col.find().pretty()这样显示更好看点

{ "_id" : ObjectId("5adca2da0ea27e685086ab8c"), "title" : "MongoDB 教程", "description" : "MongoDB 是一个 Nosql 数据库", "by" : "MongoDB中文网", "url" : "http://www.mongodb.org.cn", "tags" : [ "mongodb", "database", "NoSQL" ], "likes" : 100 }

通过自定义变量的方式进行插入

document=({title:'hello world',
    description:'world is so beautiful',
    by: 'Mine',
    url: 'http://localhost.com',
    tags: ['mongodb','database','nosql','newsql'],
    likes: 100
});
{
    "title" : "hello world",
    "description" : "world is so beautiful",
    "by" : "Mine",
    "url" : "http://localhost.com",
    "tags" : [
        "mongodb",
        "database",
        "nosql",
        "newsql"
    ],
    "likes" : 100
}

db.col.insert(document)
WriteResult({ "nInserted" : 1 })
插入文档你也可以使用 db.col.save(document) 命令。如果不指定 _id 字段 save() 方法类似于 insert() 方法。如果指定 _id 字段,则会更新该 _id 的数据。

3.2 版本后还有以下几种语法可用于插入文档:
db.collection.insertOne():向指定集合中插入一条文档数据
db.collection.insertMany():向指定集合中插入多条文档数据
插入单条数据

var document = db.collection.insertOne({"a": 3})  //插入collection这个"表"并且赋值给变量document
document
{
    "acknowledged" : true,
    "insertedId" : ObjectId("571a218011a82a1d94c02333")  //
}
db.collection.find() //验证是否插入

插入多条数据
注:db.collection.insertMany([{"b": 3}, {'c': 4}]) //插入记录
var res = db.collection.insertMany([{"b": 3}, {'c': 4}]) //插入并赋值给变量

res
{
    "acknowledged" : true,
    "insertedIds" : [
        ObjectId("571a22a911a82a1d94c02337"),
        ObjectId("571a22a911a82a1d94c02338")
    ]
}

注:{}是一个document,[]里面可以有多个document。

7、更新文档(update)

语法格式:

db.collection.update(
   ,
   ,
   {
     upsert: ,
     multi: ,
     writeConcern: 
   }
)

query : update的查询条件,类似sql update查询内where后面的。
update : update的对象和一些更新的操作符(如$,$inc...)等,也可以理解为sql update查询内set后面的
upsert : 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。
multi : 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。
writeConcern :可选,抛出异常的级别

异常级别:
WriteConcern.NONE:没有异常抛出
WriteConcern.NORMAL:仅抛出网络错误异常,没有服务器错误异常
WriteConcern.SAFE:抛出网络错误异常、服务器错误异常;并等待服务器完成写操作。
WriteConcern.MAJORITY: 抛出网络错误异常、服务器错误异常;并等待一个主服务器完成写操作。
WriteConcern.FSYNC_SAFE: 抛出网络错误异常、服务器错误异常;写操作等待服务器将数据刷新到磁盘。
WriteConcern.JOURNAL_SAFE:抛出网络错误异常、服务器错误异常;写操作等待服务器提交到磁盘的日志文件。
WriteConcern.REPLICAS_SAFE:抛出网络错误异常、服务器错误异常;等待至少2台服务器完成写操作。

插入数据:
db.stu.insert({name:'xiaohong',ip:'127.0.0.1',age:'25',seletest:'ziqiangbuxi',love:'basketball'})
db.stu.insert({name:'xiaoming',ip:'127.0.0.2',age:'23',seletest:'love self',love:'tee'})
db.stu.insert({name:'xiaogang',ip:'127.0.0.3',age:'24',love:'pingpong'})
db.stu.insert({"name" : "xiaohong", "ip" : "127.0.0.5", "age" : "24", "love" : "xiaohong2"})
db.stu.insert({name:"xiaoqiang",ip:"127.0.0.5",age:"26",love:"xiaohong2"})

basketball爱好改为diaoyu //下面两种方式都可以的

> db.stu.update({'love':'basketball'},{$set:{'love':'diaoyu'}})
> db.stu.update({'name':'xiaohong'},{$set:{'love':'diaoxiaoyu'}})
以上语句只会修改第一条发现的文档,如果你要修改多条相同的文档,则需要设置 multi 参数为 true。

示例:25岁的love都改为“tee"
> db.stu.update({'age':'24'},{$set:{'lovee':'tee'}},{multi:true}) //没有这个字段会自动添加

save() 方法更新文档

db.collection.save(
   ,
   {
     writeConcern: 
   }
)

document : 文档数据。
writeConcern :可选,抛出异常的级别。

> db.stu.find().pretty()
...
{
    "_id" : ObjectId("5aed3b6887388e6d32e3c726"),
    "name" : "xiaoqiang",
    "ip" : "127.0.0.5",
    "age" : "26",
    "love" : "xiaohong2"
}
...
> db.stu.save({
... "_id" : ObjectId("5aed3b6887388e6d32e3c726"),
... "name" : "xiaoqiang",
... "ip" : "127.0.0.5",
... "age" : "26",
... "love" : "selfcontrol"})
> db.stu.find().pretty()

{
    "_id" : ObjectId("5aed3b6887388e6d32e3c726"),
    "name" : "xiaoqiang",
    "ip" : "127.0.0.5",
    "age" : "26",
    "love" : "selfcontrol"
}

更多的参考案例:
只更新第一条记录:db.stu.update( { "count" : { $gt : 1 } } , { $set : { "test2" : "OK"} } );
全部更新:db.stu.update( { "count" : { $gt : 3 } } , { $set : { "test2" : "OK"} },false,true ); //upsert为false,multi为false,存在才会更新,更新所有满足条件并且存在该字段的
只添加第一条:db.stu.update( { "count" : { $gt : 4 } } , { $set : { "test5" : "OK"} },true,false ); //
全部添加加进去: db.stu.update( { "count" : { $gt : 5 } } , { $set : { "test5" : "OK"} },true,true );
全部更新:db.stu.update( { "count" : { $gt : 15 } } , { $inc : { "count" : 1} },false,true );
只更新第一条记录:db.stu.update( { "count" : { $gt : 10 } } , { $inc : { "count" : 1} },false,false );

db.collection.updateOne() 向指定集合更新单个文档
db.collection.updateMany() 向指定集合更新多个文档

use stu
db.stu_col1.insert( [
{"name":"abc","age":"25","status":"zxc"},
{"name":"dec","age":"19","status":"qwe"},
{"name":"asd","age":"30","status":"nmn"},
] )

更新单个文档
> db.stu_col1.updateOne({"name":"abc"},{$set:{"age":"28"}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.stu_col1.find()
{ "_id" : ObjectId("59c8ba673b92ae498a5716af"), "name" : "abc", "age" : "28", "status" : "zxc" }
{ "_id" : ObjectId("59c8ba673b92ae498a5716b0"), "name" : "dec", "age" : "19", "status" : "qwe" }
{ "_id" : ObjectId("59c8ba673b92ae498a5716b1"), "name" : "asd", "age" : "30", "status" : "nmn" }
>

更新多个文档
> db.stu_col1.updateMany({"age":{$gt:"10"}},{$set:{"status":"xyz"}})
{ "acknowledged" : true, "matchedCount" : 3, "modifiedCount" : 3 }
> db.stu_col1.find()
{ "_id" : ObjectId("59c8ba673b92ae498a5716af"), "name" : "abc", "age" : "28", "status" : "xyz" }
{ "_id" : ObjectId("59c8ba673b92ae498a5716b0"), "name" : "dec", "age" : "19", "status" : "xyz" }
{ "_id" : ObjectId("59c8ba673b92ae498a5716b1"), "name" : "asd", "age" : "30", "status" : "xyz" }
>

update常用的操作:

$inc:增大指定字段的值:db.collection.update({field:value},{$inc:{field1:amount}}})  //"field:value"是query内容,amount是调整大小
$rename:更改字段名:{$rename:{:,:,...}}
$set:修改字段的值为新的值:db.collection.update({field:value1},{$set:{field1:value2}})
$unset:db.collection.update({field:value1},{$unset:{field1:" "}})

name为“wolf"的index增加2:
test_set:PRIMARY> db.testCon.find()
{ "_id" : ObjectId("5aee6f5fa8232742f456fff7"), "name" : "wolf", "index" : 5 }
test_set:PRIMARY> db.testCon.update({"name":"wolf"},{$inc:{"index":2}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
test_set:PRIMARY> db.testCon.find()
{ "_id" : ObjectId("5aee6f5fa8232742f456fff7"), "name" : "wolf", "index" : 7 }

更新name为jack的url和name字段名分别为extra和huaming:
test_set:PRIMARY> db.testCon.insert({"name":"jack","index":"15","url":"www.jack.com"})
test_set:PRIMARY> db.testCon.update({"name":"jack"},{$rename:{'url':'extra','name':'huaming'}})
{ "_id" : ObjectId("5aee8e31a8232742f456fffa"), "index" : "15", "extra" : "www.jack.com", "huaming" : "jack" }

删除index为15的“huaming”字段:
{ "_id" : ObjectId("5aee8e31a8232742f456fffa"), "index" : "15", "extra" : "www.jack.com", "huaming" : "jack" }
test_set:PRIMARY> db.testCon.update({"index":"15"},{$unset:{"huaming":"jack"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
test_set:PRIMARY> db.testCon.find()
{ "_id" : ObjectId("5aee8e31a8232742f456fffa"), "index" : "15", "extra" : "www.jack.com" }

8、删除文档(remove)

remove的基本语法:

db.collection.remove(
   ,
   
)

2.6版本之后的新语法:

db.collection.remove(
   ,
   {
     justOne: ,
     writeConcern: 
   }
)

query :(可选)删除的文档的条件。
justOne : (可选)如果设为 true 或 1,则只删除一个文档。
writeConcern :(可选)抛出异常的级别。

> db.testcol.find({acknowledged:true})  //remove的时候尽量先find,key和value可以不加" "
{ "_id" : ObjectId("5aed244587388e6d32e3c71d"), "acknowledged" : true, "insertedIds" : [ ObjectId("5aed242487388e6d32e3c71b"), ObjectId("5aed242487388e6d32e3c71c") ] }
> db.testcol.remove({acknowledged:true})
WriteResult({ "nRemoved" : 1 })

>db.COLLECTION_NAME.remove(DELETION_CRITERIA,1) //只删除第一条找到的记录
>db.col.remove({}) //删除所有数据

remove() 方法已经过时了,现在官方推荐使用 deleteOne() 和 deleteMany() 方法。
> db.testcol.deleteMany({d:{$gt:10}})
{ "acknowledged" : true, "deletedCount" : 2 }
> db.testcol.deleteOne({d:{$gt:12}})
{ "acknowledged" : true, "deletedCount" : 1 }
db.testcol.deleteMany({})
{ "acknowledged" : true, "deletedCount" : 5 }

二、查询文档

1、查询文档

db.collection.find(query, projection)
query :可选,使用查询操作符指定查询条件
projection :可选,使用投影操作符指定返回的键。查询时返回文档中所有键值,只需省略该参数即可(默认省略)。
除了 find() 方法之外,还有一个 findOne() 方法,它只返回一个文档。

MongoDB 与 RDBMS Where 语句比较
图2
MongoDB详解(二)_第2张图片

2、and条件:

>db.col.find({key1:value1, key2:value2}).pretty()
> db.stu_col1.find({"age":{$gt:"20"},"name":"abc"})

> db.stu_col1.find()
{ "_id" : ObjectId("5aed469c87388e6d32e3c727"), "name" : "abc", "age" : "25", "status" : "xyz" }
{ "_id" : ObjectId("5aed469c87388e6d32e3c728"), "name" : "dec", "age" : "19", "status" : "xyz" }
{ "_id" : ObjectId("5aed469c87388e6d32e3c729"), "name" : "asd", "age" : "30", "status" : "xyz" }

错误的两种写法
> db.stu_col1.find({status:xyz})  //这样是不行的
2018-05-05T15:11:32.374+0800 E QUERY    [thread1] ReferenceError: xyz is not defined :
@(shell):1:19
> db.stu_col1.find({"status":xyz})
2018-05-05T15:12:28.324+0800 E QUERY    [thread1] ReferenceError: xyz is not defined :
@(shell):1:19

正确的写法
> db.stu_col1.find({status:"xyz"})
> db.stu_col1.find({"status":"xyz"})  //推荐
> db.stu_col1.find({"age":{$gt:"20"}})

3、or条件:

>db.col.find(
   {
      $or: [
         {key1: value1}, {key2:value2}
      ]
   }
).pretty()
> db.stu_col1.find({$or:[{"name":"abc"},{"age":{$gt:"25"}}]}).pretty() //一样
> db.stu_col1.find({
$or:[
    {"name":"abc"},{"age":{$gt:"25"}}
    ]
}).pretty()
{
    "_id" : ObjectId("5aed469c87388e6d32e3c727"),
    "name" : "abc",
    "age" : "25",
    "status" : "xyz"
}
{
    "_id" : ObjectId("5aed469c87388e6d32e3c729"),
    "name" : "asd",
    "age" : "30",
    "status" : "xyz"
}

and和or联用:

> db.stu_col1.find()
{ "_id" : ObjectId("5aed469c87388e6d32e3c727"), "name" : "abc", "age" : "25", "status" : "xyz" }
{ "_id" : ObjectId("5aed469c87388e6d32e3c728"), "name" : "dec", "age" : "19", "status" : "xyz" }
{ "_id" : ObjectId("5aed469c87388e6d32e3c729"), "name" : "asd", "age" : "30", "status" : "xyz" }
> db.stu_col1.find({"age":{$gte:"25"},$or:[{"name":"abc"},{"name":"age"}]})
{ "_id" : ObjectId("5aed469c87388e6d32e3c727"), "name" : "abc", "age" : "25", "status" : "xyz" }

4、projection操作:

db.collection.find(query, projection)

_id 键默认返回,需要主动指定 _id:0 才会隐藏

两种模式不可混用(因为这样的话无法推断其他键是否应返回)

db.collection.find(query, {title: 1, by: 0}) //错误,只能全1或全0。除了在inclusion模式时可以指定_id为0
db.collection.find(query, {_id:0, title: 1, by: 1}) // 正确

> db.stu_col1.find({"age":{$gt:"20"}}) //默认返回所有
{ "_id" : ObjectId("5aed469c87388e6d32e3c727"), "name" : "abc", "age" : "25", "status" : "xyz" }
{ "_id" : ObjectId("5aed469c87388e6d32e3c729"), "name" : "asd", "age" : "30", "status" : "xyz" }

db.collection.find(query, {title: 1, by: 1}) // inclusion模式 指定返回的键,不返回其他键
db.collection.find(query, {title: 0, by: 0}) // exclusion模式 指定不返回的键,返回其他键
> db.stu_col1.find({"age":{$gt:"20"}},{title:1,by:1}) //只返回"_id"
{ "_id" : ObjectId("5aed469c87388e6d32e3c727") }
{ "_id" : ObjectId("5aed469c87388e6d32e3c729") }
> db.stu_col1.find({"age":{$gt:"20"}},{title:0,by:0})
{ "_id" : ObjectId("5aed469c87388e6d32e3c727"), "name" : "abc", "age" : "25", "status" : "xyz" }
{ "_id" : ObjectId("5aed469c87388e6d32e3c729"), "name" : "asd", "age" : "30", "status" : "xyz" }

> db.stu_col1.find({"age":{$gt:"20"}},{name:1,title:1,by:1}) //显示name和_id字段
{ "_id" : ObjectId("5aed469c87388e6d32e3c727"), "name" : "abc" }
{ "_id" : ObjectId("5aed469c87388e6d32e3c729"), "name" : "asd" }
> db.stu_col1.find({"age":{$gt:"20"}},{_id:0,title:0,by:0})  //不显示_id字段
{ "name" : "abc", "age" : "25", "status" : "xyz" }
{ "name" : "asd", "age" : "30", "status" : "xyz" }

> db.stu_col1.find({},{_id:0,title:0,by:0})  //query的内容可以用{} 填充
{ "name" : "abc", "age" : "25", "status" : "xyz" }
{ "name" : "dec", "age" : "19", "status" : "xyz" }
{ "name" : "asd", "age" : "30", "status" : "xyz" }

5、not和nor

not表达式:"{field:{$not:{}}}"
nor表达式:"{field:{$nor:[ {expression-1}, {expression-2},....]}}"

查找index不大于1(小于等于1)的值

test_set:PRIMARY> db.testCon.find()
{ "_id" : ObjectId("5aedb8b3aa48b68a53ecaa9e"), "name" : "Joe", "index" : 0 }
{ "_id" : ObjectId("5aedb8b3aa48b68a53ecaa9f"), "name" : "Joe", "index" : 1 }
{ "_id" : ObjectId("5aedb8b3aa48b68a53ecaaa0"), "name" : "Joe", "index" : 2 }
{ "_id" : ObjectId("5aee6f5fa8232742f456fff7"), "name" : "wolf", "index" : 5 }
test_set:PRIMARY> db.testCon.find({"index":{$not:{$gt:1}}})
{ "_id" : ObjectId("5aedb8b3aa48b68a53ecaa9e"), "name" : "Joe", "index" : 0 }
{ "_id" : ObjectId("5aedb8b3aa48b68a53ecaa9f"), "name" : "Joe", "index" : 1 }
查找name不是以"J"开头的
test_set:PRIMARY> db.testCon.find({name:{$not:/^J.*/}})  //注意"/^J.*/"不加“ ”
{ "_id" : ObjectId("5aee6f5fa8232742f456fff7"), "name" : "wolf", "index" : 5 }
$not操作符不支持$regex正则表达式操作,使用//或者你的驱动接口代替

index为2和name为"Joe"的并集在全集中取反
test_set:PRIMARY> db.testCon.find({$nor:[{"index":2},{"name":"Joe"}]})
{ "_id" : ObjectId("5aee6f5fa8232742f456fff7"), "name" : "wolf", "index" : 5 }
{ "_id" : ObjectId("5aee73a0a8232742f456fff8"), "name" : "tom", "index" : 12 }

6、条件操作符

(>) 大于 - $gt
(<) 小于 - $lt
(>=) 大于等于 - $gte
(<= ) 小于等于 - $lte
in 在指定范围内
nin 不在指定范围内
$gt -------- greater than  >
$gte --------- gt equal  >=
$lt -------- less than  <
$lte --------- lt equal  <=
$ne ----------- not equal  !=
$eq  --------  equal  =
组合使用:
> db.stu_col1.find({"age":{$gt:"20",$lt:"30"}},{_id:0,title:0,by:0}) //20-30之间的
{ "name" : "abc", "age" : "25", "status" : "xyz" }
> db.stu_col1.find({$or:[{"age":{$gt:"25"}},{"age":{$lt:"20"}}]})  //小于20或者大于25的
> db.stu_col1.find({$or:[{"age":{$gt:"25"}},{"age":{$lt:"20"}}]},{_id:0,title:0,by:0})
in用法
test_set:PRIMARY> db.testCon.find()
{ "_id" : ObjectId("5aedb8b3aa48b68a53ecaa9e"), "name" : "Joe", "index" : 0 }
{ "_id" : ObjectId("5aedb8b3aa48b68a53ecaa9f"), "name" : "Joe", "index" : 1 }
{ "_id" : ObjectId("5aedb8b3aa48b68a53ecaaa0"), "name" : "Joe", "index" : 2 }
test_set:PRIMARY> db.testCon.find({"index":{$in:[1,2,3]}})
{ "_id" : ObjectId("5aedb8b3aa48b68a53ecaa9f"), "name" : "Joe", "index" : 1 }
{ "_id" : ObjectId("5aedb8b3aa48b68a53ecaaa0"), "name" : "Joe", "index" : 2 }
test_set:PRIMARY> db.testCon.find({"index":{$nin:[1,2,3]}})
{ "_id" : ObjectId("5aedb8b3aa48b68a53ecaa9e"), "name" : "Joe", "index" : 0 }
test_set:PRIMARY> db.testCon.find()
{ "_id" : ObjectId("5aedb8b3aa48b68a53ecaa9e"), "name" : "Joe", "index" : 0 }
{ "_id" : ObjectId("5aedb8b3aa48b68a53ecaa9f"), "name" : "Joe", "index" : 1 }
{ "_id" : ObjectId("5aedb8b3aa48b68a53ecaaa0"), "name" : "Joe", "index" : 2 }
{ "_id" : ObjectId("5aee6f5fa8232742f456fff7"), "name" : "wolf", "index" : 5 }
test_set:PRIMARY> db.testCon.find({"name":{$nin:["wolf","tom"]}})
{ "_id" : ObjectId("5aedb8b3aa48b68a53ecaa9e"), "name" : "Joe", "index" : 0 }
{ "_id" : ObjectId("5aedb8b3aa48b68a53ecaa9f"), "name" : "Joe", "index" : 1 }
{ "_id" : ObjectId("5aedb8b3aa48b68a53ecaaa0"), "name" : "Joe", "index" : 2 }

7、元素查询type,exists,mod

type:
{field:{$type:}}
$type操作符是基于BSON类型来检索集合中匹配的数据类型,并返回结果。
图3:
MongoDB详解(二)_第3张图片

> db.stu_col1.find({"age":{$type:2}})  
> db.stu_col1.find({"age":{$type:2}},{_id:0,title:0,by:0})
{ "name" : "abc", "age" : "25", "status" : "xyz" }
{ "name" : "dec", "age" : "19", "status" : "xyz" }
{ "name" : "asd", "age" : "30", "status" : "xyz" }

exists:

{field:{$exists:}} //指定boolean的值为true返回指定字段的文档,“false"返回不存在指定的文档
test_set:PRIMARY> db.testCon.insert({"name":"baidu","url":"www.baidu.com"})
test_set:PRIMARY> db.testCon.find({url:{$exists:true}})
{ "_id" : ObjectId("5aee8966a8232742f456fff9"), "name" : "baidu", "url" : "www.baidu.com" }
{ "_id" : ObjectId("5aee8966a8232742f456fff9"), "name" : "baidu", "url" : "www.baidu.com" }
test_set:PRIMARY> db.testCon.find({url:{$exists:0}})
{ "_id" : ObjectId("5aedb8b3aa48b68a53ecaa9e"), "name" : "Joe", "index" : 0 }
{ "_id" : ObjectId("5aedb8b3aa48b68a53ecaa9f"), "name" : "Joe", "index" : 1 }
{ "_id" : ObjectId("5aedb8b3aa48b68a53ecaaa0"), "name" : "Joe", "index" : 2 }
{ "_id" : ObjectId("5aee6f5fa8232742f456fff7"), "name" : "wolf", "index" : 5 }
{ "_id" : ObjectId("5aee73a0a8232742f456fff8"), "name" : "tom", "index" : 12 }
test_set:PRIMARY> db.testCon.find({url:{$exists:1}})
{ "_id" : ObjectId("5aee8966a8232742f456fff9"), "name" : "baidu", "url" : "www.baidu.com" }

注:其中boolean可以是0和1,或者true和false

mod:
{field:{$mod:[divisor,remainder]}} //取模运算
取出index对2取模余1的内容
test_set:PRIMARY> db.testCon.find({index:{$mod:[2,1]}})
{ "_id" : ObjectId("5aedb8b3aa48b68a53ecaa9f"), "name" : "Joe", "index" : 1 }
{ "_id" : ObjectId("5aee6f5fa8232742f456fff7"), "name" : "wolf", "index" : 5 }

8、limit和skip用法

>db.COLLECTION_NAME.find().limit(NUMBER)
> db.stu_col1.find().limit(1)
{ "_id" : ObjectId("5aed469c87388e6d32e3c727"), "name" : "abc", "age" : "25", "status" : "xyz" }

> db.stu_col1.find({},{"_id":0,"title":0,"by":0})
{ "name" : "abc", "age" : "25", "status" : "xyz" }
{ "name" : "dec", "age" : "19", "status" : "xyz" }
{ "name" : "asd", "age" : "30", "status" : "xyz" }
> db.stu_col1.find({},{"_id":0,"title":0,"by":0}).limit(1).skip(1)
{ "name" : "dec", "age" : "19", "status" : "xyz" }

三、聚合排序索引

1、排序(sort)

>db.COLLECTION_NAME.find().sort({KEY:1}) //其中 1 为升序排列,而-1是用于降序排列。
> db.stu_col1.find({},{_id:0,title:0,by:0}).sort({"age":1})
{ "name" : "dec", "age" : "19", "status" : "xyz" }
{ "name" : "abc", "age" : "25", "status" : "xyz" }
{ "name" : "asd", "age" : "30", "status" : "xyz" }
> db.stu_col1.find({},{_id:0,title:0,by:0}).sort({"age":-1})
{ "name" : "asd", "age" : "30", "status" : "xyz" }
{ "name" : "abc", "age" : "25", "status" : "xyz" }
{ "name" : "dec", "age" : "19", "status" : "xyz" }

2、索引(ensureIndex)

如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。
索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构
mongodb的索引是collection级别的,可以建立在any field或者sub-field
索引类型:
单键索引(Single field Indexes):MongoDB默认创建的_id的索引就是单键索引。
复合索引(Compound Indexes):在多个键上建立的索引
多键索引(MultiKey indexes):如果在一个数组上面创建索引,MongoDB会自己决定,是否要把这个索引建成多键索引。
地理空间索引(Geospatial Indexes and Queries):
全文索引(Text Indexes):
哈希索引(hash indexes):

2.1、单键索引:

db.users.ensureIndex( { "score" : 1 } )  //单键索引 
{
    _id: ObjectId(...),
    metro: {
        city: "New York",
        state: "NY"
    },
    name: "Giant Factory"
}

db.factories.ensureIndex( { metro: 1 } )
db.factories.find( { metro: { city: "New York", state: "NY" } } ) //可以找到
db.factories.find( { metro: { state: "NY", city: "New York" } } ) //找不到,子文档的查找必须是精确匹配,包括子文档中的顺序

2.2、复合索引:

db.products.ensureIndex( { "userid": -1, "score": 1 } ) //userid是正序排列,score是逆序排列的
这个索引可以支持如下的排序
db.products.find().sort( { userid: 1, score: -1 } );
db.products.find().sort( { userid: -1, score: 1 } );
db.products.find().sort( { userid: 1 } );
db.products.find().sort( { userid: -1 } );

不能支持如下的排序:
db.products.find().sort( { userid: 1, score: 1 } );
db.products.find().sort( { userid: -1, score: -1 } );
db.products.find().sort( { score: 1 } );
db.products.find().sort( { score: -1 } );

2.3、多键索引(Multikey Index):
如果在一个数组上面创建索引,MongoDB会自己决定,是否要把这个索引建成多键索引。
{a: [1, 2], b: 1}
{a: 1, b: [1, 2]}
db.feedback.find( { "comments.text": "Please expand the olive selection." } )

多个字段创建索引

>db.stu_col1.ensureIndex({"_id":1,"name":-1})

图4:
MongoDB详解(二)_第4张图片
在一个数组上面创建索引,MongoDB会自己决定,是否要把这个索引建成多键索引。

{
    "_id" : ObjectId("..."),
    "name" : "Warm Weather",
    "author" : "Steve",
    "tags" : [ "weather", "hot", "record", "april" ]
}

//在tags上创建索引,就会创建出多键索引

{
    "_id": ObjectId(...),
    "title": "Grocery Quality",
    "comments": [{
        author_id: ObjectId(...),
        date: Date(...),
        text: "Please expand the cheddar selection."
    }, {
        author_id: ObjectId(...),
        date: Date(...),
        text: "Please expand the mustard selection."
    }, {
        author_id: ObjectId(...),
        date: Date(...),
        text: "Please expand the olive selection."
    }]
}

创建{ "comments.text": 1 } 索引也会是多键索引,且在如下查找语句中有效:
db.feedback.find( { "comments.text": "Please expand the olive selection." } )

2.4、地理空间索引:

2dsphere 允许使用GeoJSON格式(http://www.geojson.org)指定点、线和多边形。
点可以用形如[longitude, latitude]([经度, 纬度])的两个元素的数组表示:

{
    "name" : "New York City",
    "loc" : {
        "type" : "Point",
        "coordinates" : [50, 2]
    }
}

线可以用一个由点组成的数组来表示:

{
    "name" : "Hudson River",
    "loc" : {
        "type" : "LineString",
        "coordinates" : [[0, 1], [0, 2], [1, 2]]
    }
}

多边形是由线组成的数组来表示:

{
    "name" : "New England",
    "loc" : {
        "type" : "Polygon",
        "coordinates" : [[[0, 1], [0, 2], [1, 2], [0, 1]]]
    }
}

2dsphere 支持 Point、MultiPoint、LineString、MultiLineString、Polygon、MultiPolygon、Geometry Collection
loc 字段的名字可以是任意的,但是其中子对象是由 GeoJSON 指定的,不能改变。
在 ensureIndex 中使用 2dsphere 选项就可以创建一个地理空间索引:
db.world.ensureIndex({"loc": "2dsphere"})

可以使用多种不同的地理空间查询:交集(intersection)、包含(within)以及接近(nearness)。查询时,需要将希望查找的内容指定为形如 {"$geometry":geoJsonDesc} 的 GeoJSON 对象。
交集(intersection),使用 $geoIntersects 操作符:

var place = {
    "type" : "Polygon",
    "coordinates" : [[[0, 1], [0, 3], [50, 2], [0, 1]]]
}

db.world.find({"loc" : {"$geoIntersects" : {"$geometry" : place}}})
会查找出所有与 place 有交集的文档。
包含(within),使用 $within 或者 $geoWithin 操作符:
db.world.find({"loc" : {"$within" : {"$geometry" : place}}})

接近(nearness),使用 $near 或者 $geoNear 操作符:

var place = {
    "type" : "Point",
    "coordinates" : [0, 3]
}

db.world.find({"loc" : {"$near" : {"$geometry" : place}}})
place 必须是个点,$near 是唯一一个会对查询结果进行自动排序的地理空间操作符,$near 的返回结果是按照距离由近及远排序的。

2.5、全文索引

全文索引用于在文档中搜索文本也可以使用正则表达式来查询字符串,但是当文本块比较大的时候,正则表达式搜索会非常慢,而且无法处理语言理解的问题(如 entry 和 entries 应该算是匹配的)。
使用全文索引可以非常快地进行文本搜索,就如同内置了多种语言分词机制的支持一样。创建索引的开销都比较大,全文索引的开销更大。创建索引时,需后台或离线创建。

{
    "_id" : ObjectId("55a0e30427c9370e525032e9"),
    "content" : "This morning I had a cup of coffee.",
    "about" : "beverage",
    "keywords" : [
        "coffee"
    ]
}
db.article.ensureIndex({"content": "text"} //创建全文索引
db.article.ensureIndex({"content": "text"}, {"default_language": "spanish"}) //指定语言
db.article.find({"$text": {"$search": "coffee"}}) //使用全文索引查询

###$ 2.6、hash索引
哈希索引可以支持相等查询,但是哈希索引不支持范围查询。您可能无法创建一个带有哈希索引键的复合索引或者对哈希索引施加唯一性的限制。但是,您可以在同一个键上同时创建一个哈希索引和一个递增/递减(例如,非哈希)的索引,这样MongoDB对于范围查询就会自动使用非哈希的索引。
db.article.ensureIndex({"a": "hashed"})

2.7、索引属性

MongDB的索引属性有以下几种:TTL索引、唯一索引和稀疏索引。
TTL索引是一种特殊索引,通过这种索引MongoDB会过一段时间后自动移除集合中的文档。
TTL索引有如下限制:
它不支持复合索引 。
被索引键必须是日期类型的数据。
如果这个键存储的是一个数组,且在索引中有多个日期类型的数据(和一篇文档关联),那么当其中最低 (比如,最早)过期阀值得到匹配时,这篇文档就会过期失效了。
TTL索引不能保证过期数据会被立刻删除。在文档过期和MongoDB从数据库中删除文档之间,可能会有延迟。
删除过期数据的后台任务 每隔60秒 运行一次。所以,在文档过期 之后 和 后台任务运行或者结束 之前 ,文档会依然存在于集合中。删除操作的持续实际取决于您的 mongod 实例的负载。
因此,在两次后台任务运行的间隔间,过期数据可能会继续留在数据库中超过60秒。在其他方面,TTL索引是普通索引,并且如果可以的话,MongoDB会使用这些索引来匹配任意查询。
db.token.ensureIndex({"lastUpdated": 1}, {"expireAfterSecs": 606024}) //token 超过24小时就会被删除掉。

> db.log_events.createIndex({"createdAt": 1},{expireAfterSeconds: 60}) //60s后到期
插入文档
> db.log_events.insert({
    "createdAt": new Date(),
    "logEvent": 2,
    "logMessage": "Success!"
})
> db.log_events.find() //时间段后查看

** 指定过期时间:**
> db.ttl.createIndex({"expireAt": 1},{expireAfterSeconds:0}) 
插入数据:
> db.ttl.insert({
    "createdAt": new Date('Mar 11, 2016 09:30:00'),
    "logEvent": 2,
    "logMessage": "Success!"
})

使用TTL时是有限制的:

-如果要索引的字段已经在其他索引中使用,不能创建TTL索引
-索引不能包含多个字段
-如果定义的字段不存在,则永不过期
-不能对capped集合创建TTL索引

唯一索引(Unique Indexes)
db.members.ensureIndex({"user_id": 1}, {unique: true}) //默认为false
唯一索引可以防止 不同 文档的被索引键上存储相同值,但是它不禁止同一篇文档在被索引键存储的数组里存储的元素或者内嵌文档是相同的值。在同一篇文档存储重复数据的情况下,重复的值只会被存入索引一次。

db.collection.ensureIndex({"a.b": 1 }, {unique: true})
假如在集合中没有其他的文档的 a.b 键的值是 5 ,那么唯一索引将会允许将以下文档插入集合:
db.collection.insert({a: [{b: 5}, {b: 5}]})

如果一篇文档不包含唯一索引的被索引键,那么索引默认会为该文档存储一个null值。
由于唯一性的限制,MongoDB将只允许有一篇可以不包含被索引键。如果超过一篇文档不包含被索引键或没有值,那么会抛出键重复(duplicate key)错误导致索引创建失败。可以组合使用唯一性和稀疏索引的特性来过滤那些包含null值的文档以避免这个错误。

稀疏索引(Sparse Indexes)
稀疏索引会跳过所有不包含被索引键的文档。这个索引之所以称为 “稀疏” 是因为它并不包括集合中的所有文档。与之相反,非稀疏的索引会索引每一篇文档,如果一篇文档不含被索引键则为它存储一个null值。
db.addresses.ensureIndex({"xmpp_id": 1}, {"sparse": true})
如果一个索引会导致查询或者排序的结果集是不完整的,那么MongoDB将不会使用这个索引,除非用户使用 hint() 方法来显示指定索引。例如,查询 { x: { $exists: false } } 将不会使用 x 键上的稀疏索引,除非显示的hint。
2dsphere (version 2), 2d 和 text 这些索引总是稀疏的。
只要一篇文档里有至少一个被索引键,稀疏且只包含有递增/递减索引键的复合索引就会索引这篇文档。
至于稀疏且包含有地理索引键(例如 2dsphere, 2d)以及递增/递减索引键的复合索引,只有地理索引键的存在与否能决定一篇文档是否被索引。
至于稀疏且包含了全文索引键和其他递增/递减索引键的复合索引,只有全文索引键的存在与否能决定是否索引该文档。
一个稀疏且唯一的索引,可以防止集合中的文档被索引键中出现重复值,同时也允许多个文档里不包含被索引键。

3、聚合

MongoDB中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。有点类似sql语句中的 count(*)。
db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)
图5:
MongoDB详解(二)_第5张图片
管道:
一般用于将当前命令的输出结果作为下一个命令的参数。
MongoDB的聚合管道将MongoDB文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的。
表达式:处理输入文档并输出。表达式是无状态的,只能用于计算当前聚合管道的文档,不能处理其它的文档。

    $project:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。
    $match:用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作。
    $limit:用来限制MongoDB聚合管道返回的文档数。
    $skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。
    $unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
    $group:将集合中的文档分组,可用于统计结果。
    $sort:将输入文档排序后输出。
    $geoNear:输出接近某一地理位置的有序文档。

db.getCollection('test').aggregate({"$group":{_id: 'max',max_value:{"$max":"$search_frequency"}}});

> db.stu_col1.aggregate({"$group":{_id:"max",max_value:{"$max":"$age"}}})
{ "_id" : "max", "max_value" : "30" }

> db.stu_col1.find()
{ "_id" : ObjectId("5aed469c87388e6d32e3c727"), "name" : "abc", "age" : "25", "status" : "xyz" }
{ "_id" : ObjectId("5aed469c87388e6d32e3c728"), "name" : "dec", "age" : "19", "status" : "xyz" }
{ "_id" : ObjectId("5aed469c87388e6d32e3c729"), "name" : "asd", "age" : "30", "status" : "xyz" }
{ "_id" : ObjectId("5aed90661a944c8f36d7b8f3"), "name" : "aaa", "age" : "128", "power" : "120" }
{ "_id" : ObjectId("5aed90c61a944c8f36d7b8f4"), "name" : "aaa", "age" : "128", "power" : "120" }
{ "_id" : ObjectId("5aed90c61a944c8f36d7b8f5"), "name" : "ggg", "age" : "120", "shuxing" : "huo" }

> db.stu_col1.aggregate({"$group":{_id: 'max',max_value:{"$max":"$age"}}});

> db.test.find()
{ "_id" : "A", "company_name" : "公司A", "search_frequency" : 212 }
{ "_id" : "B", "company_name" : "公司B", "search_frequency" : 160 }
{ "_id" : "C", "company_name" : "公司C", "search_frequency" : 553 }

> db.test.aggregate({"$group":{_id: 'min',min_value:{"$min":"$search_frequency"}}});
{ "_id" : "min", "min_value" : 160 }
> db.test.aggregate({"$group":{_id: 'max',max_value:{"$max":"$search_frequency"}}});
{ "_id" : "max", "max_value" : 553 }
> db.test.aggregate({"$group":{_id: 'sum',sum_value:{"$sum":"$search_frequency"}}});
{ "_id" : "sum", "sum_value" : 925 }
> db.test.aggregate({"$group":{_id: 'avg',avg_value:{"$avg":"$search_frequency"}}});
{ "_id" : "avg", "avg_value" : 308.3333333333333 }

project举例:
> db.stu_col1.aggregate({$project:{name:1,age:1}})
{ "_id" : ObjectId("5aed469c87388e6d32e3c727"), "name" : "abc", "age" : "25" }
{ "_id" : ObjectId("5aed469c87388e6d32e3c728"), "name" : "dec", "age" : "19" }
{ "_id" : ObjectId("5aed469c87388e6d32e3c729"), "name" : "asd", "age" : "30" }
{ "_id" : ObjectId("5aed90661a944c8f36d7b8f3"), "name" : "aaa", "age" : "128" }
{ "_id" : ObjectId("5aed90c61a944c8f36d7b8f4"), "name" : "aaa", "age" : "128" }
{ "_id" : ObjectId("5aed90c61a944c8f36d7b8f5"), "name" : "ggg", "age" : "120" }
> db.stu_col1.aggregate({$project:{_id:0,name:1,age:1}}) //只显示name和age
{ "name" : "abc", "age" : "25" }
{ "name" : "dec", "age" : "19" }
{ "name" : "asd", "age" : "30" }
{ "name" : "aaa", "age" : "128" }
{ "name" : "aaa", "age" : "128" }
{ "name" : "ggg", "age" : "120" }

四、副本集

1、副本集简介

复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性, 并可以保证数据的安全性。
允许从硬件故障和服务中断中恢复数据。
mongodb的复制至少需要两个节点。其中一个是主节点,负责处理客户端请求,其余的都是从节点,负责复制主节点上的数据。
mongodb各个节点常见的搭配方式为:一主一从、一主多从。
主节点记录在其上的所有操作oplog,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致。

图6:
MongoDB详解(二)_第6张图片

2、副本集特征:

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

3、配置

副本集设置:
#mongod --port=1200 --bind_ip=127.0.0.1 --dbpath=/mongodb/db1/ --logpath=/mongodb/db1/logs/db1.log --replSet test_set --logappend --fork
#mongod --port=1201 --bind_ip=127.0.0.1 --dbpath=/mongodb/db2/ --logpath=/mongodb/db2/logs/db2.log --replSet test_set --logappend --fork
#mongod --port=1202 --bind_ip=127.0.0.1 --dbpath=/mongodb/db3/ --logpath=/mongodb/db2/logs/db3.log --replSet test_set --logappend --fork
//要先初始化副本集

> rs.initiate({"_id":"test_set",members:[{"_id":1,"host":"127.0.0.1:1200","priority":9},{"_id":2,"host":"127.0.0.1:1201","priority":3},{"_id":3,"host":"127.0.0.1:1202","arbiterOnly":true}] })
{
    "ok" : 1,
    "operationTime" : Timestamp(1525528450, 1),
    "$clusterTime" : {
        "clusterTime" : Timestamp(1525528450, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    }
}
"_id": 副本集的名称
"members": 副本集的服务器列表
"_id": 服务器的唯一ID
"host": 服务器主机
"priority": 是优先级,默认为1,优先级0为被动节点,不能成为活跃节点。优先级不位0则按照有大到小选出活跃节点。
"arbiterOnly": 仲裁节点,只参与投票,不接收数据,也不能成为活跃节点。
test_set:PRIMARY> rs.status()
{
    "set" : "test_set",
    "date" : ISODate("2018-05-05T13:54:34.161Z"),
    "myState" : 1,
    "term" : NumberLong(1),
    "heartbeatIntervalMillis" : NumberLong(2000),
    "optimes" : {
        "lastCommittedOpTime" : {
            "ts" : Timestamp(1525528464, 1),
            "t" : NumberLong(1)
        },
        "readConcernMajorityOpTime" : {
            "ts" : Timestamp(1525528464, 1),
            "t" : NumberLong(1)
        },
        "appliedOpTime" : {
            "ts" : Timestamp(1525528464, 1),
            "t" : NumberLong(1)
        },
        "durableOpTime" : {
            "ts" : Timestamp(1525528464, 1),
            "t" : NumberLong(1)
        }
    },
    "members" : [
        {
            "_id" : 1,
            "name" : "127.0.0.1:1200",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 232,
            "optime" : {
                "ts" : Timestamp(1525528464, 1),
                "t" : NumberLong(1)
            },
            "optimeDate" : ISODate("2018-05-05T13:54:24Z"),
            "infoMessage" : "could not find member to sync from",
            "electionTime" : Timestamp(1525528461, 1),
            "electionDate" : ISODate("2018-05-05T13:54:21Z"),
            "configVersion" : 1,
            "self" : true
        },
        {
            "_id" : 2,
            "name" : "127.0.0.1:1201",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 23,
            "optime" : {
                "ts" : Timestamp(1525528464, 1),
                "t" : NumberLong(1)
            },
            "optimeDurable" : {
                "ts" : Timestamp(1525528464, 1),
                "t" : NumberLong(1)
            },
            "optimeDate" : ISODate("2018-05-05T13:54:24Z"),
            "optimeDurableDate" : ISODate("2018-05-05T13:54:24Z"),
            "lastHeartbeat" : ISODate("2018-05-05T13:54:33.563Z"),
            "lastHeartbeatRecv" : ISODate("2018-05-05T13:54:32.359Z"),
            "pingMs" : NumberLong(0),
            "syncingTo" : "127.0.0.1:1200",
            "configVersion" : 1
        },
        {
            "_id" : 3,
            "name" : "127.0.0.1:1202",
            "health" : 1,
            "state" : 7,
            "stateStr" : "ARBITER",
            "uptime" : 23,
            "lastHeartbeat" : ISODate("2018-05-05T13:54:33.562Z"),
            "lastHeartbeatRecv" : ISODate("2018-05-05T13:54:32.291Z"),
            "pingMs" : NumberLong(0),
            "configVersion" : 1
        }
    ],
    "ok" : 1,
    "operationTime" : Timestamp(1525528464, 1),
    "$clusterTime" : {
        "clusterTime" : Timestamp(1525528464, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    }
}

在主节点上插入数据,在从节点上查看。

test_set:PRIMARY>  for(var i=0; i<3;i++) db.testCon.insert({name:"Joe",index:i})
test_set:PRIMARY> db.testCon.find()
{ "_id" : ObjectId("5aedb8b3aa48b68a53ecaa9e"), "name" : "Joe", "index" : 0 }
{ "_id" : ObjectId("5aedb8b3aa48b68a53ecaa9f"), "name" : "Joe", "index" : 1 }
{ "_id" : ObjectId("5aedb8b3aa48b68a53ecaaa0"), "name" : "Joe", "index" : 2 }

test_set:SECONDARY> show dbs
2018-05-05T22:01:09.541+0800 E QUERY    [thread1] Error: listDatabases failed:{
    "operationTime" : Timestamp(1525528863, 1),
    "ok" : 0,
    "errmsg" : "not master and slaveOk=false",
    "code" : 13435,
    "codeName" : "NotMasterNoSlaveOk",
    "$clusterTime" : {
        "clusterTime" : Timestamp(1525528863, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    }
} :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
Mongo.prototype.getDBs@src/mongo/shell/mongo.js:65:1
shellHelper.show@src/mongo/shell/utils.js:820:19
shellHelper@src/mongo/shell/utils.js:710:15
@(shellhelp2):1:1
test_set:SECONDARY> rs.slaveOk()
test_set:SECONDARY> show dbs
admin   0.000GB
config  0.000GB
local   0.000GB
test    0.000GB

test_set:SECONDARY> db.testCon.find()  //已经同步
{ "_id" : ObjectId("5aedb8b3aa48b68a53ecaa9e"), "name" : "Joe", "index" : 0 }
{ "_id" : ObjectId("5aedb8b3aa48b68a53ecaa9f"), "name" : "Joe", "index" : 1 }
{ "_id" : ObjectId("5aedb8b3aa48b68a53ecaaa0"), "name" : "Joe", "index" : 2 }
test_set:SECONDARY> db.testCon.insert({"name":"jan","index":"3"})  //slave节点没有insert权限的
WriteResult({ "writeError" : { "code" : 10107, "errmsg" : "not master" } })

模拟主节点故障:
kill掉 对应的进程
rs.status()
"stateStr" : "(not reachable/healthy)",

重新启动后,又被恢复为"primary"因为优先级高 //如果启动失败:删除掉 db文件夹下的mongod.lock文件

增加和删除节点:

mongod --port=1203 --bind_ip=127.0.0.1 --dbpath=/mongodb/db4/ --logpath=/mongodb/db4/logs/db4.log --replSet test_set --logappend --fork
test_set:PRIMARY> rs.add("127.0.0.1:1203")  //primary中操作,"_id"自动设置为4
test_set:PRIMARY> rs.status()
{
    "set" : "test_set",
    "date" : ISODate("2018-05-05T14:13:12.848Z"),
    "myState" : 1,
    "term" : NumberLong(3),
    "heartbeatIntervalMillis" : NumberLong(2000),
    "optimes" : {
        "lastCommittedOpTime" : {
            "ts" : Timestamp(1525529585, 1),
            "t" : NumberLong(3)
        },
        "readConcernMajorityOpTime" : {
            "ts" : Timestamp(1525529585, 1),
            "t" : NumberLong(3)
        },
        "appliedOpTime" : {
            "ts" : Timestamp(1525529585, 1),
            "t" : NumberLong(3)
        },
        "durableOpTime" : {
            "ts" : Timestamp(1525529585, 1),
            "t" : NumberLong(3)
        }
    },
    "members" : [
        {
            "_id" : 1,
            "name" : "127.0.0.1:1200",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 340,
            "optime" : {
                "ts" : Timestamp(1525529585, 1),
                "t" : NumberLong(3)
            },
            "optimeDate" : ISODate("2018-05-05T14:13:05Z"),
            "electionTime" : Timestamp(1525529263, 1),
            "electionDate" : ISODate("2018-05-05T14:07:43Z"),
            "configVersion" : 2,
            "self" : true
        },
        {
            "_id" : 2,
            "name" : "127.0.0.1:1201",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 339,
            "optime" : {
                "ts" : Timestamp(1525529585, 1),
                "t" : NumberLong(3)
            },
            "optimeDurable" : {
                "ts" : Timestamp(1525529585, 1),
                "t" : NumberLong(3)
            },
            "optimeDate" : ISODate("2018-05-05T14:13:05Z"),
            "optimeDurableDate" : ISODate("2018-05-05T14:13:05Z"),
            "lastHeartbeat" : ISODate("2018-05-05T14:13:12.176Z"),
            "lastHeartbeatRecv" : ISODate("2018-05-05T14:13:12.178Z"),
            "pingMs" : NumberLong(0),
            "syncingTo" : "127.0.0.1:1200", //同步1200端口
            "configVersion" : 2
        },
        {
            "_id" : 3,
            "name" : "127.0.0.1:1202",
            "health" : 1,
            "state" : 7,
            "stateStr" : "ARBITER",
            "uptime" : 339,
            "lastHeartbeat" : ISODate("2018-05-05T14:13:12.176Z"),
            "lastHeartbeatRecv" : ISODate("2018-05-05T14:13:12.178Z"),
            "pingMs" : NumberLong(0),
            "configVersion" : 2
        },
        {
            "_id" : 4,
            "name" : "127.0.0.1:1203",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 24,
            "optime" : {
                "ts" : Timestamp(1525529585, 1),
                "t" : NumberLong(3)
            },
            "optimeDurable" : {
                "ts" : Timestamp(1525529585, 1),
                "t" : NumberLong(3)
            },
            "optimeDate" : ISODate("2018-05-05T14:13:05Z"),
            "optimeDurableDate" : ISODate("2018-05-05T14:13:05Z"),
            "lastHeartbeat" : ISODate("2018-05-05T14:13:12.176Z"),
            "lastHeartbeatRecv" : ISODate("2018-05-05T14:13:12.384Z"),
            "pingMs" : NumberLong(0),
            "syncingTo" : "127.0.0.1:1201", //同步1201端口,而1201同步的是1200端口
            "configVersion" : 2
        }
    ],
    "ok" : 1,
    "operationTime" : Timestamp(1525529585, 1),
    "$clusterTime" : {
        "clusterTime" : Timestamp(1525529585, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    }
}

test_set:PRIMARY> rs.remove("127.0.0.1:1203")  //删除该节点

参考博客:
http://www.mongodb.org.cn/manual/ //中文文档
https://docs.mongodb.com/manual/ //英文官方
https://www.cnblogs.com/CraryPrimitiveMan/p/4622899.html //索引