MongoDB驱动3.x到5.x runCommand结果迭代迁移问题
问题
我正尝试在Java 17环境下,将使用MongoDB Driver 3.0的应用迁移至mongodb-driver-sync 5.1.2。该应用可接收如下格式的JSON查询:
{"aggregate": "Vuce", "pipeline": [{"$match": {"Form": {"$eq": 109}}}, {"$project": {"Col1": "$DocInst", "Col2": "AAAAAAAAAAAA"}}], "cursor": {}}
通过mongodb.runCommand(query)执行并返回聚合结果(因已有数百条此类查询,优先不想修改查询文档语法,但也接受相关建议)。
但新旧版本返回的Document结构不同,导致无法获取完整结果:
- 旧版本聚合结果的"result"字段是符合条件的
List<Document>; - 新版本返回的通用Document格式如下:
{cursor: Document{{firstBatch: [Document{{_id: 11376, Col2: AAAAAAAAAAAA}}, Document{{_id: 11568, Col2: AAAAAAAAAAAA}}, ...], id: 7067558295212936488, ns: Vuce.Vuce}}, ok: 1.0}
该结果包含部分结果列表及MongoCursor相关信息,文档说明需用"getMore"继续迭代,但返回的是普通Document,无法获取MongoCursor对象来完成迭代。
当前使用的Maven依赖如下:
<dependency> <groupId>org.mongodb</groupId> <artifactId>mongodb-driver-sync</artifactId> <version>5.1.2</version> </dependency> <dependency> <groupId>org.mongodb</groupId> <artifactId>mongodb-driver-core</artifactId> <version>5.1.2</version> </dependency> <dependency> <groupId>org.mongodb</groupId> <artifactId>bson</artifactId> <version>5.1.2</version> </dependency> <dependency> <groupId>joda-time</groupId> <artifactId>joda-time</artifactId> <version>2.8.1</version> </dependency> <dependency> <groupId>net.sf.jasperreports</groupId> <artifactId>jasperreports</artifactId> <version>7.0.0</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.21.1</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.21.1</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>5.2.2</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.2</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml-schemas</artifactId> <version>4.1.2</version> </dependency>
我的问题是:如何在新版本中迭代runCommand的结果?能否从该结果中获取迭代器或MongoCursor?是否有其他执行此类查询并返回迭代器的方式?
解决方案
方案一:保留原有查询语法,手动处理游标迭代
从runCommand返回的Document中,可手动解析游标信息,循环调用getMore命令获取全部结果:
- 解析初始结果里的
cursor对象,提取firstBatch(第一批数据)、id(游标ID)和ns(命名空间,格式为数据库.集合)。 - 当游标ID不为0时,构造
getMore命令,传入游标ID和集合名,继续调用runCommand获取后续批次数据。 - 每次获取新批次后,将数据合并到结果列表,直到游标ID变为0(表示无更多数据)。
示例代码片段:
Document result = mongoDatabase.runCommand(queryDoc); Document cursorDoc = result.get("cursor", Document.class); List<Document> allResults = new ArrayList<>(cursorDoc.getList("firstBatch", Document.class)); long cursorId = cursorDoc.getLong("id"); String ns = cursorDoc.getString("ns"); while (cursorId != 0) { Document getMoreCmd = new Document("getMore", cursorId) .append("collection", ns.split("\\.")[1]); // 从命名空间提取集合名 Document moreResult = mongoDatabase.runCommand(getMoreCmd); Document moreCursor = moreResult.get("cursor", Document.class); allResults.addAll(moreCursor.getList("nextBatch", Document.class)); cursorId = moreCursor.getLong("id"); }
方案二:使用新版驱动的聚合API(推荐)
若可接受少量代码改造,直接用新版驱动的aggregate方法,能自动处理游标迭代,无需手动调用getMore:
- 从原查询JSON中提取
pipeline数组,转换为List<Bson>。 - 调用集合的
aggregate方法,传入管道,获取MongoCursor<Document>进行迭代。
示例代码片段:
// 假设原查询文档为queryDoc List<Bson> pipeline = queryDoc.getList("pipeline", Document.class) .stream() .map(Document::toBsonDocument) .collect(Collectors.toList()); MongoCollection<Document> collection = mongoDatabase.getCollection("Vuce"); try (MongoCursor<Document> cursor = collection.aggregate(pipeline).iterator()) { while (cursor.hasNext()) { Document doc = cursor.next(); // 处理单条数据 } }
这种方式符合新版驱动设计,代码更简洁,也能避免手动处理游标的潜在问题。
内容的提问来源于stack exchange,提问作者Gustavo Moreira
相关产品推荐
相关产品推荐

