NSF Back-end Dev Engineer

mongodb进阶

2020-11-26
nsf

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选项如下

  1. w: 数据写入到number个节点才向用客户端确认
    • {w: 0} 对客户端的写入不需要发送任何确认,适用于性能要求高,但不关注正确性的场景
    • {w: 1} 默认的writeConcern,数据写入到Primary就向客户端发送确认
    • {w: “majority”} 数据写入到副本集大多数成员后向客户端发送确认,适用于对数据安全性要求比较高的场景,该选项会降低写入性能
  2. j: 写入操作的journal持久化后才向客户端确认
    • 默认为”{j: false},如果要求Primary写入持久化了才向客户端确认,则指定该选项为true
  3. 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)

上一篇 cassandra

下一篇 基础知识

Comments

Content