一、创建操作命令
测试之前,我们创建一个demo数据库,直接使用命令“use demo”即可自动创建和选择到该数据库。
1、数据库操作
1、查看数据库列表
shwo databases
// 或者
show dbs
2、添加和选择数据库
// 自动创建并选择到该数据库
use 数据库名
3、查看当前数据库
// 直接使用db
db
4、查看数据库状态
db.stats()
2、创建集合和添加文档
MongoDB以 集合(collection) 的形式组织数据,collection 相当于关系型数据库中的表,如果collection不存在,当你对不存在的collection进行操作时,将会自动创建一个collection。
我们一般将集合中的元素称之为文档。
# 集合也就是关系型数据库中的表名称,这里不用创建可以直接添加
db.集合.insertOne(<JSON对象>) // 添加单个文档
db.集合.insertMany([{<JSON对象1>},{<JSON对象2>}]) // 批量添加文档
db.集合.insert() // 添加单个文档
insertOne, inertMany, insert 的区别:
- insertOne, 和 insertMany命令不支持 explain命令
- insert支持 explain命令
添加说明:
db.collection.insertOne(
doc ,
{
writeConcern: 安全级别 // 可选字段
}
)
writeConcern 定义了本次文档创建操作的安全写级别简单来说,安全写级别用来判断一次数据库写入操作是否成功,安全写级别越高,丢失数据的风险就越低,然而写入操作的延迟也可能更高。
writeConcern 决定一个写操作落到多少个节点上才算成功。
writeConcern的取值包括
- 0:发起写操作,不关心是否成功
- 1:集群中最大数据节点数: 写操作需要被复制到指定节点数才算成功
majority: 写操作需要被复制到大多数节点上才算成功。
发起写操作的程序将阻塞到写操作到达指定的节点数为止。
添加文档实例:
下面我们来创建集合members并添加一条文档:
db.members.insertOne({"name":"zhangsan", age:"19"})
插入文档时,如果没有显示指定主键,MongoDB将默认创建一个主键,字段固定为_id,ObjectId() 可以快速生成的12字节id 作为主键,ObjectId 前四个字节代表了主键生成的时间,精确到秒。主键ID在客户端驱动生成,一定程度上代表了顺序性,但不保证顺序性, 可以通过ObjectId(“id值”).getTimestamp() 获取创建时间。
如:
ObjectId("5fe0ef13ac05741b758b3ced").getTimestamp();
创建多个文档
db.collection.insertMany(
[ {doc } , {doc }, ....],
{
writeConcern: doc,
ordered: true/false
}
)
- ordered:觉得是否按顺序进行写入。
顺序写入时,一旦遇到错误,便会退出,剩余的文档无论正确与否,都不会写入
乱序写入,则只要文档可以正确写入就会正确写入,不管前面的文档是否是错误的文档
MongoDB以集合(collection)的形式组织数据,collection 相当于关系型数据库中的表,如果collection不存在,当你对不存在的collection进行操作时,将会自动创建一个collection。
如下:
将会创建一个 inventory (库存)集合,并且插入 5 个文档:
db.inventory.insertMany([
{ item: "journal", qty: 25, status: "A", size: { h: 14, w: 21, uom: "cm" }, tags: [ "blank", "red" ] },
{ item: "notebook", qty: 50, status: "A", size: { h: 8.5, w: 11, uom: "in" }, tags: [ "red", "blank" ] },
{ item: "paper", qty: 10, status: "D", size: { h: 8.5, w: 11, uom: "in" }, tags: [ "red", "blank", "plain" ] },
{ item: "planner", qty: 0, status: "D", size: { h: 22.85, w: 30, uom: "cm" }, tags: [ "blank", "red" ] },
{ item: "postcard", qty: 45, status: "A", size: { h: 10, w: 15.25, uom: "cm" }, tags: [ "blue" ] }
]);
上述操作返回一个包含确认指示符的文档和一个包含每个成功插入文档的_id的数组。
二、查询操作命令和常用函数
1、N/A
2、查看集合
其实mongo中有些命令和关系型数据库的是相同的,肯能是为了让使用人员更顺手。
比如查看集合的命令可以用:
show tables
show collections
这两种都可以使用。
3、文档简单查询
首先来看API:
// 查询所有的文档,没有过滤条件可以省略json,大括号也可以不写。
// 过滤查询的话,可以将查询条件json字符卸载find函数中
db.集合.find({})
db.集合.find({}).pretty() //返回格式化后的文档
比如我们来查刚才创建的members集合中的文档:
> db.members.find()
{ "_id" : ObjectId("6136e959a6195631a56c55e8"), "name" : "zhangsan", "age" : "19" }
> db.members.find().pretty()
{
"_id" : ObjectId("6136e959a6195631a56c55e8"),
"name" : "zhangsan",
"age" : "19"
}
4、文档条件查询
这里的集合我们都以上面创建的inventory库存为例!
> db.inventory.find().pretty()
{
"_id" : ObjectId("6136ef17a6195631a56c55e9"),
"item" : "journal",
"qty" : 25,
"status" : "A",
"size" : {
"h" : 14,
"w" : 21,
"uom" : "cm"
},
"tags" : [
"blank",
"red"
]
}
{
"_id" : ObjectId("6136ef17a6195631a56c55ea"),
"item" : "notebook",
"qty" : 50,
"status" : "A",
"size" : {
"h" : 8.5,
"w" : 11,
"uom" : "in"
},
"tags" : [
"red",
"blank"
]
}
{
"_id" : ObjectId("6136ef17a6195631a56c55eb"),
"item" : "paper",
"qty" : 10,
"status" : "D",
"size" : {
"h" : 8.5,
"w" : 11,
"uom" : "in"
},
"tags" : [
"red",
"blank",
"plain"
]
}
{
"_id" : ObjectId("6136ef17a6195631a56c55ec"),
"item" : "planner",
"qty" : 0,
"status" : "D",
"size" : {
"h" : 22.85,
"w" : 30,
"uom" : "cm"
},
"tags" : [
"blank",
"red"
]
}
{
"_id" : ObjectId("6136ef17a6195631a56c55ed"),
"item" : "postcard",
"qty" : 45,
"status" : "A",
"size" : {
"h" : 10,
"w" : 15.25,
"uom" : "cm"
},
"tags" : [
"blue"
]
}
1. 精准等值查询
db.inventory.find( { status: "D" } );
db.inventory.find( { qty: 0 } );
2. 多条件查询
db.inventory.find( { qty: 0, status: "D" } );
3. 嵌套对象精准查询
db.inventory.find( { "size.uom": "in" } );
4. 返回指定字段
只需要在查询条件后面再加一个json来描述期望返回的字段即可,值设置成1表示返回。不设置或者0表示不返回。
db.inventory.find( { }, { item: 1, status: 1 } );
默认会返回_id 字段, 同样可以通过指定 _id:0 ,不返回_id 字段。
5. 条件查询 and
db.inventory.find({ $and:[{"qty":0},{"status":"D"}] });
6. 条件查询 or
db.inventory.find(
{
$or:[
{"qty":0},
{"status":"A"}
]
}
);
5、Mongo查询条件和SQL查询对照表
6、复合主键
可以使用文档作为复合主键:
db.demeDoc.insert(
{
_id: { product_name: 1, product_type: 2},
supplierId:" 001",
create_Time: new Date()
}
)
注意复合主键,字段顺序换了,会当做不同的对象被创建,即使内容完全一致.
7、逻辑操作符匹配
- $not : 匹配筛选条件不成立的文档
- $and : 匹配多个筛选条件同时满足的文档
- $or : 匹配至少一个筛选条件成立的文档
- $nor : 匹配多个筛选条件全部不满足的文档
构造一组数据:
db.users.insertMany([
{
nickName:"曹操",
points:1000
},
{
nickName:"刘备",
points:500
}
]);
$not用法
{ field: { $not : { operator-expression} }}
测试:查找积分不小于100的用户】
PS:不小于就是大于的意思!!!
db.users.find(
{
points: {
$not: { $lt: 100}
}
}
);
注意:$not 也会筛选出并不包含查询字段的文档
$and 用法
{ $and : [ condition expression1 , condition expression2 ..... ]}
测试:查询昵称等于曹操, 积分大于 500 的文档
db.users.find(
{
$and : [
{nickName:{$eq:"曹操"}},
{points:{$gt:500}}
]
}
)
当作用在不同的字段上时可以省略 $and 。
上面的查询等价于下面的:
db.users.find(
{
nickName:{$eq:"曹操"},
points:{$gt:500}
}
)
当作用在同一个字段上面时可以简化为:
// 查询积分大于等于500 && 小于等于1000
db.users.find(
{
points:{$gte:500, $lte:1000}
}
)
$or用法
{ $or :{ condition1, condition2, condition3,... }}
测试:查询昵称是刘备或者积分等于500的用户
db.users.find(
{$or :
[
{nickName:{$eq:"刘备"}},
{points:{$gt:500}}
]
} )
如果都是等值查询的话, $or 和 $in 结果是一样的。
8、字段匹配
$exists:判断查询字段是否存在
{ field : {$exists: <boolean>} }
测试:查询users集合中存在points字段的记录
db.users.find(
{
points: {$exists: true}
}
)
测试:查询users集合中不存在age字段的记录
db.users.find(
{
age: {$exists: false}
}
)
9、文档游标
游历完游标中所有文档之后,或者在10分钟之后,游标便会自动关闭。
可以使用noCursorTimeout()函数来保持游标一直有效。
var cursor=db.users.find().noCursorTimeout()
在这之后,在不遍历游标的情况下,你需要主动关闭游标:
cursor.close()
10、游标函数
cursor.hasNext() //当还有没遍历完的游标文档时,返回true
cursor.next() //获取下一次遍历的游标文档
cursor.forEach(<function>) //遍历游标中所有指向的文档
cursor.limit(<number>) //返回指定条数
cursor.skip(<offest>) //跳过文档中指定数量的文档(传入1表示跳过第一篇文档)
cursor.count(<applySkipLimit>) //默认情况下,<applySkipLimit>为false,即cursor.count()不会考虑cursor.skip()和cursor.limit()的效果
cursor.sort(<document>) //这里的<document>定义了排序的要求(1从小到大排序,-1从大到小排序)
默认情况下 , 这里的count不会考虑 skip 和 limit的效果,如果希望考虑 limit 和 skip ,需要设置为 true。 分布式环境下,count 不保证数据的绝对正确。
注意: cursor.skip(),cursor.limit(),cursor.count()的优先级
优先级:count() —> skip() —> limit()
sort排序
cursor.sort( <doc>)
这里的< doc > 定义了排序的要求:
{ field: ordering}
- 1 表示由小到大, -1 表示逆向排序
- 当同时应用 sort, skip, limit 时 ,应用的顺序为 sort, skip, limit
11、文档投影
不使用投影时,db.users.find()返回符合筛选条件的完整文档。
使用投影可以只返回指定的字段。
用法:
db.collection.find({查询条件}, {投影设置})
投影设置:
{ field: < 1 :1 表示需要返回, 0: 表示不需要返回 , 只能为 0,或者1 , 非主键字段,不能同时混选0 或 1. 主键id是可以混选的!>}
测试:查询users中的文档,只返回用户的昵称。
db.users.find(
{},
{_id:0, points:0}
)
注意,下面的这种是错误的写法,非主键字段只能同时是1或者0!不能混用!
12、$slice 返回数组中的部分元素
可以使用 $slice 返回数组中的部分元素。
slice的值,正数表示返回前几个,负数表示返回倒数几个。
- 1: 数组第一个元素
- -1:最后一个元素
- -2:最后两个元素
- slice[ 1,2 ] : skip, limit 对应的关系。表示从第2个( 因为第一个文档序号为0)文档开始取两个。
如, 先添加一个数组元素的文档:
db.users.insertOne(
{
_id: {uid:3,accountType: "qq"},
nickName:"张飞",
points:1200,
address:
[
{address:"xxx",post_no:0000},
{address:"yyyyy",post_no:0002}
]
}
);
返回数组的第一个元素
测试:返回用户的第一个地址信息
db.users.find(
{},
{
_id:0,
nickName:1,
points:1,
address: {$slice:1}
}
)
返回倒数第一个
测试:返回用户的最后一个地址信息
db.users.find(
{},
{
_id:0,
nickName:1,
points:1,
address: {$slice:-1}
}
)
返回最后两个
db.users.find(
{},
{
_id:0,
nickName:1,
points:1,
address: {$slice:-2}
}
)
从第1个文档开始取1个
db.users.find(
{},
{
_id:0,
nickName:1,
points:1,
address: {$slice:[0, 1]}
}
)
13、elementMatch数组元素匹配
添加一组数据:
db.users.insertOne(
{
_id: {uid:4,accountType: "qq"},
nickName:"张三",
points:1200,
tag:["student","00","IT"]
}
);
测试:查询tag数组中第一个匹配"00" 的元素
db.users.find(
{},
{_id:0,
nickName:1,
points:1,
tag: { $elemMatch: {$eq: "00" } }
});
$elemMatch 和 $eq 操作符可以返回数组字段中满足条件的第一个元素。
14、查询数组中的对象
三、更新操作命令
updateOne/updateMany 方法要求更新条件部分必须具有以下之一,否则将报错。
- $set:给符合条件的文档新增一个字段,有该字段则修改其值
- $unset:给符合条件的文档,删除一个字段
- $push:增加一个对象到数组底部
- $pop:从数组底部删除一个对象
- $pull:如果匹配指定的值,从数组中删除相应的对象
- $pullAll:如果匹配任意的值,从数据中删除相应的对象
- $addToSet:如果不存在则增加一个值到数组
1、更新文档-添加修改字段
单条插入数据, 插入两条:
db.userInfo.insert([
{ name:"zhansan",
tag:["90","Programmer","PhotoGrapher"]
},
{ name:"lisi",
tag:["90","Accountant","PhotoGrapher"]
}])
修改多个文档:updateMany
测试:将tag 中有90 的文档,增加一个字段: flag: 1
db.userInfo.updateMany(
{tag:"90"},
{$set:{flag:1}}
);
修改单个文档:updateOne
db.userInfo.updateOne(
{tag:"90"},
{$set:{flag:2}}
);
查询
基于上面这两条数据,可以来查询一下数组中的元素:
userInfo 中,会计和程序员的文档:
db.userInfo.find(
{$or:
[
{tag:"Accountant"},
{tag:"Programmer"}
]
});
userInfo 中,90后的文档:
db.userInfo.find({tag:"90"});
2、修改文档
db.collection.update( <query>,<update>,<options>)
- < query> 定义了更新时的筛选条件
- < update> 文档提供了更新内容
- < options> 声明了一些更新操作的参数
更新文档操作只会作用在第一个匹配的文档上.
如果< update> 不包含任何更新操作符,则会直接使用update文档替换集合中符合文档筛选条件的文档.
更新特定字段
db.collection.update( <query>,<update>,<options>)
- < query> 定义了更新时的筛选条件
- < update> 文档提供了更新内容
- < options> 声明了一些更新操作的参数
如果< update>只包含更新操作符,db.collection.update() 将会使用update更新集合中符合< query>筛选条件的文档中的特定字段。
默认只会更新第一个匹配的值,可以通过设置 options {multi: true} 设置匹配多个文档并更新。
测试:
db.userInfo.update(
{name:"zhansan"},
{$set:{ flag: 1 }},
{multi:true}
);
3、更新操作符
- $set 更新或新增字段
- $unset删除字段
- $rename 重命名字段
- $inc 加减字段值
- $mul 相乘字段值
- $min 采用最小值
- $max 次用最大值
四、集合文档删除
1、删除文档
db.collection.remove(<query>,<options>)
默认情况下,会删除所有满足条件的文档, 可以设定参数 { justOne:true},只会删除满足添加的第一条文档。
测试:删除集合userInfo中第一个名称为“zhansan”的文档
db.userInfo.remove({name:"zhansan"}, {justOne:true})
2、删除集合
db.collection.drop( { writeConcern:<doc>})
< doc> 定义了本次删除集合操作的安全写级别。
这个指令不但删除集合内的所有文档,且删除集合的索引。
db.collection.remove()
remove 只会删除所有的文档,直接使用remve删除所有文档效率比较低,可以使用 drop 删除集合,再重新创建集合以及索引。