一 基本概念
MongoDB中数据的结构为:库、集合、文档
1 数据库
多个集合可以组成数据库。MongoDb的单个实例可以容纳多个独立的数据库,每个都有自己的集合和权限,不同的数据库也放置在不同的文件中。
数据库通过名字来标识,库名可以是满足以下条件的任意UTF-8字符串:
不能是空字符串("")。
不得含有' '(空格)、.、$、/、\和\0 (空宇符)。
应全部小写。
最多64字节。
Mongo保留库
admin:从权限角度看,这是“root”数据库。要是将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。一些特定的服务端命令也只能从这个数据库运行,比如关闭数据库。
local:这个数据库不会被复制。可以用来存储限于需要储存在本地单台服务器的集合
config:当Mongo用于分片设置时,config数据库在内部使用,用于保存分片的信息。
2 集合
集合容纳文档,没有模式。比起关系型数据库,集合可以存放任意文档。但把各种文档放进一个集合在管理和效率是都不是好的方式,推荐用关系型数据库的思维来管理文档结构。
集合通过名字来标识,集合名可以是满足以下条件的任意UTF-8字符串:
集合名不能是空字符串""。
集合名不能含有\0字符(空字符),这个字符表示集合名的结尾。
集合名不能以"system."开头,这是为系统集合保留的前缀。
用户创建的集合名字不能含有保留字符。有些驱动程序的确支持在集合名里面包含,这是因为某些系统生成的集合中包含该字符。除非你要访问这种系统创建的集合,否则千万不要在名字里出现$。
3 文档
文档是MongoDB中数据的基本单元(类似于关系型数据库中的行)。多个键及其关键的值有序的放置在一起便是文档。MongoDB的文件储存格式为BSON。
示例:{"name":"dongbo","age":"100"}
注意:
文档一定是用“{}”括起来
文档的键是双引号括起来的字符串,不能重复
不能含有\0 (空字符)。这个字符用来表示键的结尾。
.和$有特别的意义,只有在特定环境下才能使用。
以下划线"_"开头的键是保留的(不是严格要求的)。
文档的值可以是字符串、数字、数组、文档等mongo支持的数据类型。字符串需用双引号括起来。
MongoDB区分类型和大小写
键值对是有序的。
二 查询
find函数查询集合中所有符合查询条件的文档并返回结果为游标的文档集合。findOne查询一个符合查询条件的文档。
在mongo shell中我们不需要JavaScript游标处理方法就可以直接访问作为查询结果的文档集合。当执行查询操作时,mongo shell直接自动的对游标执行迭代操作并显示前20条文档,输入"it"显示接下来的20条文档。
设置显示文档数:DBQuery.shellBatchSize=50,设置完后执行find函数就能显示出前50条文档记录。
如果我们通过变量保存find返回的游标,其不会自动进行遍历:
var cursor = db.inventory.find()
实际发生的是,调用find后,此时并不会真正的访问数据库,而是等待开始要求获取结果的时候才向数据库发送查询请求。当调用hasNext或者next方法时才会真正访问数据库,这就是懒加载的过程。
先插入一些数据:
db.inventory.insert({"name":"t1","amount":16,"tags":["apple", "banana"]})
db.inventory.insert({"name":"t2","amount":50,"tags":["banana", "orange"]})
db.inventory.insert({"name":"t3","amount":58,"tags":["orange", "apple"]})
1 游标操作
处理游标有三个函数:limit、skip、sort。limit用来限制返回的文档数量,skip用来指定忽略前面的n条文档,sort用来对文档进行排序。三个函数有优先级关系:先sort排序,然后skip跳过前面n个值,最后limit限制文档最大返回数量。
db.inventory.find({"amount":{$lt:60}}).sort({"amount":1}).skip(1).limit(2)
{ "_id" :ObjectId("55a13b26b26f97b960389a5e"), "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }
{ "_id" :ObjectId("55a13b27b26f97b960389a5f"), "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }
示例中操作语句的写法只是示意优先级关系,返回结果跟3个函数的书写顺序无关。
示例中按照amount排序,因为amount的值都是整数,所以排序依据数字的大小而来。然而Mongo中对键并不会强制要求是什么类型,一个集合中多个文档的某一个键对应的值可能是mongodb支持的不同类型,应对这种情况,MongoDb预先定义了一个顺序,从小到大依次为:
(1):最小值
(2):null
(3):数字(整型,长整型,双精度)
(4):字符串
(5):对象/文档
(6):数组
(7):二进制数据
(8):对象ID
(9):布尔值
(10):日期型
(11):时间戳
(12):正则表达式
(13):最大值
按照这个顺序,便可以跨类型排序。
排序示例:
db.inventory.find().sort({"amount":-1})
{ "_id" :ObjectId("55a13b27b26f97b960389a5f"), "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }
{ "_id" :ObjectId("55a13b26b26f97b960389a5e"), "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }
{ "_id" :ObjectId("55a13b26b26f97b960389a5d"), "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }
限制文档行数示例:
db.inventory.find().limit(2)
{ "_id" :ObjectId("55a13b26b26f97b960389a5d"), "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }
{ "_id" :ObjectId("55a13b26b26f97b960389a5e"), "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }
2 包装
包装查询会用到一些额外的键:
$query
$orderby
$maxscan : integer指定查询时最多扫描文档的数量
$min : document查询的开始条件
$max : document查询的结束条件
$hint : document指定服务器使用哪些索引进行查询
$explain : boolean获取查询细节,如用到的索引,结果数量,耗时等,类似于关系数据库这边查看执行计划。并不会真正执行查询
$snapshot:boolean确保查询的结果是在查询执行那一刻的一致快照。
示例:
db.inventory.find({$query:{amount:{$lt:58}},$orderby:{amount:-1},$hint:{"name":1}},{"_id":0})
{ "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }
{ "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }
3 查询指定的键
示例:
db.inventory.find({},{"_id":0,"name":1,"tags":1})
{ "name" :"t1", "tags" : [ "apple", "banana" ] }
{ "name" :"t2", "tags" : [ "banana", "orange" ] }
{ "name" :"t3", "tags" : [ "orange", "apple" ] }
db.inventory.find({"amount":{$eq:50}},{"_id":0,"name":1,"tags":1})
{ "name" :"t2", "tags" : [ "banana", "orange" ] }
“0”表示不显示,“1”表示显示
4 使用正则表达式
示例:
db.inventory.find({"name":/1/},{"_id":0,"name":1,"tags":1})
{ "name" :"t1", "tags" : [ "apple", "banana" ] }
返回name中匹配“1”的结果
5 去除重复
示例:
db.inventory.distinct("name")
[ "t1","t2", "t3" ]
去重的作用是对某个键查询的结果除去重复结果,关系型数据库中distinct的作用也一样
6 查询集合中总文档数:
db.inventory.count()
7 查询内嵌文档:
查询文档有两种方式,一种是完全匹查询,另一种是针对键/值对查询。
db.profile.insert({"name" : [{"first" : "db", "last":"z"}]})
db.profile.insert({"name" : [{"first" : "bing", "last" : "pan" }]})
db.profile.insert({"name" : [{"first" : "bol", "last" : "z" }]})
内嵌文档的完全匹配查询和数组的完全匹配查询一样,内嵌文档内键值对的数量,顺序都必须一致才会匹配:
db.profile.find({"name.first":"bol"})
{ "_id" :ObjectId("55a15e53b26f97b960389a6c"), "name" : [ {"first" : "bol", "last" : "z" } ] }
采用针对键/值对查询,通过点来精确表示内嵌文档的键。当内嵌文档变得复杂后,如键的值为内嵌文档的数组,内嵌文档的匹配需要些许技巧,例如使用$elemMatch操作符。
db.profile.find({"name":{$elemMatch:{"first":"bing","last":"pan"}}})
{ "_id" :ObjectId("55a15e53b26f97b960389a6b"), "name" : [ {"first" : "bing", "last" : "pan" } ] }
elemMatch投影操作符将限制查询返回的数组字段的内容只包含匹配elemMatch条件的数组元素。
注意:
数组中元素是内嵌文档。
如果多个元素匹配$elemMatch条件,操作符返回数组中第一个匹配条件的元素。
8 控制查询返回的数组中元素的个数
使用$slice操作符,$slice:1的意思是返回数组值中前1个元素
db.inventory.find({amount:{$gt:50}},{"_id":0})
{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }
db.inventory.find({amount:{$gt:50}},{tags:{$slice:1},"_id":0})
{ "name" :"t3", "amount" : 58, "tags" : ["orange" ] }
$slice:[1,1],第一个1表示在数组中跳过的项目数,第二个值表示返回的项目数。
db.inventory.find({amount:{$gt:50}},{tags:{$slice:[1,1]},"_id":0})
{ "name" :"t3", "amount" : 58, "tags" : [ "apple"] }
9 查询操作符:
数组查询操作符:
$all
用法:{ field : {$all:[value1,value2]}},查询数组中包含指定值的文档,条件的数组是文档中已有数组的子集
示例:
db.inventory.find({"tags":{$all:["apple"]}},{"_id":0})
{ "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }
{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }
当只需要匹配数组的一个值时,简便写法:
db.inventory.find({"tags":"apple"},{"_id":0})
{ "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }
{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }
$size
用法:{ field:{ $size:size } },查询数组中元素的数量等于size的文档,size必须是数字
示例:
db.inventory.find({"tags":{$size:2}},{"_id":0})
{ "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }
{ "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }
{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }
$elemMatch
用法:{field:{$elemMatch:{exp}}} ,文档内部存在由文档作为元素组成数组,如果要以数组中的文档作为查询条件,就可以用$elemMatch操作符指向内部文档。
算术操作符
$gt
用法:{ field : { $gt:value } },查询键值大于指定值的所有文档
示例:
db.inventory.find({"amount":{$gt:50}},{"_id":0})
{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }
$gte
用法:{ field: { $gte: value } },查询键值不小于指定值的所有文档。
$lt
用法:{ field: { $lt: value } },查询键值小于指定值的所有文档。
$lte
用法:{ field: { $lte: value } },查询键值不大于指定值的所有文档。
$eq
用法:{ field: { $eq: value } },查询键值等于指定值的所有文档。
$ne
用法:{ field: { $ne: value } },查询键值不等于指定值的所有文档。
$in
用法:{ field: { $in: [value1,value2] } },查询键值等于指定数组中任意值的文档
示例:
db.inventory.find( { amount : {$in:[50,58]} },{"_id":0})
{ "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }
{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }
$nin
用法:{ field: { $nin: [ value1, value2]} },查询键不存在或者键值不等于指定数组的任意值的文档
示例:
db.inventory.find({amount:{$nin:[50,58]}},{"_id":0})
{ "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }
逻辑操作符
$and
用法:{ $and: [ { }, { } ] },$and指定一个至少包含两个表达式的数组,选择出满足该数组中所有表达式的文档。and操作符使用短路操作,若第一个表达式的值为“false”,余下的表达式将不会执行。
示例:
db.inventory.find({$and:[{"amount":{$gt:50}},{"amount":{$lt:70}}]},{"_id":0})
{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }
$or
用法:{ $or: [ { }, { }] },$or执行逻辑OR运算,指定一个至少包含两个表达式的数组,选择出至少满足数组中一条表达式的文档。
示例:
db.inventory.find({$or:[{"amount":{$gt:50}},{"amount":{$lt:50}}]},{"_id":0})
{ "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }
{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }
$nor
用法:{ $nor: [ { }, { }] },$nor执行逻辑NOR运算,指定一个至少包含两个表达式的数组,选择出都不满足该数组中所有表达式的文档。
示例:
db.inventory.find({$nor:[{"amount":{$gt:50}},{"amount":{$lt:50}}]},{"_id":0})
{ "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }
$not
用法:{ field: { $not: { } } },$not执行逻辑NOT运算,选择出不能匹配表达式的文档,包括没有指定键的文档。$not操作符不能独立使用,必须跟其他操作一起使用(除$regex)。
示例:
db.inventory.find({"amount":{$not:{$gt:50}}},{"_id":0})
{ "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }
{ "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }
元素操作符
$exists
用法:{ field: { $exists: } },断言字段是否存在,如果$exists的值为true,选择存在该字段的文档;若值为false则选择不包含该字段的文档。
示例:
db.inventory.find({qty:{$exists:false}},{"_id":0})
{ "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }
{ "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }
{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }
db.inventory.find({amount:{$exists:true,$nin:[16,58]}},{"_id":0})
{ "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }
$mod
用法:{ field: { $mod: [ divisor, remainder ]} },匹配字段值对(divisor)取模,值等于(remainder)的文档。
示例:
db.inventory.find({amount:{$mod:[5,0]}},{"_id":0})
{ "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }
$type
用法:{ field: { $type: } },选择字段值为指定的BSON数据类型的文档.
type>使用下面类型对应的编号:
类型 类型 编号
Double 双精度 1
String 字符串 2
Object 对象 3
Array 数组 4
Binary data 二进制对象 5
Object id 对象id 7
Boolean 布尔值 8
Date 日期 9
Null 未定义 10
Regular Expression 正则表达式 11
JavaScript JavaScript代码 13
Symbol 符号 14
JavaScript (with scope) JavaScript代码(带范围) 15
32-bit integer 32位整数 16
Timestamp 时间戳 17
64-bit integer 64位整数 18
Min key 最小键 255
Max key 最大键 127
如果文档的键值是一个数组。那么$type将对数组里面的元素进行类型匹配而不是键值数组本身。
示例:
db.inventory.find({"amount":{$type:1}},{"_id":0})
{ "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }
{ "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }
{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }
MinKey和Maxkey表示可能的最小值和最大值,查询示例:
db.post.insert({x:MinKey})
db.post.find({"x":{$type:-1}})
{ "_id" :ObjectId("55a14d2eb26f97b960389a61"), "x" : {"$minKey" : 1 } }
JavaScript查询操作符
$regex
regex操作符查询中可以对字符串的执行正则匹配。MongoDB使用Perl兼容的正则表达式(PCRE)库来匹配正则表达式,可以使用正则表达式对象或者regex操作符。
options(regex提供四个选项标志)
i如果设置了这个修饰符,忽略大小写。
m如果设置了这个修饰符,忽略换行。
s如果设置了这个修饰符,模式中的点号元字符匹配所有字符,包含换行符。如果没有这个修饰符,点号不匹配换行符。
x如果设置了这个修饰符,模式中的没有经过转义的或不在字符类中的空白数据字符总会被忽略,并且位于一个未转义的字符类外部的#字符和下一个换行符之间的字符也被忽略。这个修饰符使被编译模式中可以包含注释。注意:这仅用于数据字符。空白字符还是不能在模式的特殊字符序列中出现,比如序列。
注:JavaScript只提供了i和m选项,x和s选项必须使用$regex操作符。
示例:查询name键值以“3”结尾的文档
db.inventory.find({name:/3/i},{"_id":0});
{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }
db.inventory.find( { name: { $regex: '.3',$options: 'i' } },{"_id":0} )
{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }
$where
$where操作符功能强大而且灵活,他可以使用任意的JavaScript作为查询的一部分,包含JavaScript表达式的字符串或者JavaScript函数。不是非常必要时,一定要避免使用"where"査询,因为它们在速度上要比常规査询慢很多,每个文档都要从BSON转换成JavaScript对象,然后通过"where"的表达式来运行,同时还不能利用索引。所以,只在走投无路时才考虑"$where"这种用法。
示例:比较文档中的两个键的值是否相等。
db.fruit.insert({"apple":1,"banana": 4, "peach" : 4})
db.fruit.insert({"apple":3,"banana": 3, "peach" : 4})
查找出banana等于peach键值的文档(4种方法):
db.fruit.find( { $where: "this.banana== this.peach" } )
{ "_id" :ObjectId("55a14f35b26f97b960389a62"), "apple" : 1,"banana" : 4, "peach" : 4 }
db.fruit.find( { $where: "obj.banana== obj.peach" } )
{ "_id" :ObjectId("55a14f35b26f97b960389a62"), "apple" : 1,"banana" : 4, "peach" : 4 }
db.fruit.find( { $where: function() {return (this.banana == this.peach) } } )
{ "_id" :ObjectId("55a14f35b26f97b960389a62"), "apple" : 1,"banana" : 4, "peach" : 4 }
db.fruit.find( { $where: function() {return obj.banana == obj.peach; } } )
{ "_id" :ObjectId("55a14f35b26f97b960389a62"), "apple" : 1,"banana" : 4, "peach" : 4 }
查出文档中存在的两个键的值相同的文档
db.fruit.find({$where:function () {
for (var current in this) {
for (var other in this) {
if (current != other &&this[current] == this[other]) {
return true;
}
}
}
return false;
}});
{ "_id" :ObjectId("55a14f35b26f97b960389a62"), "apple" : 1,"banana" : 4, "peach" : 4 }
{ "_id" :ObjectId("55a14f35b26f97b960389a63"), "apple" : 3,"banana" : 3, "peach" : 4 }
三 语法总结
1、find操作的永远是文档,所以find()中一定要有{}包裹限定条件
2、限定条件文档编写时,如果是key,后面都是跟文档,如果是操作符,后面跟数组或字符串
3、$and$or$nor$not逻辑或、与、非操作符都是后面跟数组,并在数组中将多个限定文档作为数组的元素。
4、$gt $lt $gte $lte$eq $ne算术操作符后面都是跟数字
5、$all查询的是条件数组问文档数组的子集,$in查询的是键的值在条件数组中