如何用前缀字符串替换数组中的所有元素?

时间:2015-12-29 03:30:42

标签: mongodb mongodb-query aggregation-framework

在我的MongoDB集合中,我有帐户列表,每个帐户都有角色,结构格式如下

{
    "_id" : "acc1",
    "email" : "acc1@email.com",
    "password" : "password",
    "roles" : [ 
        "ADMIN", 
        "USER"
    ],
},
{
    "_id" : "acc2",
    "email" : "acc2@email.com",
    "password" : "password",
    "roles" : [ 
        "USER"
    ],
},
{
    "_id" : "acc2",
    "email" : "acc2@email.com",
    "password" : "password",
    "roles" : [ 
        "ADMIN",
        "SYSTEM",
        "USER",
    ],
}

现在,我想添加ROLE_作为前缀的所有角色,那么JSON应该是

{
    "_id" : "acc2",
    "email" : "acc2@email.com",
    "password" : "password",
    "roles" : [ 
        "ROLE_ADMIN",
        "ROLE_SYSTEM",
        "ROLE_USER",
    ]
}

我不知道如何实现MongoDB脚本来转换整个文档,将前缀附加到像这样的元素。

2 个答案:

答案 0 :(得分:4)

您可以使用cursor.forEach()迭代收集并更新每个文档。它非常简单,但速度很慢,不应该用于大型集合。

db.users.find().forEach(function (doc) {
    var newRoles = doc.roles.map(function (value) {
        return "ROLE_" + value;
    });
    db.users.update(
        {_id: doc._id}, 
        {$set: {roles: newRoles}}
    );
});

使用MongoDB 3.2测量执行时间,收集50k文档,结果如下:

  • 这种方法:17.244s
  • user3100115' s approach:2.181s

明显的结论是仅在小型集合中使用这种简单的方法,并坚持使用大型集合的批量方法。

答案 1 :(得分:2)

执行此操作的最佳方法是使用.aggregate()方法,该方法提供对聚合管道的访问。

在你的管道中你只需要一个$project的阶段,你使用$map返回连接字符串的数组。当然$concat运算符连接字符串并返回连接的字符串。

然后,您迭代聚合结果,这是一个游标,并使用"bulk"操作更新您的文档,以实现最高效率。

var bulkOp = db.users.initializeOrderedBulkOp();
var count  = 0;

db.users.aggregate([
    { "$project": { 
        "roles": { 
            "$map": { 
                "input": "$roles", 
                "as": "role", 
                "in": { "$concat": [ "ROLE_", "$$role" ] } 
            } 
        } 
    }}
]).forEach(function(doc) {
    bulkOp.find( { "_id": doc._id } ).updateOne(
        { "$set": { "roles": doc.roles } }
    );
    count++;
    if (count % 300 === 0) {
        // Execute per 300 operations and re-init
        bulkOp.execute();
        bulkOp = db.users.initializeOrderedBulkOp();
    }
})

// Clean up queues

if (count > 0)
    bulkOp.execute();

MongoDB 3.2 deprecates Bulk()及其相关方法,并提供.bulkWrite()方法。

var requests = [];

db.users.aggregate([
    { "$project": { 
        "roles": { 
            "$map": { 
                "input": "$roles", 
                "as": "role", 
                "in": { "$concat": [ "ROLE_", "$$role" ] } 
            } 
        } 
    }}
]).forEach( document => {
    requests.push( 
        { "updateOne": 
            { 
                "filter": { "_id": doc._id },
                "update": { "$set": { "roles": doc.roles } }
            }
        }
    );
    if (requests.length === 1000) {
       // Execute per 1000 operations
        db.users.bulkWrite(requests);
        requests = [];
    }
});

db.users.bulkWrite(requests);

您的文档如下所示:

{
        "_id" : "acc1",
        "email" : "acc1@email.com",
        "password" : "password",
        "roles" : [
                "ROLE_ADMIN",
                "ROLE_USER"
        ]
}
{
        "_id" : "acc2",
        "email" : "acc2@email.com",
        "password" : "password",
        "roles" : [
                "ROLE_USER"
        ]
}
{
        "_id" : "acc3",
        "email" : "acc2@email.com",
        "password" : "password",
        "roles" : [
                "ROLE_ADMIN",
                "ROLE_SYSTEM",
                "ROLE_USER"
        ]
}