最近在开发中使用runCommand查询数据时,发现每次返回的数据量都是101条,而我需要查询的是全部的数据,带着问题,扒了一下runCommand数据查询操作的官方文档,得到了问题的答案。
准备运行环境
MongoClient
这里我是用的Mongo驱动是3.4.3版本的(目前项目中使用的是这个驱动);
ServerAddress serverAddress = new ServerAddress("127.0.0.1", 27017);
MongoClientOptions mongoClientOptions = MongoClientOptions.builder().build();
MongoCredential mongoCredential = MongoCredential.createCredential("username", "db", "password".toCharArray());
MongoClient mongoClient = new MongoClient(Collections.singletonList(serverAddress),
Collections.singletonList(mongoCredential), mongoClientOptions);
代码中使用mongodb地址、库、用户名、密码等信息创建了MongoClient对象
初始化数据
MongoClient对象创建完成后,我们使用单元测试向MongoDB库的test集合中批量插入1024条数据
@Test
public void testInsert() {
MongoDatabase database = mongoClient.getDatabase("db");
BasicDBObject basicDBObject = new BasicDBObject();
basicDBObject.put("insert", "test");
BasicDBList dbList = new BasicDBList();
for(int i = 1; i <= 1024; i ++) {
BasicDBObject document = new BasicDBObject();
document.put("_id", i);
document.put("name", "name_" + i);
dbList.add(document);
}
basicDBObject.put("documents", dbList);
database.runCommand(basicDBObject);
}
问题复现
编写了一个与线上查询逻辑一致的单元测试用例进行问题复现
@Test
public void testFindWithQuestion() {
MongoDatabase database = mongoClient.getDatabase("db");
BasicDBObject basicDBObject = new BasicDBObject();
basicDBObject.put("find", "test");
BasicDBObject projection = new BasicDBObject();
projection.put("_id", 1);
basicDBObject.put("projection", projection);
Document document = database.runCommand(basicDBObject);
double ok = document.getDouble("ok");
if(1 == ok) {
Document cursor = document.get("cursor", Document.class);
if(cursor.containsKey("firstBatch")) {
List<Document> documentList = cursor.get("firstBatch", List.class);
System.out.println(documentList);
System.out.println(documentList.size());
}
}
}
单元测试执行完之后打印了一下数据条数,结果真的是101条,与线上问题一致:
解决问题
问题来源
带着问题去MongoDB官方文档中查询find操作命令相关的文档:
find — MongoDB Manual
在文档中查找到了batchSize参数,是这样描述的:
此参数用于指定firstBatch中返回的文档数量,默认是101;
看到这里终于明白为什么查询时返回的文档数量是101了,那么接下来的问题就是如何查询到全部的文档。
解决办法
带着问题,继续看文档,最终看到了Output返回参数的介绍。
返回的字段中包含了cursor字段,cursor字段中包含了游标编号和fisrtBatch文档;文档中强调了可以使用getMore命令查询剩余的文档。
getMore文档地址getMore — MongoDB Manual
getMore命令可以和返回游标的命令结合使用,可用于查询find命令或者aggregate命令的游标指向文档的后续批次文档。到这里基本上是找到了如何解决问题的答案。
代码实现
@Test
public void testFind() {
MongoDatabase database = mongoClient.getDatabase("db");
BasicDBObject basicDBObject = new BasicDBObject();
basicDBObject.put("find", "test");
BasicDBObject projection = new BasicDBObject();
projection.put("_id", 1);
basicDBObject.put("projection", projection);
boolean hasNext = true;
do {
Document document = database.runCommand(basicDBObject);
MongoDocumentList mongoDocumentList = resolveDocument(document);
System.out.println(mongoDocumentList.documentList);
System.out.println(mongoDocumentList.documentList.size());
hasNext = mongoDocumentList.hasNext();
if(hasNext) {
basicDBObject = new BasicDBObject();
basicDBObject.put("getMore", mongoDocumentList.cursor);
basicDBObject.put("collection", "test");
}
} while (hasNext);
}
private MongoDocumentList resolveDocument(Document document) {
double ok = document.getDouble("ok");
if(1 == ok) {
Document cursor = document.get("cursor", Document.class);
List<Document> documentList = null;
if(cursor.containsKey("firstBatch")) {
documentList = cursor.get("firstBatch", List.class);
} else if (cursor.containsKey("nextBatch")) {
documentList = cursor.get("nextBatch", List.class);
}
return new MongoDocumentList(cursor.getLong("id"), documentList);
}
return null;
}
@AllArgsConstructor
private static class MongoDocumentList {
private Long cursor;
private List<Document> documentList;
public boolean hasNext() {
return 0 != cursor;
}
}
代码中定义了MongoDocumentList实体,用于封装每次查询的结果,代码中判断查询是否结束是根据getMore命令返回的数据中的cursor编号是否为0进行判断的。
执行单元测试的结果如下:
查询的总数据量刚好是1024条