1、MongoDB使用面向文档(ducument-oriented)的数据模型,文档的存储使用BSON(Binary JSON)的格式,实际显示会将BSON转化为易读的JSON格式。Mogondb使用的是非模式化(schema-less)存储,通常以集合的形式展示数据。
Binary JSON的缩写,是一种类json的一种二进制形式的存储格式,支持内嵌的文档对象和数组对象。具有轻量性、可遍历性、高效性的特点,缺点是空间利用率不是很理想。
支持的数据类型:String, double, int, long, Date, Timestamp, Array, Binary data, ObjectId, Boolean等。
例:描述一个人物
{
"_id" : ObjectId("5d81fb9c5863617a5fe360f6"),
"name" : "张三",
"age" : 20,
"tags" : ["善良","阳光","长得帅"],
"image" : {
"url" : "http://example.com/db.jpg",
"type" : "jpg",
"data" : "binary"
}
}
2、类似于关系型数据库:支持即席查询(ad hoc queries),用户可以根据自己的需求,灵活的选择查询条件;自动生成主键,支持辅助索引。
3、采用副本集(replica set)模式,易于故障恢复。主节点支持读写,副节点只读;当主节点故障之后,集群会把某一个副节点提升为主节点,等旧主节点恢复后,该节点会变成新主节点的二级节点。主节点和副节点之间的复制通过oplog实现,可以类比Mysql的binlog。
4、类比InnoDB的redo log,MongDB也通过日志(journal)机制实现数据的快速恢复。
5、支持水平扩展与分片。
SQL术语/概念 | MongoDB术语/概念 | 解释/说明 |
---|---|---|
database | database | 数据库 |
table | collection | 数据库表/集合 |
row | document | 数据记录行/文档 |
column | field | 数据字段/域 |
index | index | 索引 |
table joins | ---- | 表连接,MongoDB不支持 |
primary key | primary key | 主键,MongoDB自动将_id字段设置为主键 |
Capped Collection是有上限的集合,可以同时设置记录数或者集合大小的上限。当达到上限后,最老的数据会被删除,可以适用于用来记录日志等需要定期清理的数据。
命名空间,由 数据库名称.集合名称 组成,集合名称可以包含[.],如admin.system.user代表admin表的system.user集合。数据库名称不能包含[.]。
插入操作会返回被插入文档的**_id**字段值,是文档记录的主键,如果没有设置该值Mongodb会自动设置全局唯一的主键,如 ObjectId('5d887ae229d440807a38564a')
工作集表示操作的整个过程中所访问的数据总和,通常是整个数据的一个子集,不过实际的working set大小依赖于当前实际的数据库使用情况。比如运行一个query,它可能需要scan整个collection,那么working set将会包含整个collection。因为物理内存是有限的,会导致working set中一部分documents被“page out”(swap到磁盘cache中),或者被操作系统从内存中移除;此后mongodb再次访问这些文档时,将会发生“page fault”。
更多相关的方法参见:https://docs.mongodb.com/manual/reference/method/
Operator | SQL Analogy | Explain |
---|---|---|
$and | and | and条件连接 |
$or | or | or条件连接,如果比较的key相同,转化为in操作 |
$in | in | 字段在某一取值范围,可以利用索引 |
$nin | not in | 字段值不在某一取值范围,不能利用索引 |
$exists | exists | 判断字段是否存在 |
$not | ! | 取反,但是$not后接正则表达式或者文档 |
$gt | > | 比较运算,大于 |
$gte | >= | 比较运算,大于或者等于 |
$lt | < | 比较运算,小于 |
$lte | <= | 比较运算,小于或者等于 |
$ne | <> | 比较运算,不等于,不能利用索引 |
$regex | regex | 正则表达式,大小写敏感,abc*的形式可以利用索引;可以大小写不敏感去匹配,但是会无法使用索引 |
$mod | mod | 求余结果匹配 |
$type | – | Bson类型匹配 |
$text | – | 在有全文索引的字段上进行全文搜索 |
$all | – | 数组操作符,查询集合的所有元素和文档相应数组的元素匹配,可以利用索引 |
use db
db.collectionName.find({查询条件},{查询字段})
条件 | 等价于 | 说明 |
---|---|---|
字段1 : 值1 | 字段1 = 值1 | 等号连接 |
字段2 : { 操作符 : 值2 } | 字段2 操作符 值2 | {‘a’:{KaTeX parse error: Expected 'EOF', got '}' at position 5: gt:2}̲} ==> a > 2,除了and/$or其他操作符基本是这种形式。 |
a n d / and/ and/or : [上述两种拼接条件的集合] | (条件1 and/or 条件2) | 与或条件 |
条件 | 等价于 |
---|---|
字段 : 1 | 显示字段 |
字段 : 0 | 不显示字段 |
注意_id字段除非手动设置为0,否则都是显示的
// 各查询条件相当于and连接
db.inventory.find(
{
"item" : "journal",
"qty" : {$gt : 10},
$or : [
{ "tags" : "blank" },
{"size.h" : {$gt : 10}}
]
},
{
"item" : 1,
"qty" : 1,
"_id" : 0
}
)
相当于伪SQL:
select item,qty from inventory where item = 'journal' and qty > 10 and (tags = 'blank' or "size.h" = 10)
db.collection.insert(doc)
db.collection.insert([doc1,doc2])
其中doc形式为:{"item":"a","qty":1}
db.collection.update(
{query},//根据查询条件确定记录
{operator:{update}},//更新操作,常见的operator有$rename,$set,$unset,没有操作符则会发生替换
{optional} //可选项
)
db.inventory.update(
{ qty: 25 },
{ $set: { "size.w": 18 } },
{
upsert: true,
multi: true
}
)
db.collection.remove(
{query}, //查询条件确定删除的记录
{optional} //可选项
)
//删除所有
db.inventory.remove()
1、$project:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档,可以使用改造字段的函数,如$concat、$year等使得输出的域为新的值。下例的结果只有_id(默认)和item行:
db.inventory.aggregate({$project:{"item":1}})
2、$match:用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作。
db.inventory.aggregate([{$project : {"item" : 1}}, {$match : {"item" : "mat"}}])
3、$group:将集合中的文档分组,可用于统计结果,分组条件的字段要定义为_id,_id可以使用改造字段的函数,如$concat、$year等使得输出的域为新的值。下例中根据item统计个数,字段用$引用:
db.inventory.aggregate([{$project : {"item" : 1}}, {$group : {_id : "$item", count : {$sum : 1}}}])
相当于sql:select item, count(1) from inventory
统计操作:
$sum:计算总和
$avg:计算平均值
$min:获取集合中所有文档对应值得最小值
$max:获取集合中所有文档对应值得最大值
$first:根据资源文档的排序获取第一个文档数据
$last:根据资源文档的排序获取最后一个文档数据
$push:在结果文档中插入值到一个数组中
$addToSet:结果文档中插入值到一个数组中,所有文档唯一
下例中将先将数组拆开成多条文档,然后再利用$push根据_id分组聚合
db.inventory.aggregate([{$project : { tags : 1}}, { $unwind : '$tags'},{$group : {_id : '$_id', tagItems : {$push : '$tags'}}}])
4、$limit:用来限制MongoDB聚合管道返回的文档数。
5、$skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。
6、$sort:将输入文档排序后输出。
7、$unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
如有一条user记录为:
{
user_id:A_id ,
bonus:[
{ type:a ,amount:1000 },
{ type:b ,amount:2000 },
{ type:b ,amount:3000 }
]
}
//统计
db.user.aggregate([
{$match: {user_id : A_id} },
{$unwind:bonus},
{$match: {'bonus.type' : b} },
{$group: {_id : '$user_id' , amount : {$sum : {'$bonus.amount'}} }}
])
//结果
{_id:A_id , amount : 5000}
8、$geoNear:输出接近某一地理位置的有序文档。
9、$out:将聚合的结果保存在对应的集合中,集合存在会把对应集合的所有数据覆盖,如果集合不存在则创建集合。如:
db.user.aggregate([
{$match: {user_id : A_id} },
{$unwind:bonus},
{$match: {'bonus.type' : b} },
{$group: {_id : '$user_id' , amount : {$sum : {'$bonus.amount'}} }},
{$out: 'sumByBonus'}
])
1、explain: 类似于查询的explain,可以查看管道的执行计划
2、allowDiskUse: 是否允许利用磁盘。由于管道操作运行时Mongodb的ram大小有限制,当管道操作的数据量太大的时候,则会提示"超出内存限制"的错误。允许利用磁盘可以避免这种错误,但是会影响性能。
3、cursor: 对于大量返回结果的聚合,返回游标,可以避免数据大小超限。
管道聚合操作虽然极大的方便了我们进行数据分析处理,但是很容易出现性能问题。下面是几点注意事项:
1、尽可能减少管道中文档的数目和大小,即利用 p r o j e c t 返 回 我 们 需 要 的 字 段 , 利 用 project返回我们需要的字段,利用 project返回我们需要的字段,利用match在管道前面步骤中进行条件过滤。
2、在 m a t c h 和 match和 match和sort操作中利用索引可以极大地提升速度。
3、除了 m a t c h 和 match和 match和sort的其它操作无法利用索引。
4、如果启用了分片,则 m a t c h 和 match和 match和project操作是在各自的分片上进行。一旦还有其他的操作,则在主分片上进行。
创建索引:
db.collectionName.createIndex(fields, options)
field:索引字段,1是升序,2是降序,可多个。
options:可选项,常见的如下
expireAfterSeconds:如果索引字段为时间,可以加上过期时间,即为ttl索引,是特殊的单字段索引,用来自动清除集合中过期的文档,适用于集合文档只在数据库中保存一定时间的场景。如果被索引的字段不为date类型或者date数组类型,则文档不会过期。当字段时间值加上过期时间小于当前时间,则文档会被清理;如果字段是date数组则使用数组最小值判断是否该被清除。
name:索引名称,不设置系统会默认的。
background:是否在后台进行,当数据量大的时候创建索引耗时较大,可以设置为true。
unique:是否为唯一索引
sparse:是否为稀疏索引
db.collectionName.dropIndex(indexName) 删除索引
db.collectionName.getIndexes() 查看索引信息
db.collectionName.reIndex() 重新建立索引,生产环境慎用,因为改操作会获取写锁,导致服务暂时不可用
类似于MySql索引性质:
1、索引使用B-Tree实现。
2、如果有多个索引满足查询计划,最终只会使用其中一个,一般会选择查询计划中扫描文档数更少的索引。
3、索引也满足最左前缀原则,索引的字段顺序会影响查询效率。一般把需要精确匹配的字段放在前面,范围匹配的字段放在后面。
4、索引会占用内存空间,过多的索引会影响性能。
5、可以在查询中使用hint(indexName)来强制使用索引。
索引的最左前缀原则示例:
db.inventory.createIndex({"item":1,"qty":-1},{name:"item_qty_idx",background:true}) //创建复合索引
db.inventory.find({"item":"mat","qty":{$gt:15}}).explain(true) //利用索引
db.inventory.find({"item":"mat"}).explain(true) //利用索引
db.inventory.find({"qty":{$gt:15}}).explain(true) //全集合扫描
db.collectionName.createIndex({field1 : 1, field2 : -1}) 创建索引,可以为单字段或多字段,1表示升序,-1表示降序
db.collectionName.createIndex({field: 1}, {unique: true, dropDups: true}) 创建唯一索引,如果存在重复键的文档则删除后面重复的,dropDups默认为false
db.collectionName.createIndex({field: 1}, {unique: true, sparse: true}) 创建唯一索引,忽略那些不包含索引字段的文档。
注意:如果一个间隙索引会导致查询或者排序操作得到一个不完整结果集的时候,MongoDB将不会使用这个索引,hint提示除外。
在满足qty>25的文档上创建索引,索引字段为item:
db.inventory.createIndex({item:1}, {partialFilterExpression:{qty: {$gt:25}}})
如文档包含 'tags' : ['a','b','c']
则在tags上创建索引,实际上索引表示为:
{'tags' : 'a'}
{'tags' : 'b'}
{'tags' : 'c'}
db.collectionName.createIndex({field: 'hashed'}) 创建哈希索引
注意:
1、哈希索引只支持等号查询,不支持范围查询。
2、不支持多键哈希索引。
3、浮点型被hash之前会被转化为整形进行计算。
db.setProfilingLevel(level) 开启分析
level=2:所有的读写操作均会被记录
level=1:可以指定记录某时间以上的满操作,如db.setProfilingLevel(1, 50)代表50ms以上的慢操作被记录
level=0:禁用分析工具
查询5条记录的操作分析结果:
db.system.profile.find().sort({$natural: -1}).limit(5).pretty()
使用帮助,查看执行计划支持的操作类型。
db.inventory.explain().help()
1、queryPlanner模式,默认模式,MongoDB运行查询优化器对当前的查询进行评估并选择一个最佳的查询计划。
命令:db.inventory.find().explain() 或者 db.inventory.find().explain("queryPlanner")
2、executionStats模式,返回最佳执行计划执行完成时的相关统计信息,对于那些被拒绝的执行计划,不返回其统计信息。
命令:db.inventory.find().explain("executionStats")
3、allPlansExecution模式,前2种模式的更细化,即会包括上述2种模式的所有信息。
命令:db.inventory.find().explain(true) 或者 db.inventory.find().explain("allPlansExecution")
> db.inventory.find({"item":{$regex:"k"}}).explain(true)
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "test.inventory", //查询的集合
"indexFilterSet" : false, //是否有indexFilterSet,这个是针对某种查询条件提前设置需要使用的索引,如果查询条件匹配,则会在预先设置的索引中选择最优的执行
"parsedQuery" : { //查询条件
"item" : {
"$regex" : "k"
}
},
"winningPlan" : { //胜出的执行计划
"stage" : "FETCH", //执行计划的stage,主要有四种:1、COLLSCAN 全表扫描;2、IXSCAN 索引扫描;3、FETCH 根据索引去检索文档;4、SHARD_MERGE 合并分片结果
"inputStage" : { // 子阶段
"stage" : "IXSCAN",
"filter" : {
"item" : {
"$regex" : "k"
}
},
"keyPattern" : { //索引内容
"item" : 1,
"qty" : -1
},
"indexName" : "item_qty_idx", //索引名称
"isMultiKey" : false, //是否为多键索引
"multiKeyPaths" : {
"item" : [ ],
"qty" : [ ]
},
"isUnique" : false, //是否唯一索引
"isSparse" : false, //是否稀疏索引
"isPartial" : false, //是否为部分索引
"indexVersion" : 2,
"direction" : "forward", //方向,如果sort为-1则显示backward
"indexBounds" : { // 扫描的索引范围
"item" : [
"[\"\", {})",
"[/k/, /k/]"
],
"qty" : [
"[MaxKey, MinKey]"
]
}
}
},
"rejectedPlans" : [ ] // 拒绝的执行计划
},
"executionStats" : {
"executionSuccess" : true, //是否执行成功
"nReturned" : 10001, //查询的返回条数
"executionTimeMillis" : 21, //整体执行时间
"totalKeysExamined" : 10008, //索引扫描条数
"totalDocsExamined" : 10001, //文档扫描条数
"executionStages" : {
"stage" : "FETCH",
"nReturned" : 10001,
"executionTimeMillisEstimate" : 10, //根据索引去检索文档的时间
"works" : 10009,
"advanced" : 10001,
"needTime" : 7,
"needYield" : 0,
"saveState" : 78,
"restoreState" : 78,
"isEOF" : 1,
"invalidates" : 0,
"docsExamined" : 10001,
"alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"filter" : {
"item" : {
"$regex" : "k"
}
},
"nReturned" : 10001,
"executionTimeMillisEstimate" : 10, //扫描索引需要的时间
"works" : 10009,
"advanced" : 10001,
"needTime" : 7,
"needYield" : 0,
"saveState" : 78,
"restoreState" : 78,
"isEOF" : 1,
"invalidates" : 0,
"keyPattern" : {
"item" : 1,
"qty" : -1
},
"indexName" : "item_qty_idx",
"isMultiKey" : false,
"multiKeyPaths" : {
"item" : [ ],
"qty" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"item" : [
"[\"\", {})",
"[/k/, /k/]"
],
"qty" : [
"[MaxKey, MinKey]"
]
},
"keysExamined" : 10008,
"seeks" : 1,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0
}
},
"allPlansExecution" : [ ]
},
"serverInfo" : {
"host" : "KerwindeMacBook-Pro.local",
"port" : 27017,
"version" : "3.6.3",
"gitVersion" : "9586e557d54ef70f9ca4b43c26892cd55257e1a5"
},
"ok" : 1
}
除了COLLSCAN、IXSCAN、FETCH、SHARD_MERGE四种之外,还常常出现以下几种:
对于普通查询,效率良好的组合包括:
Fetch+IDHACK、Fetch+ixscan、PROJECTION+ixscan、SHARDING_FILTER+ixscan等。
需要优化的组合包括:
COLLCAN:利用索引避免全表扫描
SORT:利用索引排序,尽量避免内存排序
SUBPLA:避免没有使用索引的or查询
MongoDB支持可插拔的存储引擎,主要有MMAPv1、WiredTiger和In-Memory三种。
MMAPv1是3.2版本以前默认的存储引擎。
3.2版本以后已经取代MMAPv1成为默认的存储引擎。
In-Memory存储引擎将数据存储在内存中,除了少量的元数据和诊断(Diagnostic)日志,In-Memory存储引擎不会维护任何存储在硬盘上的数据(On-Disk Data),避免Disk的IO操作,减少数据查询的延迟。
为了实现高可用,MongoDB支持主从复制(Master-slave)与副本集(Replica Set)两种模式。副本集的功能更加强大,主要有以下四个作用:
1、数据冗余,副节点与主节点保持同步;也可以根据实际使用情况设置副节点同步delay一定的时间,防止误操作使数据丢失。
2、故障恢复,当主节点宕机后,副节点自动升级为主节点继续提供服务。
3、可以在副节点上做一些代价比较大的操作。比如想在一个数据量大的集合中创建索引,可以先在副节点上创建,然后将副节点切换为主节点,在新的副节点上完成索引的创建。
4、读取数据的负载均衡,特别适合于读多写少且不要求强一致性的场景。
最简单的副本集由三个节点构成,主节点、副节点以及仲裁者节点。仲裁者节点是一个轻量的mongodb服务,它不会从主节点同步数据,只是充当一个观察者,用于选举。
副本集的实现主要通过oplog和心跳机制。
oplog
oplog是一个Capped Collection,它存在于主、副节点的local数据库中,名称为oplog.rs,记录了所有数据的变化。对于批量更新,每一条记录都会写oplog。
副节点同步数据采用长轮询的方式。首先查看自己最新的一条oplog的时间戳,然后向主节点请求时间戳大于本身时间戳的数据,最后根据新拉取的oplog更新数据同时写入自己的oplog。
需要注意的是,如果副节点无法确定自身oplog和主节点oplog的同步位置,则副节点则会被永久停止。因为oplog是一个有上限的集合,当副节点离线太久,主节点的oplog达到上限删除旧数据,导致同步点记录被删除。所以要根据故障的恢复时间和数据库吞吐量来设定合理的oplog上限。
更新数据时可以设置write concern参数,根据参数,数据只有同步到指定个数的节点才算提交成功,以此可以避免数据的丢失。同步的节点个数越多,数据越不容易丢失,但也会降低写入的性能。
心跳
心跳主要用于选举和故障恢复中,默认情况下每一个节点每2秒ping其他所有的成员,来确定整个副本集的健康状态。
如果主节点检测不到大多数成员依然活跃,则自己会主动降为副节点,以此确保整个集群主节点只会有一个。
当数据量太大,则需要对系统分片。分片的作用是数据的分布式存储和各个分片的负载均衡。
shard:实际存储数据的数据片,可以是单个mongod实例或者replica set。
mongos:路由,从config server缓存集群的元数据,将请求路由到正确的数据片。
config server:保存集群的元数据,包括数据片的位置、分片信息以及数据片的移动日志等。
分片时,选择文档的某些字段作为shard key(分片键),文档根据分片键的逻辑组合称为chunk。分片是根据分片键范围(range-based)将数据划分为一个个数据块。
分片集合只允许_id和分片键为唯一索引。
MongoDB依靠两种机制保证集群之间的平衡,即chunk分裂和转移。分裂是chunk达到一定容量后(默认64M)分裂成更小的chunk。当集群之间chunk数量差距超过阈值后,则会触发chunk在集群之间的转移。转移比分裂消耗系统性能大。
分片键一旦选定后即不可更改,分片键的选择应该考虑以下几种情形:
1、尽量保证写入操作在各个集群间均衡进行,避免使用严格递增或者递减的字段作为分片键。假如选择自增的n作为主键,分片后有两个chunk),分别为:{"n":$minKey}->{"n":1000}和{"n":1001}->{"n":$maxKey},则在n<=1000时会只写入到第一个chunk,而n>=1000则只会写入到第二个chunk。
2、分片键的值分布不能过于集中,如果某一chunk只有一个分片键值,则无法再进行分裂。
3、分片键应该是经常作为查询条件的字段,这样mongos可以直接路由到相应的分片,否则就会访问所有的分片进行查询。
显示版本:
db.version() //3.6.3
显示所有方法:
db.help()
展示所有数据库:
show dbs
切换数据库:
use dbname //执行该语句后,可以使用变量db代表当前数据库,dbname可以是不存在的数据库
显示正在使用的数据库:
db
在不切换数据库情况下操作其他数据库:
use db1; //此时相当于db = db1
db2 = db.getSibling("db2"); //用变量db2代表数据库db2
show collections(); //展示的是db1下的集合,因为db=db1,没有切换数据库
db2.getCollectionNames(); //展示的是db2下所有的集合名称
(如果将第二句变为db = db.getSibling("db2"),实际上相当于use db2)
删除当前数据库:
db.dropDatabase()
创建集合:
1、db.test.insert(name:"test") 插入数据并自动创建集合
2、db.createCollection("collectionName", options) 指定名称创建集合,options可选。
例:
db.createCollection("test",{"capped":true, size:16384, max:100})
创建上限集合(Capped Collection),当集合容量达到上限时新插入的文档会覆盖最老的记录。上限集合本意是为了记录日志设计的,因此不支持删除和更新等常规集合支持的操作。
创建新数据库myDB:
1、use myDB; db.createCollection("test") 创建集合test同时创建数据库myDB
2、use myDB; db.test.insert(name:"test") 插入一条新数据,同时创建集合test和数据库myDB
删除集合:
db.test.drop()
集合重命名:
db.[collectionName].renameCollection("newCollectionName")
统计:
1、db.stats() 数据库信息
2、db.test.stats() 集合信息
获取集合:
1、db.collectionName.[curdOrder] 操作集合
2、db.getCollection("collectionName").[curdOrder] 当使用上面方式,集合名称在shell中不支持时可以用这种。如db.createCollection("1 test"),shell不支持"1 test"的集合名称。
Command | Explain |
---|---|
insert() | 插入一条或者多条文档 |
insertOne() | 插入一条文档 |
insertMany() | 插入多条文档 |
插入多条记录:
db.inventory.insertMany([
{ item: "journal", qty: 25, tags: ["blank", "red"], size: { h: 14, w: 21, uom: "cm" } },
{ item: "mat", qty: 85, tags: ["gray"], size: { h: 27.9, w: 35.5, uom: "cm" } },
{ item: "mousepad", qty: 25, tags: ["gel", "blue"], size: { h: 19, w: 22.85, uom: "cm" } }
])
结果:
{
"acknowledged" : true,
"insertedIds" : [
ObjectId("5d88762029d440807a385644"),
ObjectId("5d88762029d440807a385645"),
ObjectId("5d88762029d440807a385646")
]
}
查询所有记录:
db.inventory.find( {} ) 或者 db.inventory.find()
-- 等同于SQL:select * from inventory
查询指定字段
db.inventory.find( {}, {"item": 1, "qty": 1} )
-- 等同于SQL:select _id, item, qty from inventory
db.inventory.find( {}, {"item": 1, "qty": 1, "_id": 0} )
-- 等同于SQL:select item, qty from inventory
排除某些字段:
db.inventory.find( {}, {"item": 0, "qty": 0} )
$and:
1、直接在{}中加拼接条件
db.inventory.find({
item: "journal",
qty: 25
})
2、使用$and操作符
db.inventory.find({
$and:[ {item: "journal"}, {qty: 25} ]
})
-- 等同于SQL:select * from inventory where item = 'journal' and qty = 25
$or:
db.inventory.find({
$or: [{ item: "mat" }, { qty: { $lt: 30 } } ]
})
-- 等同于SQL:select * from inventory where item = 'mat' or qty < 25
regex:
db.inventory.find({
item: {$regex : "m"}
})
或者
db.inventory.find({
item: /m/
})
注意:item: /m/i表示大小写不敏感匹配(同item: {$regex : "m","options":"i"}),无法使用索引。
$and & $or
db.inventory.find( {
"size.h": { $gt : 20 },
$or: [ { qty: { $lt: 30 } }, { item: /^m/ } ]
} )
-- size.h是内嵌文档的字段查询,这里根据size.h匹配对应的文档,可以类比OGNL。
-- 等同于SQL:select * from inventory i join inventory_size is on i.id = is.inventory_id where is.h > 20 and (qty < 30 or item LIKE "m%")
$in & $nin
db.inventory.find( {
"qty": { $in : [25, 40] }
} )
-- 等同于SQL:select * from inventory where qty in(25,40)
db.inventory.find( {
"qty": { $nin : [25, 40] }
} )
-- 等同于SQL:select * from inventory where qty not in(25,40)
db.inventory.find( {
"tags": { $all : ['green', 'gold'] }
} )
exists:{ field: { $exists: } }
1.当boolean为true,$exists匹配包含字段的文档,包括字段值为null的文档。
2.当boolean为false,$exists返回不包含对应字段的文档。
db.inventory.find( {
"qty": { $exists : true }
} )
-- 和SQL中的exists查询有点区别,SQL查询中exists后接子查询,而mongodb由于是非模式数据库,这里只是判断文档有该字段。
查询包含数组的记录,数组可能简单元素组成或者是对象元素组成:
db.inventory.find( { tags: ["blank", "red"] } ) 数组顺序匹配
db.inventory.find( { tags: { $all: ["blank","red"] } } ) 只要所有元素匹配就可以
db.inventory.find( { tags: "red" } ) 查询数组中包含某一个元素的文档,注意和上面的区别
限制一条记录中,数组元素的个数:
db.inventory.find({},{"tags" : {$slice : 1}}) 查询所有记录,数组包含前一条
db.inventory.find({},{"tags" : {$slice : -1}}) 查询所有记录,数组包含后一条
排序sort:
db.inventory.find({}).sort({"qty" : 1}) 按照qty字段升序,没有该字段的记录在前面
db.inventory.find({}).sort({"qty" : -1}) 按照qty字段降序,没有该字段的记录在后面
分页skip&limit:
db.inventory.find({}).sort({"qty" : -1}).skip(2).limit(1)
Command | Explain |
---|---|
update() | 默认更新一条文档,在可选项加上multi: true可更新多条 |
updateOne() | 3.2以后,更新一条文档,可选项加上upsert: true,无命中可以新增记录 |
updateMany() | 3.2以后,更新多条文档,可选项加上upsert: true,无命中可以新增记录 |
replaceOne() | 3.2以后,替换一条文档,可选项加上upsert: true,无命中可以新增记录 |
findAndModify() | 查找并替换一条文档,默认返回未修改的记录,可选项加上new: true,返回修改后的记录 |
重点介绍update(),因为下面三种是3.2之后才有的功能,而update可以实现下面所有的功能。
使用格式:
db.collection.update(
,//要更新的查询条件
,//更新操作,常见的operator有$rename,$set,$unset,没有操作符则会发生替换
{
upsert: ,//true代表query无命中则新增
multi: ,//是否更新多条
writeConcern: ,//可选,文档写入策略,{w: 1},0代表无需节点应答,1代表写入了单节点或者集群主节点(默认),大于1代表需要有相应数目节点应答
collation: ,//3.4以后,特定语言的字符比较规则
arrayFilters: [ , ... ],//3.6以后,数组过滤器
hint: // 4.2以后,指定索引支持query,索引不存在就报错
}
)
示例:
默认更新一条:
db.inventory.update(
{ item: "mat" },
{ $set: { tags:["pink"], qty:60, "size.h": 20 } }
)
更新多条:
db.inventory.update(
{ qty: 25 },
{ $set: { "size.w": 18 } },
{
upsert: true,
multi: true
}
)
替换:
db.inventory.update(
{ qty: 30 },
{ "size.w": 23 },//不用$operator会发生替换
{
upsert: true
//不能加multi属性,只会替换一条记录
}
)
查找并替换:
db.inventory.findAndModify({
query:{ "qty" : 25 },
sort:{ "item" : 1 },//只会查找替换一条满足条件的,可以排序命中的结果替换第一条,updateOne不能排序
update:{
$set : { qty : 40 }
},
new : true//决定返回值是旧纪录还是新纪录
})
除了$set之外的其他操作:
$unset: 移除字段,内嵌文档字段用[外字段.内字段]表示,数组用[外字段.0(下标).内字段]表示
db.inventory.update({"item":"paper"},{$unset:{'instock.0.qty':1}})
db.inventory.update({"item":"paper"},{$unset:{'instock.0':1}}) 数组第一个元素会被
置为null
$pop: 移除数组元素
db.inventory.update({"item":"paper"},{$pop:{'instock':-1}}) 删除数组元素,-1从左边删除,1从右边删除,不会置为null
$push: 加入单个数组元素
db.inventory.update({"item":"mat"},{$push:{'tags':'yellow'}})
$push&$each: 加入多个数组元素
db.inventory.update({"item":"mat"}, {$push:{'tags':{$each:['red','orange']}}})
加入多个数组元素并限制数组个数$slice:
db.inventory.update({"item":"mat"}, {$push:{'tags':{$each:['red','orange'],$slice:4}}}) //正数代表从左向右保留的数组个数
$rename: 重命名字段
db.inventory.update({"item":"paper"},{$rename:{'instock':'newInstock'}})
Command | Explain |
---|---|
remove() | 删除一条或者多条文档,可选项justOne: true则删除一条,默认删除多条 |
deleteOne() | 3.2以后,删除一条文档 |
deleteMany() | 3.2以后,删除多条文档 |
删除一条或者多条:
db.collection.remove(
,
{
justOne: ,
writeConcern: ,
collation: //3.4以后
}
)
删除所有:
db.collection.remove()