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

mongodb将文档从一个集合移动到另一个集合,mongodb,Mongodb,如何在MongoDB中将文档从一个集合移动到另一个集合??例如:我在集合A中有很多文档,我想将所有1个月前的文档移动到集合B(这些1个月前的文档不应该在集合A中) 使用聚合我们可以进行复制。但我正试图做的是移动文档。 可以使用什么方法移动文档?您可以使用范围查询从sourceCollection获取数据,并将光标数据保存在变量中,并在其上循环,然后插入到目标集合: var doc = db.sourceCollection.find({ "Timestamp":{

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

使用聚合我们可以进行复制。但我正试图做的是移动文档。
可以使用什么方法移动文档?

您可以使用范围查询从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);
 })
db.source_collection.drop()
希望能有帮助

插入和删除:

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

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

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

例如:

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

更新2

请不要再对这个答案投赞成票了。无论在哪一方面,写出来的都更好

更新

这是一种更安全的方法,应该用它来代替我的


如果我理解正确,并且您希望移动所有超过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()

这是对@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的所有文档从图片移动到MongoDB 3.0上的工件集合

,可以使用具有以下语法的命令:

db.source_collection.copyTo("target_collection")
`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;
    }
})
}`
然后可以使用命令删除旧集合:

 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);
 })
db.source_collection.drop()

我很喜欢@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)

如果您希望在记录复制到新位置后也删除这些记录。代码可以像这样轻松构建,使其可重启。

$out用于创建包含数据的新集合,所以使用$out

db.oldCollection.aggregate([{$out : "newCollection"}])
然后用滴水

db.oldCollection.drop()

我计划使用pymongo的bulkinsert和bulkdelete方法一次获取1000条记录

对于源和目标

  • 创建mongodb对象以连接到数据库

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

    来源
    
    //将其替换为mongodb对象创建逻辑
    source\u db\u obj=db\u help.create\u db\u obj(source\u db,source\u col)
    source\u bulk=source\u db\u obj.initialize\u ordered\u bulk\u op()
    source\u bulk\u bak=source\u db\u obj.initialize\u ordered\u bulk\u op()
    

    为目标
    
    //将其替换为mogodb对象创建逻辑
    target\u db\u obj=db\u help.create\u db\u obj(target\u db,target\u col)
    target_bulk=target_db_obj.initialize_ordered_bulk_op()
    target\u bulk\u bak=target\u db\u obj.initialize\u ordered\u bulk\u op()
    

  • 获取与筛选条件匹配的源记录

    source\u find\u results=source\u db\u obj.find(过滤器)

  • 循环查看源记录

    创建目标和源批量操作

    将具有当前日期时间的存档的_at字段附加到目标集合

    `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;
        }
    })
    }`
    
    
    //将其替换为获取UTCtime的逻辑。
    doc['archived_at']=db_help.getUTCTime()
    目标\批量插入(文档)
    源\批量删除(文档)

    要在出现任何错误或异常时回滚,请创建目标\u bulk\u bak和源\u bulk\u bak操作

    
    目标为bulk.find({'''u id':doc['''u id']})。删除一个()
    来源\批量\包装插入(文件)
    //删除额外的列
    pop文件(“存档”,无)
    

  • 当记录计数为1000时,执行目标大容量插入和源大容量删除。注意:此方法使用目标\u bulk和源\u bulk对象执行

    执行批量插入批量删除(源批量、目标批量)

  • 当发生异常时,执行目标\u bulk\u bak删除和源\u bulk\u bak插入。这将回滚更改。因为mongodb没有回滚功能,所以我想出了这个方法

    执行批量插入移除(源批量、目标批量)

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

  • 完整代码

        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)))
    

  • 如文所述,@markus-w-mahlberg(和@mark mullin精炼)的批量操作是高效的,但不安全。如果bulkInsert失败,bulkRemove仍将继续。为确保移动时不会丢失任何记录,请改用此选项:

    函数插入批处理(集合、文档){
    var bulkInsert=collection.initializeUnderedBulkop();
    var insertedIds=[];
    变量id;
    文档.forEach(功能(文档){
    id=单据编号;
    //插入而不引发重复项的错误
    bulkInsert.find({u id:id}).upsert().replaceOne(doc);
    插入式推送(id);
    });
    bulkInsert.execute();
    返回插入项;
    }
    函数deleteBatch(集合、文档){
    var bulkRemove=collection.initia
    
    db.getCollection("sourceColl").aggregate([
      { $merge: {
         into: "targetColl",
         on: "_id",
         whenMatched: "fail",
         whenNotMatched: "insert"
      }}
    ]);
    db.getCollection("sourceColl").deleteMany({})