27

我需要替换某些文档中的字符串。我已经用谷歌搜索了这段代码,但不幸的是它并没有改变任何东西。我不确定下面这行的语法:

pulpdb = db.getSisterDB("pulp_database");
var cursor = pulpdb.repos.find();
while (cursor.hasNext()) {
  var x = cursor.next();
  x['source']['url'].replace('aaa', 'bbb'); // is this correct?
  db.foo.update({_id : x._id}, x);
}

我想添加一些调试打印以查看值是什么,但我没有使用 MongoDB Shell 的经验。我只需要替换这个:

{ "source": { "url": "http://aaa/xxx/yyy" } }

{ "source": { "url": "http://bbb/xxx/yyy" } }
4

4 回答 4

38

它通常不正确:如果你有字符串http://aaa/xxx/aaayyy等于aaa),你最终会得到http://bbb/xxx/bbb. 但是,如果您对此表示满意,则代码将起作用。

添加调试信息使用print功能:

var cursor = db.test.find();
while (cursor.hasNext()) {
  var x = cursor.next();
  print("Before: "+x['source']['url']);
  x['source']['url'] = x['source']['url'].replace('aaa', 'bbb');
  print("After: "+x['source']['url']);
  db.test.update({_id : x._id}, x);
}

(顺便说一句,如果你想打印出物体,也有printjson功能)

于 2012-04-06T11:00:08.553 回答
4

如果您使用的是 MongoDB 2.6 或更新版本,最好的方法是使用该方法循环游标对象,并使用“批量”.forEach操作更新每个文档以获得最大效率。

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

db.collection.find().forEach(function(doc) {
    print("Before: "+doc.source.url);
    bulk.find({ '_id': doc._id }).update({
        '$set': { 'source.url': doc.source.url.replace('aaa', 'bbb') }
    })
    count++;
    if(count % 200 === 0) {
        bulk.execute();
        bulk = db.collection.initializeOrderedBulkOp();
    }

// Clean up queues
if (count > 0) 
    bulk.execute();

MongoDB 3.2开始,不推荐使用Bulk() API 及其相关方法,您将需要使用该db.collection.bulkWrite()方法。

您将需要循环游标,动态构建查询并将$push每个操作构建到一个数组。

var operations = [];
db.collection.find().forEach(function(doc) {
    print("Before: "+doc.source.url);
    var operation = {
        updateOne: { 
            filter: { '_id': doc._id }, 
            update: { 
                '$set': { 'source.url': doc.source.url.replace('aaa', 'bbb') }
            }
        }
    };
    operations.push(operation);
})
operations.push({ 
    ordered: true, 
    writeConcern: { w: "majority", wtimeout: 5000 } 
})

db.collection.bulkWrite(operations);
于 2015-11-08T08:01:07.430 回答
2

如今,

  • 开始Mongo 4.2db.collection.updateMany(的别名db.collection.update)可以接受一个聚合管道,最后允许根据自己的值更新一个字段。
  • 开始Mongo 4.4,新的聚合运算符$replaceOne使得替换字符串的一部分变得非常容易。
// { "source" : { "url" : "http://aaa/xxx/yyy" } }
// { "source" : { "url" : "http://eee/xxx/yyy" } }
db.collection.updateMany(
  { "source.url": { $regex: /aaa/ } },
  [{
    $set: { "source.url": {
      $replaceOne: { input: "$source.url", find: "aaa", replacement: "bbb" }
    }}
  }]
)
// { "source" : { "url" : "http://bbb/xxx/yyy" } }
// { "source" : { "url" : "http://eee/xxx/yyy" } }
  • 第一部分 ( { "source.url": { $regex: /aaa/ } }) 是匹配查询,过滤要更新的文档(包含 的文档"aaa"
  • 第二部分 ( $set: { "source.url": {...) 是更新聚合管道(注意方括号表示使用聚合管道):
    • $set是一个新的聚合运算符 ( Mongo 4.2),在这种情况下它替换了字段的值。
    • 使用 new 运算符计算新值$replaceOne。请注意如何source.url直接根据其自身的值 ( $source.url) 进行修改。

请注意,这是完全处理的服务器端,不允许您执行问题的调试打印部分。

于 2020-02-22T11:26:57.187 回答
1

MongoDB 可以通过 mapreduce 进行字符串搜索/替换。是的,你需要有一个非常特殊的数据结构——你不能在顶部键中有任何东西,但你需要将所有内容存储在value. 像这样:

{
    "_id" : ObjectId("549dafb0a0d0ca4ed723e37f"),
    "value" : {
            "title" : "Top 'access denied' errors",
            "parent" : "system.admin_reports",
            "p" : "\u0001\u001a%"
    }
}

一旦你有这个整齐的设置,你可以做:

$map = new \MongoCode("function () {
  this.value['p'] = this.value['p'].replace('$from', '$to');
  emit(this._id, this.value);
}");
$collection = $this->mongoCollection();
// This won't be called.
$reduce = new \MongoCode("function () { }");
$collection_name = $collection->getName();
$collection->db->command([
  'mapreduce' => $collection_name,
  'map' => $map,
  'reduce' => $reduce,
  'out' => ['merge' => $collection_name],
  'query' => $query,
  'sort' => ['_id' => 1],
]);
于 2014-12-26T19:47:37.170 回答