【问题标题】:MongoDB - copy collection in java without looping all itemsMongoDB - 在java中复制集合而不循环所有项目
【发布时间】:2013-04-16 09:01:54
【问题描述】:

有没有办法将所有项目集合复制到新集合而不循环所有项目? 我找到了一种通过 DBCursor 循环的方法:

...
DB db = mongoTemplate.getDb();
DBCursor cursor = db.getCollection("xxx").find();

//loop all items in collection
while (cursor.hasNext()) {
   BasicDBObject b = (BasicDBObject) cursor.next();
   // copy to new collection 
   service.createNewCollection(b);
}
...

您能否建议在不循环所有项目的情况下在 java 中进行复制?
(不在 mongo shell 中,使用 java 实现) Tnx。

【问题讨论】:

  • 您使用的是哪个版本的 Mongo?如果 2.1 可以帮助你 -> stackoverflow.com/questions/8933307/… - 特别是接受答案下方的评论。
  • 这是 mongo shell,我需要 java 实现
  • 你的问题是这样的“你能建议在不循环所有项目的情况下进行复制吗?(在 mongo shell 中,使用 java 实现)Tnx。”。你不能在 mongo shell 中使用 java。
  • 你知道用java怎么做吗?
  • 没有,但是阅读文档让我理解了它的工作原理。你读过吗?

标签: java mongodb collections


【解决方案1】:

如果目标集合在同一个数据库中,我认为使用 kellogg.lee 所述的聚合运算符是最好的方法。

为了复制到在不同 mongod 实例上运行的其他数据库中的集合,可以使用以下方法:

第一种方法:

List<Document> documentList = sourceCollection.find().into(new ArrayList<Document>);
targetCollection.insertMany(documentList);

但是,如果源集合很大,此方法可能会导致 outOfMemory 错误。

第二种方法:

sourceCollection.find().batchSize(1000).forEach((Block<? super Document>) document -> targetCollection.insertOne(document));

这种方法比第一种方法更安全,因为它不保留整个文档的本地列表,并且可以根据内存要求确定块大小。但是,这可能比第一个慢。

【讨论】:

    【解决方案2】:

    在 MongoDB 2.6 中,添加了 $out aggregation operator,它将聚合结果写入集合。这提供了一种简单的方法,可以使用 Java 驱动程序(我使用 Java 驱动程序版本 2.12.0)将集合中的所有项在服务器端复制到同一数据库中的另一个集合:

    // set up pipeline
    List<DBObject> ops = new ArrayList<DBObject>();
    ops.add(new BasicDBObject("$out", "target")); // writes to collection "target"
    
    // run it
    MongoClient client = new MongoClient("host");
    DBCollection source = client.getDB("db").getCollection("source")
    source.aggregate(ops);
    

    单行版本:

    source.aggregate(Arrays.asList((DBObject)new BasicDBObject("$out", "target")));
    

    根据文档,对于大型数据集 (>100MB),您可能需要使用 allowDiskUse 选项 (Aggregation Memory Restrictions),尽管我在 >2GB 集合上运行它时没有遇到这个限制,所以它可能不适用于这个特定的管道,至少在 2.6.0 中。

    【讨论】:

    • 我们可以使用这种方法将一个集合复制到其他数据库中的其他集合吗?
    【解决方案3】:

    我遵循了插入对象数组的建议:Better way to move MongoDB Collection to another Collection 这将我的时间从 45 分钟减少到 2 分钟。这是 Java 代码。

            final int OBJECT_BUFFER_SIZE = 2000;
            int rowNumber = 0;
            List<DBObject> objects;
            final int totalRows = cursor.size();
            logger.debug("Mongo query result size: " + totalRows);
                // Loop design based on this:
                // https://stackoverflow.com/questions/18525348/better-way-to-move-mongodb-collection-to-another-collection/20889762#20889762
                // Use multiple threads to improve
                do {
                    logger.debug(String.format("Mongo buffer starts row %d - %d copy into %s", rowNumber,
                            (rowNumber + OBJECT_BUFFER_SIZE) - 1, dB2.getStringValue()));
                    cursor = db.getCollection(collectionName.getStringValue()).find(qo)
                            .sort(new BasicDBObject("$natural", 1)).skip(rowNumber).limit(OBJECT_BUFFER_SIZE);
                    objects = cursor.toArray();
                    try {
                        if (objects.size() > 0) {
                            db2.getCollection(collectionName.getStringValue()).insert(objects);
                        }
                    } catch (final BSONException e) {
                        logger.warn(String.format(
                                "Mongodb copy %s %s: mongodb error. A row between %d - %d will be skipped.",
                                dB1.getStringValue(), collectionName.getStringValue(), rowNumber, rowNumber
                                        + OBJECT_BUFFER_SIZE));
                        logger.error(e);
                    }
                    rowNumber = rowNumber + objects.size();
                } while (rowNumber < totalRows);
    

    缓冲区大小似乎很重要。 10,000 的大小可以正常工作;但是,出于各种其他原因,我选择了较小的尺寸。

    【讨论】:

      【解决方案4】:

      我的想法是从 Java 驱动程序发送 cloneCollection admin 命令。以下是部分示例。

      DB db = mongo.getDB("admin");
      DBObject cmd = new BasicDBObject();
      cmd.put("cloneCollection", "users.profiles");//the collection to clone
      
      //add the code here to build the rest of the required fields as JSON string 
      
      CommandResult result = db.command(cmd);
      

      我记得利用驱动程序的JSON.parse(...) util API 让驱动程序在幕后构建结构。试试这个,因为这更简单。

      注意:我没有尝试过,但我相信这会奏效。

      【讨论】:

      • 这只有在你从远程服务器克隆时才有效,对吧?
      • 是的...但我想在本地做
      • @prilia 您可以从 mongoshell 本地的命令行运行命令。你只需要使用适当的语法。
      【解决方案5】:

      您可以使用 google guava 来执行此操作。要从迭代器中获取 Set,您可以使用 Sets#NewHashSet(Iterator)

      【讨论】:

      • 我认为您应该能够自己解决这个问题。 DBCursor 有一个方法可以返回它的迭代器。
      猜你喜欢
      • 2020-02-14
      • 1970-01-01
      • 1970-01-01
      • 2015-02-28
      • 1970-01-01
      • 2021-08-21
      • 2023-03-29
      • 2018-08-08
      • 2019-08-14
      相关资源
      最近更新 更多