【问题标题】: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。
  • 问题的核心是取一个子串。你能帮我找到更好的标题吗?

标签: mongodb mongoose mongodb-query aggregation-framework


【解决方案1】:

执行此操作的最有效方法是在撰写本文时即将发布的 MongoDB 中,使用 $split 运算符将我们的字符串拆分为 shown here,然后使用 @987654322 将数组中的最后一个元素分配给一个变量@ 变量运算符和 $arrayElemAt 运算符。

接下来,我们使用$switch 运算符对该变量执行逻辑条件处理或case 语句。

这里的条件是$gt,如果值包含"test",则返回true,在这种情况下,在in表达式中,我们拆分该字符串并简单地返回$concatenated值新计算的数组中的第一个元素和-。如果条件为 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 对象并使用parseFloatNumber 转换您的值,然后使用$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. 中所示

【讨论】:

    猜你喜欢
    • 2016-03-01
    • 2016-02-19
    • 1970-01-01
    • 2015-08-16
    • 2015-11-11
    • 2012-12-18
    • 1970-01-01
    • 2015-08-07
    • 1970-01-01
    相关资源
    最近更新 更多