mongodb学习
完整内容请见https://www.runoob.com/mongodb/mongodb-replication.html
高级特性
详情请见原文
运算符
$占位符
$
运算符标识要更新的数组中的元素,而无需显式指定数组中元素的位置
example:
students使用以下文档创建一个集合:
db.students.insert([
{ "_id" : 1, "grades" : [ 85, 80, 80 ] },
{ "_id" : 2, "grades" : [ 88, 90, 92 ] },
{ "_id" : 3, "grades" : [ 85, 100, 90 ] }
])
更新id=1的文档,将第一个匹配到grades里面分数为80的改为82:
db.students.updateOne(
{ _id: 1, grades: 80 },
{ $set: { "grades.$" : 82 } }
)
结果如下:
{ "_id" : 1, "grades" : [ 85, 82, 80 ] }
{ "_id" : 2, "grades" : [ 88, 90, 92 ] }
{ "_id" : 3, "grades" : [ 85, 100, 90 ] }
students集合中的文档,该文档的 grades元素值为嵌入式文档的数组:
{
_id: 4,
grades: [
{ grade: 80, mean: 75, std: 8 },
{ grade: 85, mean: 90, std: 5 },
{ grade: 85, mean: 85, std: 8 }
]
}
db.students.updateOne(
{ _id: 4, "grades.grade": 85 },
{ $set: { "grades.$.std" : 6 } }
)
将第一个匹配到的grade值为85的文档的std字段的值为6
结果如下:
{
"_id" : 4,
"grades" : [
{ "grade" : 80, "mean" : 75, "std" : 8 },
{ "grade" : 85, "mean" : 90, "std" : 6 },
{ "grade" : 85, "mean" : 85, "std" : 8 }
]
}
总结:位置$操作符充当更新文档查询中第一个匹配的占位符
$[]
一般用来操作嵌套list,下面举一个极端例子
集合中有如下一个文档,我想修改注释了a这个位置的数字,将0改为1,命令如下:
db.getCollection('ai_data').updateOne(
{'_id':ObjectId("5eeb19d727e09ee301c83662")},
{$set: {'data.$[x].2': 1}},
{arrayFilters: [{'x.0': NumberLong(1570308300000)}]}
)
{
"_id" : ObjectId("5eeb19d727e09ee301c83662"),
"task_id" : "5eeb19d327e09ee301c83661",
"data" : [
[
NumberLong(1570308300000),
27.17,
0, # a
27.17,
0,
27.17,
0
]
]
}
简单解释下,[]中可以存放一个变量,后续在arrayFilters中筛选变量,可以使用纯数字用来定下标
$elemMatch
用于数组,作为搜索条件限制数组,
作为条件和普通搜索区别为只要找到一个元素满足条件即可,
作为投影限制返回文档数组内只包含满足条件的第一个元素。
搜索文档中data这个数组元素a包含1000的文档A,然后限制A中data数组中元素a为100的第一个数组元素
db.getCollection('test').find(
{'data':{'$elemMatch': {'a': 1000}}},
{'data':{'$elemMatch': {'a': 100}}}
).count()
$slice切片
example
db.posts.find( {}, { comments: { $slice: [ 20, 10 ] } } )
$slice: [ 20, 10 ]里面第一个参数为skip的元素数量 , 第二个参数是往后获取元素数量限制个数limit 。
在update操作里面也能用$slice切片来更新数据,主要是保留某个切片区间的数据。
$addFields聚合
简单来说就是添加字段
db.accounts.insert({"_id": 1, "d": [{"a":1}, {"a":2}]})
db.accounts.aggregate([{
'$addFields': {'a': 1}]);
> out:
{"_id": 1, "a": 1, "d": [{"a": 1}, {"a": 2}]}
$unwind聚合
用于aggregate中,作用只有一个:解构文档,从输入文档中解构数组字段,以输出每个元素的文档。每个输出文档用一个元素值替换该数组。对于每个输入文档,输出n个文档,其中n是数组元素的数量,对于空数组可以为零。
db.inventory.insertOne({ "_id" : 1, "item" : "ABC1", sizes: [ "S", "M", "L"] })
db.inventory.aggregate( [ { $unwind : "$sizes" } ] )
> out:
{ "_id" : 1, "item" : "ABC1", "sizes" : "S" }
{ "_id" : 1, "item" : "ABC1", "sizes" : "M" }
{ "_id" : 1, "item" : "ABC1", "sizes" : "L" }
$redact聚合
通过基于文档本身中存储的信息限制每个文档的内容,来重塑流中的每个文档。包含$project
和的功能 $match
。可用于实施字段级修订。对于每个输入文档,输出一个或零个文档。
$cond:
$$DESCEND: $redact
返回当前文档级别的字段,不包括嵌入式文档。要将嵌入式文档和嵌入式文档包含在数组中,请将$cond
表达式应用于嵌入式文档,以确定对这些嵌入式文档的访问。
$$PRUNE: $redact
排除此当前文档/嵌入文档级别的所有字段,而无需进一步检查任何排除的字段。即使排除的字段包含具有不同访问级别的嵌入式文档。
$$KEEP: $redact
返回或将所有字段保留在当前文档/嵌入式文档级别,而无需进一步检查此级别的字段。即使所包含的字段包含具有不同访问级别的嵌入式文档,也适用。
db.accounts.insert({"_id": 1, "d": [{"a":1}, {"a":2}]})
db.accounts.aggregate([{
'$addFields': {'a': 1},
$redact: {
$cond: {
if: { $eq: [ "$a", 1 ] },
then: "$$DESCEND",
else: "$$PRUNE"}}}]);
> out:
{"_id": 1, "d":[{"a": 1}]}
我们应该注意到,此操作符的作用是洋葱一样作用于文档的每一层(从最外层到最里层),如果外层没有搜索条件的字段,可以使用$addFields添加满足条件的字段
db.eval()js操作
db.eval
(function, arguments)
example:
db.eval( function(name, incAmount) {
var doc = db.getCollection('ai_data').findOne( { _id : ObjectId("5ec61f8db125a4733fa6e9dd") } );
doc.data[0][2] = 1;
db.getCollection('ai_data').save( doc );
return doc;
} );
这样可以实现所有能想到的复杂操作,缺点是从4.2版本起这个功能被移除了,可以选择使用load()方法载入js文件,然后可以直接调用js函数,用这种方式来实现运行js脚本。
多层次操作
单collection多次document操作
db.collection.bulkWrite()
提供可控执行顺序的批量写操作,语法如下
db.collection.bulkWrite(
[ , , ... ],
{
writeConcern : ,
ordered :
}
)
参数 | 类型 | 描述 |
---|---|---|
operations | array | bulkWrite() 写操作的数组。支持操作:insertOne、updateOne、updateMany、deleteOne、deleteMany、replaceOne |
writeConcern | document | 可选, write concern 文档,省略则使用默认的 write concern。 |
ordered | boolean | 可选,表示mongod实例有序还是无序执行操作。默认值true。 |
MongoDB支持的WriteConncern选项如下
- w: 数据写入到number个节点才向用客户端确认
- {w: 0} 对客户端的写入不需要发送任何确认,适用于性能要求高,但不关注正确性的场景
- {w: 1} 默认的writeConcern,数据写入到Primary就向客户端发送确认
- {w: “majority”} 数据写入到副本集大多数成员后向客户端发送确认,适用于对数据安全性要求比较高的场景,该选项会降低写入性能
- j: 写入操作的journal持久化后才向客户端确认
- 默认为”{j: false},如果要求Primary写入持久化了才向客户端确认,则指定该选项为true
- wtimeout: 写入超时时间,仅w的值大于1时有效。
- 当指定{w: }时,数据需要成功写入number个节点才算成功,如果写入过程中有节点故障,可能导致这个条件一直不能满足,从而一直不能向客户端发送确认结果,针对这种情况,客户端可设置wtimeout选项来指定超时时间,当写入过程持续超过该时间仍未结束,则认为写入失败。
example
db.collection.bulkWrite(
[
{ insertOne : <document> },
{ updateOne : <document> },
{ updateMany : <document> },
{ replaceOne : <document> },
{ deleteOne : <document> },
{ deleteMany : <document> }
],
{ ordered : false }
)
缺点:只支持同一集合内操作
事务
MongoDB 4.0 引入的事务功能,支持多文档ACID特性
python 版本
pymongo下测试通过(mongodb4.2)。
import pymongo
from bson import ObjectId
c1 = 'apple'
c2 = 'xigua'
task_client = pymongo.MongoClient(
"mongodb://XXX:XXXXXX@127.0.0.1:27017/?authSource=admin&readPreference=primary"
"&appname=MongoDB%20Compass&ssl=false&retryWrites=false")
with task_client.start_session() as s:
s.start_transaction()
task_db = task_client["furit"]
task_db[c1].insert_one({'_id': ObjectId("5ecb5eaf2feaca47432224da")}, session=s)
task_db[c1].delete_one({'_id': ObjectId("5ecb5eaf2feaca47432224da")}, session=s)
task_db[c2].insert_one({'task_id': ObjectId("5ecb5eaf2feaca47432224da")}, session=s)
task_db[c2].delete_many({'task_id': ObjectId("5ecb5eaf2feaca47432224da")}, session=s)
s.commit_transaction()
Session
是 MongoDB 3.6 版本引入的概念,引入这个特性主要就是为实现多文档事务。
注意:
- 事务必须用在副本集情景下否则会报错
- mongodb版本在4.0以上,4.0支持副本集(replica sets)4.2开始支持分片集群(sharded clusters)
mongodb单机转副本集用来测试对事务的支持,过程如下:
在配置文件mongo.cfg添加以下内容
replication:
oplogSizeMB: 10240
replSetName: apple
sharding:
clusterRole: shardsvr
重启服务
mongodb命令行下输入以下命令:
use admin
cfg={"_id":"apple","members":[{"id":0,"host":"127.0.0.1:27017"}]}
rs.initiate(cfg)