mongodb将文档从一个集合移动到另一个集合

时间:2014-11-20 12:00:11

标签: mongodb

文件如何 MongoDB 中的一个集合移动到另一个集合?例如:我在集合A中有很多文档,我想将所有1个月的旧文档移动到集合B(这些1个月的旧文档不应该在集合A中)。

使用聚合,我们可以执行复制。但我要做的是移动文件。 可以使用什么方法来移动文档?

15 个答案:

答案 0 :(得分:56)

更新2

请不要再提供此答案。正如书面@jasongarber's answer在任何方面都更好。

<强>更新

This answer by @jasongarber是一种更安全的方法,应该用来代替我的。


如果我说得对,你想要移动超过1个月的所有文件,而你使用mongoDB 2.6,就没有理由不使用批量操作,这是进行多项操作的最有效方法我知道:

> var bulkInsert = db.target.initializeUnorderedBulkOp()
> var bulkRemove = db.source.initializeUnorderedBulkOp()
> var date = new Date()
> date.setMonth(date.getMonth() -1)
> db.source.find({"yourDateField":{$lt: date}}).forEach(
    function(doc){
      bulkInsert.insert(doc);
      bulkRemove.find({_id:doc._id}).removeOne();
    }
  )
> bulkInsert.execute()
> bulkRemove.execute()

这应该非常快,它的优点是,如果在批量插入过程中出现问题,原始数据仍然存在。


修改

为了防止使用太多内存,您可以对处理的每个x文档执行批量操作:

> var bulkInsert = db.target.initializeUnorderedBulkOp()
> var bulkRemove = db.source.initializeUnorderedBulkOp()
> var x = 10000
> var counter = 0
> var date = new Date()
> date.setMonth(date.getMonth() -1)
> db.source.find({"yourDateField":{$lt: date}}).forEach(
    function(doc){
      bulkInsert.insert(doc);
      bulkRemove.find({_id:doc._id}).removeOne();
      counter ++
      if( counter % x == 0){
        bulkInsert.execute()
        bulkRemove.execute()
        bulkInsert = db.target.initializeUnorderedBulkOp()
        bulkRemove = db.source.initializeUnorderedBulkOp()
      }
    }
  )
> bulkInsert.execute()
> bulkRemove.execute()

答案 1 :(得分:37)

批量操作@ markus-w-mahlberg表示(和@ mark-mullin精炼)效率高但不安全。如果bulkInsert失败,bulkRemove仍将继续。为确保您在移动时不丢失任何记录,请改为使用:

function insertBatch(collection, documents) {
  var bulkInsert = collection.initializeUnorderedBulkOp();
  var insertedIds = [];
  var id;
  documents.forEach(function(doc) {
    id = doc._id;
    // Insert without raising an error for duplicates
    bulkInsert.find({_id: id}).upsert().replaceOne(doc);
    insertedIds.push(id);
  });
  bulkInsert.execute();
  return insertedIds;
}

function deleteBatch(collection, documents) {
  var bulkRemove = collection.initializeUnorderedBulkOp();
  documents.forEach(function(doc) {
    bulkRemove.find({_id: doc._id}).removeOne();
  });
  bulkRemove.execute();
}

function moveDocuments(sourceCollection, targetCollection, filter, batchSize) {
  print("Moving " + sourceCollection.find(filter).count() + " documents from " + sourceCollection + " to " + targetCollection);
  var count;
  while ((count = sourceCollection.find(filter).count()) > 0) {
    print(count + " documents remaining");
    sourceDocs = sourceCollection.find(filter).limit(batchSize);
    idsOfCopiedDocs = insertBatch(targetCollection, sourceDocs);

    targetDocs = targetCollection.find({_id: {$in: idsOfCopiedDocs}});
    deleteBatch(sourceCollection, targetDocs);
  }
  print("Done!")
}

答案 2 :(得分:10)

插入和删除:

var documentsToMove = db.collectionA.find({});
documentsToMove.forEach(function(doc) {
    db.collectionB.insert(doc);
    db.collectionA.remove(doc);
});

注意:对于拥有大型文档的大型集合或集合,此方法可能会非常慢。

答案 3 :(得分:4)

这是对@Markus W Mahlberg的重述

返回恩惠 - 作为一种功能

function moveDocuments(sourceCollection,targetCollection,filter) {
    var bulkInsert = targetCollection.initializeUnorderedBulkOp();
    var bulkRemove = sourceCollection.initializeUnorderedBulkOp();
    sourceCollection.find(filter)
        .forEach(function(doc) {
        bulkInsert.insert(doc);
        bulkRemove.find({_id:doc._id}).removeOne();
        }
  )
  bulkInsert.execute();
  bulkRemove.execute();
}

使用示例

var x = {dsid:{$exists: true}};
moveDocuments(db.pictures,db.artifacts,x)

将具有顶级元素dsid的所有文档从图片移动到工件集合

答案 4 :(得分:4)

$ out用于创建包含数据的新集合,因此请使用$ out

main.c

然后使用drop

#include <fftw3.h>

答案 5 :(得分:3)

从性能的角度来看,最好使用一个命令删除大量文档(特别是如果你有查询部分的索引),而不是一个一个地删除它们。

例如:

db.source.find({$gte: start, $lt: end}).forEach(function(doc){
   db.target.insert(doc);
});
db.source.remove({$gte: start, $lt: end});

答案 6 :(得分:2)

您可以使用范围查询从sourceCollection获取数据并将游标数据保存在变量中并循环到其上并插入目标集合:

 var doc = db.sourceCollection.find({
        "Timestamp":{
              $gte:ISODate("2014-09-01T00:00:00Z"),
              $lt:ISODate("2014-10-01T00:00:00Z")
        }
 });

 doc.forEach(function(doc){
    db.targetCollection.insert(doc);
 })

希望如此有帮助!!

答案 7 :(得分:2)

从MongoDB 3.0起,您可以使用copyTo命令,语法如下:

db.source_collection.copyTo("target_collection")

然后您可以使用drop命令删除旧集合:

db.source_collection.drop()

答案 8 :(得分:1)

第一个选项(使用mongo dump)

1。从集合中获取转储

mongodump -d db -c source_collection

2。从集合中还原

mongorestore -d db -c target_collection dir = dump / db_name / source_collection.bson

第二个选项

运行汇总

db.getCollection('source_collection')。aggregate([{$ match:{“ emailAddress”:“ apitester@mailinator.com”}},{$ out:“ target_collection”}])

第三选项(最慢)

运行贯穿for循环

db.getCollection('source_collection')。find()。forEach(function(docs){db.getCollection('target_collection')。insert(docs);})print(“ Rolleback完成!”);

答案 9 :(得分:1)

这可以在服务器端使用$merge运算符(从MongoDB 4.2开始)完成。

db.getCollection("sourceColl").aggregate([
  { $merge: {
     into: "targetColl",
     on: "_id",
     whenMatched: "fail",
     whenNotMatched: "insert"
  }}
]);
db.getCollection("sourceColl").deleteMany({})

答案 10 :(得分:0)

我确实喜欢@ markus-w-mahlberg的回复,但有时候,我已经看到需要让人们更简单一些。因此,我有几个功能在下面。你可以像他一样使用批量运算符自然地包装东西,但是这个代码同样适用于新旧Mongo系统。

function parseNS(ns){
    //Expects we are forcing people to not violate the rules and not doing "foodb.foocollection.month.day.year" if they do they need to use an array.
    if (ns instanceof Array){
        database =  ns[0];
        collection = ns[1];
    }
    else{
        tNS =  ns.split(".");
        if (tNS.length > 2){
            print('ERROR: NS had more than 1 period in it, please pass as an [ "dbname","coll.name.with.dots"] !');
            return false;
        }
        database = tNS[0];
        collection = tNS[1];
    }
    return {database: database,collection: collection};
}

function insertFromCollection( sourceNS,  destNS, query, batchSize, pauseMS){
    //Parse and check namespaces
    srcNS = parseNS(sourceNS);
    destNS = parseNS(destNS);
    if ( srcNS == false ||  destNS == false){return false;}

    batchBucket = new Array();
    totalToProcess = db.getDB(srcNS.database).getCollection(srcNS.collection).find(query,{_id:1}).count();
    currentCount = 0;
    print("Processed "+currentCount+"/"+totalToProcess+"...");
    db.getDB(srcNS.database).getCollection(srcNS.collection).find(query).addOption(DBQuery.Option.noTimeout).forEach(function(doc){
        batchBucket.push(doc);
        if ( batchBucket.length > batchSize){
            db.getDB(destNS.database).getCollection(destNS.collection)insert(batchBucket);
            currentCount += batchBucket.length;
            batchBucket = [];
            sleep (pauseMS);
            print("Processed "+currentCount+"/"+totalToProcess+"...");       
        }
    }
    print("Completed");
}

/** Example Usage:
        insertFromCollection("foo.bar","foo2.bar",{"type":"archive"},1000,20);    

你显然可以添加db.getSiblingDB(srcNS.database).getCollection(srcNS.collection).remove(query,true) 如果您还希望在将记录复制到新位置后删除它们。可以轻松地构建代码以使其可重新启动。

答案 11 :(得分:0)

我计划使用pkinongo的bulkinsert和bulkdelete方法一次打破1000条记录。

对于源和目标

  1. 创建mongodb对象以连接数据库。

  2. 实例化批量对象。注意:我也创建了批量对象的备份。这将帮助我在发生错误时回滚插入或删除。 例如:

    来源 // replace this with mongodb object creation logic source_db_obj = db_help.create_db_obj(source_db, source_col) source_bulk = source_db_obj.initialize_ordered_bulk_op() source_bulk_bak = source_db_obj.initialize_ordered_bulk_op()
    对于目标 // replace this with mogodb object creation logic target_db_obj = db_help.create_db_obj(target_db, target_col) target_bulk = target_db_obj.initialize_ordered_bulk_op() target_bulk_bak = target_db_obj.initialize_ordered_bulk_op()

  3. 获取与过滤条件匹配的源记录

    source_find_results = source_db_obj.find(filter)

  4. 循环浏览源记录

    创建目标和源批量操作

    将archived_at字段与当前日期时间追加到目标集合

    //replace this with the logic to obtain the UTCtime. doc['archived_at'] = db_help.getUTCTime() target_bulk.insert(document) source_bulk.remove(document)

    如果出现任何错误或异常,请进行回滚,创建target_bulk_bak和source_bulk_bak操作。

    target_bulk_bak.find({'_id':doc['_id']}).remove_one() source_bulk_bak.insert(doc) //remove the extra column doc.pop('archieved_at', None)

  5. 当记录计数为1000时,执行目标 - 批量插入和源 - 批量删除。注意:此方法需要执行target_bulk和source_bulk对象。

    execute_bulk_insert_remove(source_bulk,target_bulk)

  6. 发生异常时,执行target_bulk_bak删除和source_bulk_bak inesertions。这将回滚更改。由于mongodb没有回滚,我想出了这个黑客

    execute_bulk_insert_remove(source_bulk_bak,target_bulk_bak)

  7. 最后重新初始化源和目标批量和bulk_bak对象。这是必要的,因为您只能使用它们一次。

  8. 完整代码

        def execute_bulk_insert_remove(source_bulk, target_bulk):
            try:
                target_bulk.execute()
                source_bulk.execute()
            except BulkWriteError as bwe:
                raise Exception(
                    "could not archive document, reason:    {}".format(bwe.details))
    
        def archive_bulk_immediate(filter, source_db, source_col, target_db, target_col):
            """
            filter: filter criteria for backup
            source_db: source database name
            source_col: source collection name
            target_db: target database name
            target_col: target collection name
            """
            count = 0
            bulk_count = 1000
    
            source_db_obj = db_help.create_db_obj(source_db, source_col)
            source_bulk = source_db_obj.initialize_ordered_bulk_op()
            source_bulk_bak = source_db_obj.initialize_ordered_bulk_op()
    
            target_db_obj = db_help.create_db_obj(target_db, target_col)
            target_bulk = target_db_obj.initialize_ordered_bulk_op()
            target_bulk_bak = target_db_obj.initialize_ordered_bulk_op()
    
            source_find_results = source_db_obj.find(filter)
    
            start = datetime.now()
    
            for doc in source_find_results:
                doc['archived_at'] = db_help.getUTCTime()
    
                target_bulk.insert(doc)
                source_bulk.find({'_id': doc['_id']}).remove_one()
                target_bulk_bak.find({'_id': doc['_id']}).remove_one()
                doc.pop('archieved_at', None)
                source_bulk_bak.insert(doc)
    
                count += 1
    
                if count % 1000 == 0:
                    logger.info("count: {}".format(count))
                    try:
                        execute_bulk_insert_remove(source_bulk, target_bulk)
                    except BulkWriteError as bwe:
                        execute_bulk_insert_remove(source_bulk_bak, target_bulk_bak)
                        logger.info("Bulk Write Error: {}".format(bwe.details))
                        raise
    
                    source_bulk = source_db_obj.initialize_ordered_bulk_op()
                    source_bulk_bak = source_db_obj.initialize_ordered_bulk_op()
    
                    target_bulk = target_db_obj.initialize_ordered_bulk_op()
                    target_bulk_bak = target_db_obj.initialize_ordered_bulk_op()
    
            end = datetime.now()
    
            logger.info("archived {} documents to {} in ms.".format(
                count, target_col, (end - start)))
    

答案 12 :(得分:0)

我收集了1500份文件,收集了1500万份文件,但有些收藏品是空的。

仅使用copy脚本失败,但使用此脚本优化:

db.getCollectionNames().forEach(function(collname) {
    var c = db.getCollection(collname).count();
    if(c!==0){
      db.getCollection(collname).copyTo('master-collection');
      print('Copied collection ' + collname);
    }
});

一切都适合我。

注意:copyTo已被弃用,因为它会阻止读/写操作:所以如果您知道在此操作期间数据库不可用,我认为没问题。

答案 13 :(得分:0)

这里是@jasongarber答案的更新,它使用了最新的mongo'bulkWrite'操作(Read docs here),并且使整个过程保持异步,因此您可以将其作为取决于其完成情况的更广泛脚本的一部分来运行

async function moveDocuments (sourceCollection, targetCollection, filter) {
  const sourceDocs = await sourceCollection.find(filter)

  console.log(`Moving ${await sourceDocs.count()} documents from ${sourceCollection.collectionName} to ${targetCollection.collectionName}`)

  const idsOfCopiedDocs = await insertDocuments(targetCollection, sourceDocs)

  const targetDocs = await targetCollection.find({_id: {$in: idsOfCopiedDocs}})
  await deleteDocuments(sourceCollection, targetDocs)

  console.log('Done!')
}

async function insertDocuments (collection, documents) {
  const insertedIds = []
  const bulkWrites = []

  await documents.forEach(doc => {
    const {_id} = doc

    insertedIds.push(_id)
    bulkWrites.push({
      replaceOne: {
        filter: {_id},
        replacement: doc,
        upsert: true,
      },
    })
  })

  if (bulkWrites.length) await collection.bulkWrite(bulkWrites, {ordered: false})

  return insertedIds
}

async function deleteDocuments (collection, documents) {
  const bulkWrites = []

  await documents.forEach(({_id}) => {
    bulkWrites.push({
      deleteOne: {
        filter: {_id},
      },
    })
  })

  if (bulkWrites.length) await collection.bulkWrite(bulkWrites, {ordered: false})
}

答案 14 :(得分:0)

对于我来说,每个都不起作用。所以我必须进行一些更改。

var kittySchema = new mongoose.Schema({
name: String
});

var Kitten = mongoose.model('Kitten', kittySchema);

var catSchema = new mongoose.Schema({
name: String
});

var Cat = mongoose.model('Cat', catSchema);

这是两个集合的模型

`function Recursion(){
Kitten.findOne().lean().exec(function(error, results){
    if(!error){
        var objectResponse = results;
        var RequiredId = objectResponse._id;
        delete objectResponse._id;
        var swap = new Cat(objectResponse);
        swap.save(function (err) {
           if (err) {
               return err;
           }
           else {
               console.log("SUCCESSFULL");
               Kitten.deleteOne({ _id: RequiredId }, function(err) {
                if (!err) {
                        console.log('notification!');
                }
                else {
                        return err;
                }
            });
               Recursion();
           }
        });
    }
    if (err) {
        console.log("No object found");
        // return err;
    }
})
}`