web-dev-qa-db-ja.com

Mongoのすべてのドキュメントで文字列を置き換える方法

特定のドキュメントの文字列を置き換える必要があります。私はこのコードをグーグルで検索しましたが、残念ながら何も変更しません。以下の行の構文についてはわかりません。

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シェルを使用した経験がありません。これを置き換えるだけです:

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

{ "source": { "url": "http://bbb/xxx/yyy" } }
25
lzap

一般的には修正されません。文字列http://aaa/xxx/aaayyyaaaと等しい)がある場合、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関数もあります)

38
om-nom-nom

MongoDB 2.6以降を使用している場合にこれを行う最良の方法は、 _.forEach_ メソッドを使用してカーソルオブジェクトをループし、各ドキュメントを使用して各ドキュメントを更新する "bulk" です。最大効率のための操作。

_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とそれに関連する methods は非推奨です 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);
_
3
styvane

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],
]);
1
chx

今日、

  • 開始Mongo 4.2db.collection.updateManydb.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"を含むドキュメント)をフィルタリングします
  • 2番目の部分($set: { "source.url": {...)は、更新集約パイプラインです(角括弧が集約パイプラインの使用を示すことに注意してください):
    • $set は、この場合はフィールドの値を置き換える新しい集計演算子(Mongo 4.2)です。
    • 新しい値は、新しい $replaceOne 演算子を使用して計算されます。 source.urlが独自の値($source.url)に基づいて直接変更される方法に注意してください。

これはサーバー側で完全に処理されるため、質問のデバッグ印刷部分を実行できないことに注意してください。

0
Xavier Guihot