将字符串字段值更改为其子字符串的最有效方法
Posted
技术标签:
【中文标题】将字符串字段值更改为其子字符串的最有效方法【英文标题】:Most efficient way to change a string field value to its substring 【发布时间】:2016-12-14 10:11:25 【问题描述】:我有一个包含如下文档的集合:
data: 11,
version: "0.0.32"
有些有test
后缀version
:
data: 55,
version: "0.0.42-test"
version
字段具有不同的值,但始终符合以下模式:0.0.XXX
。我想将所有文档更新为如下所示:
data: 11,
version: 32
和后缀版本(对于测试文档 - version
应该是否定的):
data: 55,
version: -42
包含这些文档的集合由我们的关键系统使用,在更新数据时需要关闭 - 所以我希望更新/更改尽可能快。此集合中大约有 66_000_000
个文档,大小约为 100GB。
哪种类型的 mongodb 操作最有效?
【问题讨论】:
关于标题,32
不是 "0.0.32"
的子字符串。事实上,它根本不是一个字符串。 JFYI。
问题的核心是取一个子串。你能帮我找到更好的标题吗?
【参考方案1】:
执行此操作的最有效方法是在撰写本文时即将发布的 MongoDB 中,使用 $split
运算符将我们的字符串拆分为 shown here,然后使用 @987654322 将数组中的最后一个元素分配给一个变量@ 变量运算符和 $arrayElemAt
运算符。
接下来,我们使用$switch
运算符对该变量执行逻辑条件处理或case 语句。
这里的条件是$gt
,如果值包含"test"
,则返回true,在这种情况下,在in表达式中,我们拆分该字符串并简单地返回$concat
enated值新计算的数组中的第一个元素和-
。如果条件为 false,我们只返回变量。
当然,在我们的 case 语句中,我们使用 $indexOfCP
,如果没有出现 "test"
,则返回 -1
。
let cursor = db.collection.aggregate(
[
"$project":
"data": 1,
"version":
"$let":
"vars":
"v":
"$arrayElemAt": [
"$split": [ "$version", "." ] ,
-1
]
,
"in":
"$switch":
"branches": [
"case":
"$gt": [
"$indexOfCP": [ "$$v", "test" ] ,
-1
]
,
"then":
"$concat": [
"-",
"",
"$arrayElemAt": [
"$split": [ "$$v", "-" ] ,
0
]
]
],
"default": "$$v"
]
)
聚合查询产生如下内容:
"_id" : ObjectId("57a98773cbbd42a2156260d8"), "data" : 11, "version" : "32"
"_id" : ObjectId("57a98773cbbd42a2156260d9"), "data" : 55, "version" : "-42"
如您所见,“版本”字段数据是字符串。如果该字段的数据类型无关紧要,您可以简单地使用 $out
聚合管道阶段运算符将结果写入新集合或替换您的集合。
"out": "collection"
如果您需要将数据转换为浮点数,那么执行此操作的唯一方法是迭代,因为 MongoDB 除了整数到字符串之外没有提供开箱即用的类型转换方法聚合 Cursor 对象并使用parseFloat
或Number
转换您的值,然后使用$set
运算符和bulkWrite()
方法更新您的文档以获得最大效率。
let requests = [];
cursor.forEach(doc =>
requests.push(
"updateOne":
"filter": "_id": doc._id ,
"update":
"$set":
"data": doc.data,
"version": parseFloat(doc.version)
,
"$unset": "person": " "
);
if ( requests.length === 1000 )
// Execute per 1000 ops and re-init
db.collection.bulkWrite(requests);
requests = [];
);
// Clean up queues
if(requests.length > 0)
db.coll.bulkWrite(requests);
虽然聚合查询将在 MongoDB 3.4 或更高版本中完美运行,但我们从 MongoDB 3.2 向后最好的选择是使用 bulkWrite()
方法的 mapReduce
。
var results = db.collection.mapReduce(
function()
var v = this.version.split(".")[2];
emit(this._id, v.indexOf("-") > -1 ? "-"+v.replace(/\D+/g, '') : v)
,
function(key, value) ,
"out": "inline": 1
)["results"];
results
看起来像这样:
[
"_id" : ObjectId("57a98773cbbd42a2156260d8"),
"value" : "32"
,
"_id" : ObjectId("57a98773cbbd42a2156260d9"),
"value" : "-42"
]
从这里您使用之前的.forEach
循环来更新您的文档。
从 MongoDB 2.6 到 3.0,您将需要使用现已弃用的 Bulk()
API 及其关联方法,如我的 answer here. 中所示
【讨论】:
以上是关于将字符串字段值更改为其子字符串的最有效方法的主要内容,如果未能解决你的问题,请参考以下文章
将数千条记录插入表中的最有效方法是啥(MySQL,Python,Django)
在 SQL Server 中检查逗号分隔字符串中是不是存在子字符串的最有效方法