MongoDB是文档型数据库, 与传统关系型数据库有一些区别
概念关系
database 数据库
collection 集合相当于表
docment 文档 相当于行
index 索引
唯一键
mongodb不支持多个集合连接,即是 table join不支持
mongodb 支持多种数据类型
:
例如:
null 表示空或者不存在的字段
字符串
日期
布尔
数值
正则表达式
数组
对象id (objectID)
一个mongodb中可以建立多个数据库。
"show dbs" 命令可以显示所有数据库的列表
"db" 命令可以显示当前数据库对象或集合
"use"命令,可以连接到一个指定的数据库
文档是一个键值(key-value)对(即BSON)。MongoDB 的文档不需要设置相同的字段,并且相同的字段不需要相同的数据类型
最大BSON文档大小为16M字节
_id
字段始终是文档中的第一个字段
mongo命令行工具
--help
显示命令行选项--nodb
在mongo不连接数据库的情况下启动shell。--shell
允许命令 js
默认全局命令
1. `help` 显示帮助。
2. `db.help()` 显示数据库方法的帮助。
3. `db.<collection>.help()` 显示有关集合方法的帮助。collection可以是现有的集合或不存在的集合的名称。
4. `show dbs` 服务器上所有数据库的列表。根据用户权限返回不同的值。
5. `use <db>` 切换当前数据库到<db>
6. `show collections` 当前数据库的所有集合的列表。
7. `show users` 当前数据库的用户列表。
8. `show roles` 当前数据库的所有角色列表,包括用户定义和内置角色。
9. `show profile` 花费1毫秒或更长时间的五个最近的操作
10. `show databases` 所有可用数据库的列表。根据用户权限返回不同的值。
11. `load()` 执行JavaScript文件
database 数据库层命令
1. `db.auth()` 用户身份验证。
2. `db.collection.stats() ` 查看文档的一些信息, 例如填充因子
3. `coll = db.<collection>` 将当前数据库中的特定集合设置为变量 coll, 相当于取别名
例如: 对myCollection使用变量执行操作, coll = db.myCollection, 可以使用别名 调用 coll.find()
3. `db.collection.find() ` 查找集合中的所有文档并返回游标。
4. `db.collection.insertOne()` 将新文档插入集合中。
5. `db.collection.insertMany()` 将多个新文档插入集合中。
6. `db.collection.updateOne()` 更新集合中的单个现有文档。
7. `db.collection.updateMany()` 更新集合中的多个现有文档。
8. `db.collection.save()` 插入新文档或更新集合中的现有文档。
9. `db.collection.deleteOne()` 从集合中删除单个文档。
10. `db.collection.deleteMany()` 从集合中删除文档。
11. `db.collection.drop()` 完全删除或部分删除集合。
12. `db.collection.remove()` 完全删除或部分删除集合。
13. `db.collection.createIndex()` 如果索引不存在,则在集合上创建新索引; 否则,操作无效。
14. `db.getSiblingDB()` 使用此相同连接返回对另一个数据库的引用,而不显式切换当前数据库。这允许跨数据库查询。
增删改查
新增:
db.collection.insert()
db.collection.insertOne()
db.collection.insertMany()
在插入文档时,如果collection不存在则会新建
新建操作默认情况下,如果没有_id
则会创建一个_id
下面的方法也可能插入新的文档:
db.collection.update() 与参数upsert: true一起使用
db.collection.updateOne()与参数upsert: true一起使用
db.collection.updateMany()与参数upsert: true一起使用
db.collection.findAndModify()与参数upsert: true一起使用
db.collection.findOneAndUpdate()与参数upsert: true一起使用
db.collection.findOneAndReplace()与参数upsert: true一起使用
db.collection.save()
db.collection.bulkWrite()
例如:
// 插入一个数据
db.inventory.insertOne(
{ item: "canvas", qty: 100, tags: ["cotton"], size: { h: 28, w: 35.5, uom: "cm" } }
)
// 插入多个数据
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" } }
])
db.createCollection("people")
添加文档字段:
db.people.updateMany(
{ },
{ $set: { join_date: new Date() } }
)
删除文档字段:
db.people.updateMany(
{ },
{ $unset: { "join_date": "" } }
)
查
find
findone
例如:
db.inventory.find( {} )
find()和findOne()
find返回一个cusor游标对象, 默认情况下, 打印前20条文档信息
1.`db.collection.find(<query>)` 找到<query>与集合中的条件匹配的文档。如果<query>未指定条件或为空(即{}),则读取操作将选择集合中的所有文档。
2.`db.collection.find(<query>, <projection>)` 查找符合<query>条件的文档,并返回中的特定字段<projection>。默认情况下, _id字段默认返回,除非明确指定不返回
coll = db.users; coll.find( { }, { name: true } );
3.`db.collection.find().sort(<sort order>)` 升序(1)返回按字段排序的结果。使用-1降序排序
4.`db.collection.find(<query>).sort(<sort order>)` 返回<query>与指定条件匹配的文档。并进行排序
5.`db.collection.find( ... ).limit( <n> )` 将结果限制为<n>行
6.`db.collection.find( ... ).skip( <n> )` 跳过<n>行结果, 在查找返回很多时候时, 不建议使用
7.`db.collection.count()` 返回集合中的文档总数。
8.`db.collection.find(<query>).count()` 返回与查询匹配的文档总数。在count()时会忽略limit()和skip()。
9.`db.collection.findOne(<query>)` 查找并返回单个文档。如果未找到,则返回null。 等价于find().limit(1)
等值查找:
格式
{ <field1>: <value1>, ... }
例如:
db.inventory.find( { status: "D" } )
模糊匹配
db.users.find( { user_id: /bc/ } )
SELECT * FROM users WHERE user_id like "%bc%"
字符前端模糊匹配:
db.users.find( { user_id: /^bc/ } )
SELECT * FROM users WHERE user_id like "bc%"
排序:
1 asc -1 desc
db.users.find( { status: "A" } ).sort( { user_id: 1 } )
db.users.find( { status: "A" } ).sort( { user_id: -1 } )
count 统计
db.users.count()
db.users.find().count()
db.users.count( { user_id: { $exists: true } } )
db.users.find( { user_id: { $exists: true } } ).count()
db.users.count( { age: { $gt: 30 } } )
db.users.find( { age: { $gt: 30 } } ).count()
去重
db.users.distinct( "status" )
limit
db.users.find().limit(5).skip(10)
3.特殊条件可以使用查询运算符
{ <field1>: { <operator1>: <value1> }, ... }
例如:
db.inventory.find( { status: { $in: [ "A", "D" ] } } )
AND条件:
db.inventory.find( { status: "A", qty: { $lt: 30 } } )
OR:
db.inventory.find( { $or: [ { status: "A" }, { qty: { $lt: 30 } } ] } )
db.users.find({ $or: [ { status: "A" }, { age: 50 } ] })
AND与OR一起:
db.inventory.find( {
status: "A",
$or: [ { qty: { $lt: 30 } }, { item: /^p/ } ]
} )
$lt 小于
$lte 小于等于
$gt 大于
$gte 大于等于
$in 在该数组内值得文档
$not 否
$mod 取模操作
数组查找:
$all 值都存在: {"authors":{“all”:["aaa", "bbb"]}} 表示作者既有aaa,又有bbb的
$size 长度, 查询特定长度的数组
$slice 返回匹配数组元素的子集{“count”: {"$slice": 10}}总数前10
如果为-10, 则为总数最后10个数,
也可以是一个范围{“count”: {"$slice": [5, 11]}} 总数6-11位置的数, 除去5
$where 键值对的查询方式无法满足查询条件时,可以使用where, 可以在查询中执行任意的JavaScript,进行查找, 一般不进行使用,因为有可能存在安全风险
limit限制返回的大小
skip, 跳过多少文档数, 但是应该避免skip大量结果, 因为拥有一个位置查找过程,因此可能效率比较慢
优化:
使用 find() 和 limit() 实现
考虑根据文档里有的时间戳或者id查询
‘_id’是mongodb ObjectID类型的,ObjectID 使用12 字节的存储空间,每个字节两位十六进制数字,是一个24 位的字符串,包括timestamp, machined, processid, counter 等。
因此可以利用_id做比较分页进行查询
在当前页内查出最后1条记录的_id,记为last_id
把记下来的last_id,作为查询条件,查出大于last_id的记录作为下一页的内容
db.users.find().limit(pageSize);
last_id = x1
//第二页
db.users.find({'_id'> last_id}). limit(pageSize);
last_id = x2
sort排序
查询可以只返回指定字段:
1表示返回, 0 表示不返回,默认返回_id
, 除非明确指定不返回_id
字段
db.inventory.find( { status: "A" }, { item: 1, status: 1 } )
不返回id:
db.inventory.find( { status: "A" }, { item: 1, status: 1, _id: 0 } )
组合查询/复杂查询
db.user.find({“name”: “aaa”}).sort(“create_time”: 1)
辅助函数:
$maxscan
db.user.find()._addSpecial("$maxscan": 20) 扫描文档数量的上限为20条
$min 查询开始条件, 文档必须与索引键完全匹配, 查询会强制使用给定的索引,一般使用 $gt 代替
$max
查询结束条件, 文档必须与索引键完全匹配, 查询会强制使用给定的索引,一般使 用$lg代替
快照
查询过程中, 文档持续修改,更新发送移动,可能在游标末尾,会返回一个新的文档, 解决办法是,可以使用快照snapshot进行查询, 例如: db.user.find().snapshot()
但是快照会影响其他性能,在必要时才进行使用
子文档/嵌套文档查询:
子文档查询字段使用点号"." 例如: profile.address
db.inventory.insertMany( [
{ item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A" },
{ item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "A" },
{ item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" },
{ item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" },
{ item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" }
]);
全匹配子文档
db.inventory.find( { size: { h: 14, w: 21, uom: "cm" } } )
查询嵌套文档指定条件:
db.inventory.find( { "size.uom": "aaaa" }
db.inventory.find( { "size.h": { $lt: 15 } } ))
and 子文档
db.inventory.find( { "size.h": { $lt: 15 }, "size.uom": "in", status: "D" } )
查询数组:
db.inventory.insertMany([
{ item: "journal", qty: 25, tags: ["blank", "red"], dim_cm: [ 14, 21 ] },
{ item: "notebook", qty: 50, tags: ["red", "blank"], dim_cm: [ 14, 21 ] },
{ item: "paper", qty: 100, tags: ["red", "blank", "plain"], dim_cm: [ 14, 21 ] }
]);
等于匹配:
db.inventory.find( { tags: ["red", "blank"] } )
查找既有red又有blank的字段:
db.inventory.find( { tags: { $all: ["red", "blank"] } } )
查找包含一个值:
tags 拥有red的文档
db.inventory.find( { tags: "red" } )
条件匹配:
{ <array field>: { <operator1>: <value1>, ... } }
例如: 查找数组中包含大于25值的文档
db.inventory.find( { dim_cm: { $gt: 25 } } )
小于:
db.users.find({ age: { $lt: 25 } })
范围:
db.inventory.find( { dim_cm: { $gt: 15, $lt: 20 } } )
匹配大小:
db.inventory.find( { "tags": { $size: 3 } } )
匹配指定元素:
查找dim_cm 第二个数大于25
db.inventory.find( { "dim_cm.1": { $gt: 25 } } )
嵌入式文档数组:
查找子文档字段包含条件
db.inventory.find( { "instock": { warehouse: "A", qty: 5 } } )
小于等于
db.inventory.find( { 'instock.qty': { $lte: 20 } } )
范围匹配
db.inventory.find( { "instock.qty": { $gt: 10, $lte: 20 } }
多个条件匹配
db.inventory.find( { "instock.qty": 5, "instock.warehouse": "A" } )
嵌入式文档也可以进行限制返回的字段:
db.inventory.find( { status: "A" }, { item: 1, status: 1, "instock.qty": 1 } )
1.查询null字段:
一般查询等于null的字段, 会返回等于null的字段同时也会匹配到不包含该字段的文档
因此一般不进行null的字段查找,若需要查找,可以通过引用将指定的字段为null的值设置为特殊的字符串进行匹配
db.inventory.find( { item: null } )
2.类型查找:
10 为bson类型编号
db.inventory.find( { item : { $type: 10 } } )
3.存在性校验
$exists
db.inventory.find( { item : { $exists: false } } )
更新:
db.collection.updateOne()
db.collection.updateMany()
db.collection.replaceOne()
语法:
{
<update operator>: { <field1>: <value1>, ... },
<update operator>: { <field2>: <value2>, ... },
...
}
$set 如果该字段不存在这会创建对应的字段
例如:
db.inventory.updateOne(
{ item: "paper" },
{
$set: { "size.uom": "cm", status: "P" },
$currentDate: { lastModified: true }
}
)
更新多个文档:
db.inventory.updateMany(
{ "qty": { $lt: 50 } },
{
$set: { "size.uom": "in", status: "P" },
$currentDate: { lastModified: true }
}
)
字段加: age = age +3
db.users.update(
{ status: "A" } ,
{ $inc: { age: 3 } },
{ multi: true }
)
替换文档:
db.inventory.replaceOne(
{ item: "paper" },
{ item: "paper", instock: [ { warehouse: "A", qty: 60 }, { warehouse: "B", qty: 40 } ] }
)
MongoDB会维护文档的写入顺序,也就是相邻的,但是有时候,更新一个文档变大了, 原来的位置无法存放下文档了, 只能移动到新的位置
MongoDB也存在预留空间,也就是填充因子, 填充因子是MongoDB为每个新闻的预留的增长空间, 可以使用db.collection.stats() 查看, paddingFactor 即是填充因子,
新增时为1,表示不预留任何增长空间, 如果增加文档,则文档发送移动,增长因子变为1.5
预留一半的空间, 多次移动会持续增长,但是不会增长那么快了
移动文档时非常缓慢的操作, 因此应尽量避免移动文档。 移动文档时,需要将原来空间释放掉,再写入另外一个空间。
此外,文档发生移动,原来的空间闲置, 产生空白空间,这就可能带来大量的碎片空间,并且无法利用, 除非进行压缩
更新修改器:
1. $set 设置指定字段
2. $inc 增加或者减少数字
3. $ push 数组操作, 数组存在,则会向已有数组末尾加入一个新的元素,如果没有,则创建
例如: db.mct.update({"user":"aaa"}, {"push": {"context": "qbc"})
$each一次添加多个值, 例如:
db.mct.update({"user":"aaa"}, {"push": {"context": {"$each":[1,2,3]}}
添加1,2,3 一共3个值
可以和$slice一起使用,限制大小, $slice 必须是负整数
db.mct.update({"user":"aaa"}, {"push": {"context": {"$each":[1,2,3], "$slice": -10}}) 限制数组只包含最后加入的10个元素
4. $ne 不等于, 可以保证数组的元素不重复
db.mct.update({"user":{"$ne": "aaa"}}, {"push": {"user": "aaa"}}
5. $addToSet 添加到数组
db.mct.update({"_id": "xxxx"}, {"addToSet": {"user": "aaa"}}
也可以和$each组合一次添加多个值
6. $pop 删除元素, 可以从头或者尾删除
{"$pop": {"key": -1}} 从头部删除, 为1表示尾部删除
7. $pull 依据特定条件进行删除:
db.mct.update({"_id": "xxxx"}, {"pull": {"user": "aaa"}}
删除user中的aaa元素
8. 基于数组位置的修改, 数组的下标都是0开始的
db.mct.update({"_id": "xxxx"}, {"inc": {"user.0.count": 1}}
删除:
delete
db.collection.deleteOne()
db.collection.deleteMany()
db.collection.remove()
删除所有:
db.inventory.deleteMany({})
删除一条数据:
db.inventory.deleteOne( { status: "D" } )
删除操作不会删除索引,即使删除所有文档也不会删除索引
批量写入:
如果在处理其中一个写操作期间发生错误,MongoDB将返回而不处理列表中的任何剩余写操作
创建索引:
1 表示asc, -1表示desc 降序
db.people.createIndex( { user_id: 1 } )
db.users.createIndex( { user_id: 1, age: -1 } )