MongoDB:如何使用正则表达式重命名字段

时间:2014-08-08 11:24:38

标签: javascript regex mongodb mongodb-query

我的文档中有一个字段,以其时间戳命名,如下所示:

{
    _id: ObjectId("53f2b954b55e91756c81d3a5"),
    domain: "example.com",
    "2014-08-07 01:25:08": {
        A: [
            "123.123.123.123"
        ],
        NS: [
            "ns1.example.com.",
            "ns2.example.com."
        ]
    }
}

这对查询来说非常不切实际,因为每个文档都有不同的时间戳。 因此,我想将所有文档的此字段重命名为固定名称。 但是,我需要能够使用正则表达式匹配字段名称,因为它们都是不同的。

我尝试过这样做,但这是非法查询。

db['my_collection'].update({}, {$rename:{ /2014.*/ :"201408"}}, false, true);

有人有解决此问题的方法吗?

基于NEIL LUNN的答案:

conn = new Mongo();
db = conn.getDB("my_db");

var bulk = db['my_coll'].initializeOrderedBulkOp();
var counter = 0;

db['my_coll'].find().forEach(function(doc) {

    for (var k in doc) {
            if (k.match(/^2014.*/) ) {
                print("replacing " + k)
                var unset = {};
                unset[k] = 1;
                bulk.find({ "_id": doc._id }).updateOne({ "$unset": unset, "$set": { WK1: doc[k]} });
                counter++;
            }

    }

    if ( counter % 1000 == 0 ) {
        bulk.execute();
        bulk = db['my_coll'].initializeOrderedBulkOp();
    }

});

if ( counter % 1000 != 0 )
    bulk.execute();

2 个答案:

答案 0 :(得分:3)

这不是mapReduce操作,除非您想要一个仅包含从mapReduce输出生成的_idvalue字段的新集合,就像:

    "_id": ObjectId("53f2b954b55e91756c81d3a5"), 
    "value": { 
        "domain": "example.com",
        ... 
    } 
}

充其量只是对您的收藏进行“服务器端”修改,但当然不是您想要的结构。

虽然有多种方法可以执行服务器中的所有代码,但请不要尝试这样做,除非您真的在某个地方。无论如何,这些方式通常不能很好地与分片一起使用,这通常是人们“真正在某个地方”的记录大小。

当您想要更改内容并批量执行时,通常必须“循环”收集结果并处理更新,同时可以访问当前文档信息。也就是说,如果您的“更新”是“基于”已包含在文档的字段或结构中的信息。

因此没有“正则表达式替换”操作可用,并且当然没有一个用于重命名字段。因此,让我们使用bulk operations循环,以“最安全”的方式执行此操作,而无需在服务器上运行所有代码。

var bulk = db.collection.initializeOrderedBulkOp();
var counter = 0;

db.collection.find().forEach(function(doc) {

    for ( var k in doc ) {
        if ( doc[k].match(/^2014.*/) ) {
            var update = {};
            update["$unset"][k] = 1;
            update["$set"][ k.replace(/(\d+)-(\d+)-(\d+).+/,"$1$2$3") ] = doc[k];
            bulk.find({ "_id": doc._id }).updateOne(update);
            counter++;
        }
    }

    if ( counter % 1000 == 0 ) {
        bulk.execute();
        bulk = db.collection.initializeOrderedBulkOp();
    }

});

if ( counter % 1000 != 0 )
    bulk.execute();

所以主要的事情是$unset运算符删除现有字段,$set运算符在文档中创建新字段。您需要文档内容来检查并使用“字段名称”和“值”,因此循环,因为没有其他方法。

如果服务器上没有MongoDB 2.6或更高版本,则循环概念仍然没有立即的性能优势。您可以查看.eval()之类的内容以便在服务器上进行处理,但正如文档所示,我们不建议这样做。如果必须,请谨慎使用。

答案 1 :(得分:0)

正如您已经认识到的那样,对于MongoDB查询语言,value-key确实非常糟糕。很糟糕,你想做的事情都不起作用。

但你可以用MapReduce做到这一点。 mapreduce函数不会执行任何操作,但finalize函数会在Javascript中进行转换。

或者您可以使用您的编程语言编写一个小程序,从该集合中读取所有文档,进行更改,然后使用collection.save将其写回。