问题描述
我试图在成功的 _id
操作后为插入的文档检索 InsertMany
的值。为此,我使用了 InsertManyResult.getInsertedIds()
。虽然这种方法在大多数情况下都有效,但在某些情况下,并非所有 _id
值都被检索到。
我不确定我是否做错了什么,但我认为 InsertManyResult.getInsertedIds()
为插入的所有文档返回 _id
。
问题详情
我在 MongoDB 中插入 1000 个文档,分两批 500 个文档。每个文档的大小约为 1 MB。
使用 InsertMany
插入批次后,我尝试通过 _id
读取 InsertManyResult.getInsertedIds()
的值并将其保存到集合中以备后用。
我假设在通过 InsertMany
插入 500 文档后,InsertManyResult.getInsertedIds()
将返回 500 _id
值。然而,它只返回 16 _id
个值(500)。
当我直接通过 Mongo Shell 检查 Mongo 集合时,我看到所有记录都已成功插入。我的测试集合中有 1000 个文档。我只是无法通过 _id
获取所有插入文档的 InsertManyResult.getInsertedIds()
。对于插入的 1000 个文档,我只得到 32 _id
。
JSON 结构
为了重现这个问题,我有 一个 JSON,它的大小约为 1 MB,如下所示。
{
"textVal" : "RmKHtemmzJDXgEApmWeoZGrdZJZerIj1","intVal" : 161390623,"longVal" : "98213019054010317","timestampVal" : "2020-12-31 23:59:59.999","numericVal" : -401277306,"largeArrayVal" : [ "MMzJDXg","ApmWeoZGrdZJZerI","1LhTxQ","adprPSb1ZT",...,"QNLkBZuXenmYE77"]
}
请注意,键 largeArrayVal
包含几乎所有数据。为了可读性,我省略了大部分值。
示例代码
下面的代码将上面显示的 JSON 解析为 Document
,然后通过 InsertMany
将其插入到 MongoDB。完成后,我尝试使用 _id
插入 InsertManyResult.getInsertedIds()
。
private static final int MAX_DOCUMENTS = 1000;
private static final int BULK_SIZE = 500;
private static List<ObjectId> insertBatchReturnIds(List<Document> insertBatch)
{
List<ObjectId> insertedIds = new ArrayList<ObjectId>();
InsertManyResult insertManyResult;
insertManyResult = mongoClient.getDatabase(MONGO_DATABASE).getCollection(MONGO_COLLECTION).insertMany(insertBatch);
insertManyResult.getInsertedIds().forEach((k,v) -> insertedIds.add(v.asObjectId().getValue()));
System.out.println("Batch inseted:");
System.out.println(" - Was ackNowladged: " + Boolean.toString(insertManyResult.wasAckNowledged()).toupperCase());
System.out.println(" - InsertManyResult.getInsertedIds().size(): " + insertManyResult.getInsertedIds().size());
return insertedIds;
}
private static void insertDocuments()
{
int documentsInserted = 0;
List<Document> insertBatch = new ArrayList<Document>();
List<ObjectId> insertedIds = new ArrayList<ObjectId>();
final String largeJson = loadLargeJsonFromFile("d:\\test-sample.json");
System.out.println("Starting INSERT test...");
while (documentsInserted < MAX_DOCUMENTS)
{
insertBatch.add(Document.parse(largeJson));
documentsInserted++;
if (documentsInserted % BULK_SIZE == 0)
{
insertedIds.addAll(insertBatchReturnIds(insertBatch));
insertBatch.clear();
}
}
if (insertBatch.size() > 0)
insertedIds.addAll(insertBatchReturnIds(insertBatch));
System.out.println("INSERT test finished");
System.out.println(String.format("Expected IDs retrieved: %d. Actual IDs retrieved: %d.",MAX_DOCUMENTS,insertedIds.size()));
if (insertedIds.size() != MAX_DOCUMENTS)
throw new IllegalStateException("Not all _ID were returned for each document in batch");
}
示例输出
Starting INSERT test...
Batch inseted:
- Was ackNowladged: TRUE
- InsertManyResult.getInsertedIds().size(): 16
Batch inseted:
- Was ackNowladged: TRUE
- InsertManyResult.getInsertedIds().size(): 16
INSERT test finished
Expected IDs retrieved: 1000. Actual IDs retrieved: 32.
Exception in thread "main" java.lang.IllegalStateException: Not all _ID were returned for each document in batch
我的问题
-
InsertManyResult.getInsertedIds()
是否意味着为插入的所有文档返回_id
? - 我使用
InsertManyResult.getInsertedIds()
的方式是否正确? - 插入的 JSON 的大小可能是这里的一个因素吗?
- 我应该如何使用
InsertManyResult
获取插入文档的_id
?
注意
我知道我可以在 _id
之后读取 Document.parse
,因为它是生成它的驱动程序,或者我可以在插入文档后选择 _id
。
我想知道如何使用 InsertManyResult.getInsertedIds()
来实现这一点,因为它似乎是为了满足这一目的。
解决方法
您的文档有 1 mb 大,因此单个命令中最多只能包含 16 个文档。驱动程序确实会自动将整套文档拆分为批次,但您似乎是一次从一批读取 ID,因此问题可能是以下之一:
- 存在一个驱动程序问题,即在将结果返回到您的应用程序之前它没有将批处理结果合并在一起
- 驱动程序一次给您一批结果,因此您确实获得了所有 ID,但不在您期望的段中(在这种情况下,没有错误,但您确实需要处理批次,因为它们由司机提供)
Ruby 中的以下测试按预期工作,生成 100 个 ID:
c = Mongo::Client.new(['localhost:14920'])
docs = [{a: 'x'*1_000_000}]*100
res = c['foo'].insert_many(docs)
p res.inserted_ids.length
pp res.inserted_ids