目录
假设使用test数据库的user集合,有三条测试数据
查询
函数原型:db.collection_name.find(query,projection)
collection_name即集合的名称,query是查询条件(相当于SQL中的where子句),projection是投影条件(相当于SQL中的select子句),后两者都是可以不写的:
> use test
switched to db test
> db.user.insert({"name":"zhangsan","sex":"man","age":20,"hobby":"programming"})
WriteResult({ "nInserted" : 1 })
> db.user.find()
{ "_id" : ObjectId("5c3eef6d7da85af675c7c107"), "name" : "zhangsan", "sex" : "man", "age" : 20, "hobby" : "programming" }
{ "_id" : ObjectId("5c3eefee7da85af675c7c108"), "name" : "lisi", "sex" : "woman", "age" : 16, "hobby" : "music" }
{ "_id" : ObjectId("5c3ef0037da85af675c7c109"), "name" : "wangwu", "sex" : "man", "age" : 18, "hobby" : "read book" }
可以看到,什么都不写就相当于 select * from user
下面测试带条件的查询:
> db.user.find({name:"zhangsan"})
{ "_id" : ObjectId("5c3eef6d7da85af675c7c107"), "name" : "zhangsan", "sex" : "man", "age" : 20, "hobby" : "programming" }
> db.user.find({age:{$gt:17}})
{ "_id" : ObjectId("5c3eef6d7da85af675c7c107"), "name" : "zhangsan", "sex" : "man", "age" : 20, "hobby" : "programming" }
{ "_id" : ObjectId("5c3ef0037da85af675c7c109"), "name" : "wangwu", "sex" : "man", "age" : 18, "hobby" : "read book" }
> db.user.find({age:{$gt:17}},{name:1})
{ "_id" : ObjectId("5c3eef6d7da85af675c7c107"), "name" : "zhangsan" }
{ "_id" : ObjectId("5c3ef0037da85af675c7c109"), "name" : "wangwu" }
第一条语句相当于 select * from user where name="zhangsan",第二条相当于 select * from user where age>17
这里使用的文档结构比较简单,假如有嵌套文档,可以用"子文档名.子文档字段名"的形式来选择字段,多层嵌套依此类推
可以看到,查询条件中,key是不需要带上双引号的
$gt代表大于号,类似的运算符还有:
操作 | 格式 |
---|---|
等于 | {<key>:<value> } |
小于 | {<key>:{$lt:<value>}} |
小于或等于 | {<key>:{$lte:<value>}} |
大于 | {<key>:{$gt:<value>}} |
大于或等于 | {<key>:{$gte:<value>}} |
不等于 |
|
条件非 | {<key>:{$not:条件} |
逻辑或 | {$or:[{<key1>:<value1> },{<key2>:<value2> }]} |
非或 | {$nor:[{<key1>:<value1> },{<key2>:<value2> }]} |
条件与 | {$and:[条件1,条件2,…]} |
存在 | {<key>:{$exists:true/false}} |
类型查询 |
|
逻辑与就是多个查询条件并列,一个逻辑或的例子:
> db.user.find({$or:[{age:{$gt:18}},{sex:"man"}]})
{ "_id" : ObjectId("5c3eef6d7da85af675c7c107"), "name" : "zhangsan", "sex" : "man", "age" : 20, "hobby" : "programming" }
{ "_id" : ObjectId("5c3ef0037da85af675c7c109"), "name" : "wangwu", "sex" : "man", "age" : 18, "hobby" : "read book" }
相当于 select name from user where age>17 or sex="man"
类型查询顾名思义,就是不按照值,而是按类型进行查找,假设有个人,名字就是数字,那么:
> db.user.find({name:{$type:'number'}})
{ "_id" : ObjectId("5c3f22a87da85af675c7c10a"), "name" : 10 }
类型和其数值对应如下:
Number | Alias |
---|---|
1 | “double” |
2 | “string” |
3 | “object” |
4 | “array” |
5 | “binData” |
7 | “objectId” |
8 | “bool” |
9 | “date” |
10 | “null” |
11 | “regex” |
13 | “javascript” |
15 | “javascriptWithScope” |
16 | “int” |
17 | “timestamp” |
18 | “long” |
-1 | “minKey” |
127 | “maxKey” |
number是广义数字类型
除此之外,还有范围查询操作符:
操作符 | 说明 |
$in | 匹配以下文档:包含给定集合中至少一个元素 |
$all | 匹配以下文档:包含给定集合中全部元素 |
$nin | 匹配以下文档:不包含给定集合中任何元素 |
例子:
> db.user.find({hobby:{$in:["programming","music"]}})
{ "_id" : ObjectId("5c3eef6d7da85af675c7c107"), "name" : "zhangsan", "sex" : "man", "age" : 21, "hobby" : "programming" }
{ "_id" : ObjectId("5c3eefee7da85af675c7c108"), "name" : "lisi", "sex" : "woman", "age" : 16, "hobby" : "music" }
> db.user.find({hobby:{$all:["programming","music"]}})
> db.user.find({hobby:{$nin:["programming","music"]}})
{ "_id" : ObjectId("5c3ef0037da85af675c7c109"), "name" : "wangwu", "sex" : "man", "age" : 18, "hobby" : "read book" }
{ "_id" : ObjectId("5c3f1885cce0b679769390fa"), "name" : "lucy", "age" : 22, "hobby" : "movie", "sex" : "woman" }
{ "_id" : ObjectId("5c3f1bbfcce0b67976939109"), "name" : "tom", "age" : 2 }
{ "_id" : ObjectId("5c3f22a87da85af675c7c10a"), "name" : 10 }
以及数组查询操作符:
操作符 | 说明 | 用法 |
---|---|---|
$elemMatch | 匹配成员数组中包含某个元素的文档 | {<key>:{$elemMatch:<value>}} |
$size | 匹配数组元素数相同的文档 | {<key>:{$size:<number>}} |
MongoDB还提供了一个非常强力的查询操作符:$where,它可以使用JavaScript函数来选择数据,使用this指针获取文档中的成员:
> db.user.find({$where:"function(){return this.age>=20}"})
{ "_id" : ObjectId("5c3eef6d7da85af675c7c107"), "name" : "zhangsan", "sex" : "man", "age" : 21, "hobby" : "programming" }
{ "_id" : ObjectId("5c3f1885cce0b679769390fa"), "name" : "lucy", "age" : 22, "hobby" : "movie", "sex" : "woman" }
像示例中这种简单的查询,简写形式也是完全等效的:
db.user.find({$where:"this.age>=20"})
第三条语句相当于 select name from user where age>17 ,"{name:1}"代表投影到name这个域
projection有inclusion和exclusion两种模式,但不能混用:
> db.user.find({age:{$gt:17}},{name:1}) //inclusion模式,仅包含指定域
{ "_id" : ObjectId("5c3eef6d7da85af675c7c107"), "name" : "zhangsan" }
{ "_id" : ObjectId("5c3ef0037da85af675c7c109"), "name" : "wangwu" }
> db.user.find({age:{$gt:17}},{name:0}) //exclusion模式,仅排除指定域
{ "_id" : ObjectId("5c3eef6d7da85af675c7c107"), "sex" : "man", "age" : 20, "hobby" : "programming" }
{ "_id" : ObjectId("5c3ef0037da85af675c7c109"), "sex" : "man", "age" : 18, "hobby" : "read book" }
> db.user.find({age:{$gt:17}},{name:1,age:0}) //混用两种模式,报错
Error: error: {
"ok" : 0,
"errmsg" : "Projection cannot have a mix of inclusion and exclusion.",
"code" : 2,
"codeName" : "BadValue"
}
可以看到,两种模式下,_id域都会出现在结果中,如果想隐藏该域,只能显式指定{_id:0}
MongoDB还提供了一个findOne函数,顾名思义,是仅仅返回一个结果:
> db.user.findOne({$or:[{age:{$gt:18}},{sex:"man"}]})
{
"_id" : ObjectId("5c3eef6d7da85af675c7c107"),
"name" : "zhangsan",
"sex" : "man",
"age" : 20,
"hobby" : "programming"
}
并且返回的文档自动进行了格式化,更清晰,find().limit(1).pretty()效果相同:
> db.user.find({$or:[{age:{$gt:18}},{sex:"man"}]}).limit(1).pretty()
{
"_id" : ObjectId("5c3eef6d7da85af675c7c107"),
"name" : "zhangsan",
"sex" : "man",
"age" : 21,
"hobby" : "programming"
}
类似pretty(),find()函数还有些增强函数:
- limit(number):限定查询数量
- skip(number):跳过查询结果的前number条
- sort({key1:1/-1,key2:1/-1,…}):按照key1、key2进行排序,1代表升序,-1代表降序
结合limit和skip可以实现分页,但仅适用小规模数据,因为skip是先查询,再一个个跳过,对于大规模数据,其性能很差,可以加个自增字段,结合$gt运算符和limit实现分页
查询的结果其实就是一个BSON数据,因此可以赋给一个对象,并对其操作:
> zhangsan = db.user.findOne({name:"zhangsan"})
{
"_id" : ObjectId("5c3eef6d7da85af675c7c107"),
"name" : "zhangsan",
"sex" : "man",
"age" : 21,
"hobby" : "programming"
}
> zhangsan['age']=22
22
> zhangsan
{
"_id" : ObjectId("5c3eef6d7da85af675c7c107"),
"name" : "zhangsan",
"sex" : "man",
"age" : 22,
"hobby" : "programming"
}
这种数据替换不会对原始数据产生影响
插入
在 MongoDB学习(一):安装&基础概念&数据类型&部分shell操作 最后提到了插入方法,不再重复
更新
函数原型:db.collection_name.update(query,update,options)
collection_name和query的含义同find方法一样,update相当于SQL中的set子句,options有三种:
- upsert : 可选,这个参数的意思是,如果要更新的文档不存在,是否插入新文档,true为插入,默认是false不插入。
- multi : 可选,默认false,代表只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条所有记录全部更新。
- writeConcern :可选,抛出异常的级别。形式为:writeConcern :{ w: <value>, j: <boolean>, wtimeout: <number> },用在分布式环境下
- w:代表更新结果返回前,在集群中需要有多少台机器确认结果
-
w:0代表不需要确认
-
w:1代表只要主节点确认
-
w:2代表主节点和至少一个从节点确认,w=3、4、…… 依此类推
-
w:"majority"代表需要过半成员确认
-
w:{tag1,tag2……}代表需要指定tag的分片确认
-
-
j:代表是否需要写入journal文件
-
wtimeout:代表确认结果操作的超时时间
- w:代表更新结果返回前,在集群中需要有多少台机器确认结果
举个例子:
> db.user.update({name:"zhangsan"},{$set:{age:21}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find({name:"zhangsan"})
{ "_id" : ObjectId("5c3eef6d7da85af675c7c107"), "name" : "zhangsan", "sex" : "man", "age" : 21, "hobby" : "programming" }
> db.user.update({name:"tom"},{$set:{age:22,sex:"man",hobby:"pc games"}},{upsert:true})
WriteResult({
"nMatched" : 0,
"nUpserted" : 1,
"nModified" : 0,
"_id" : ObjectId("5c3f1885cce0b679769390fa")
})
> db.user.find({name:"tom"})
{ "_id" : ObjectId("5c3f1885cce0b679769390fa"), "name" : "tom", "age" : 22, "hobby" : "pc games", "sex" : "man" }
这里update部分使用的是$set操作符,其它操作符还有:
- $set:{field:value} 更新指定键的值为value
- $unset:{field:1} 删除指定键
- $inc:{field:value} 对field的值增加value(仅限数字类型)
- $push:{field:value} 向数组field添加一个元素value
- $pushAll:{field:array} 将array中所有元素都添加到field中
- $pull:{field:_value} 从数组field中删除一个值为value的元素(注意下划线)
- $addToSet:{field:value} 增加一个值value到集合field内。
- { $pop : { field : 1/-1 } } 删除数组第一个/最后一个元素
- { $rename : { old_field_name : new_field_name } } 修改域的key
- {$bit : { field : {bit_operator : number}}} 位操作
- 偏移操作符:{'comments.by':'joe'}, {$inc:{'comments.$.votes':1}} 相当于对comments数组中,所有字段"by"为"joe"的子文档的"votes"字段+1
例如inc操作:
> db.user.update({name:"tom"},{$inc:{age:2}})
WriteResult({
"nMatched" : 0,
"nUpserted" : 1,
"nModified" : 0,
"_id" : ObjectId("5c3f1885cce0b679769390fa")
})
> db.user.find({name:"tom"})
{ "_id" : ObjectId("5c3f1885cce0b679769390fa"), "name" : "tom", "age" : 24, "hobby" : "pc games", "sex" : "man" }
tom的年龄大了两岁
可以看到,使用upsert选项后,文档不存在时会插入新文档
和insert类似,update也有updateOne和updateMany两个变体
还有两个作用类似的函数:
- db.collection_name.save(document,{writeConcern:{w:<value>,j:<boolean>,wtimeout:<number>}})
会根据主键进行文档替换:
> db.user.save({ "_id" : ObjectId("5c3f1885cce0b679769390fa"), "name" : "lucy", "age" : 22, "hobby" : "movie", "sex" : "woman" })
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find({name:"lucy"})
{ "_id" : ObjectId("5c3f1885cce0b679769390fa"), "name" : "lucy", "age" : 22, "hobby" : "movie", "sex" : "woman" }
可以看到,tom已经变成lucy了
- db.collection_name.findAndModify(query,update) 是原子性操作,即这个函数要么完成,要么不生效
- 注意:由于该函数不存在options参数,因此是默认upsert的,即如果没找到要修改的文档,就会插入新的
删除
函数原型:db.collection_name.remove(query,options)
重复的不解释了,options有两个:
- justOne : (可选)如果设为 true 或 1,则只删除一个文档,如果不设置该参数,或使用默认值 false,则删除所有匹配的文档。
- writeConcern :和update的同名选项作用一致
和其它函数不同的是,其变体名字叫deleteOne、deleteMany