Spring数据Mongo:如何保存批量忽略所有重复键错误?
Spring Data Mongo: How to save batch ignoring all duplicate key errors?
我有以下域对象:
@Document
class Foo {
@Id
private final String bar;
private final String baz;
// getters, setters, constructor omitted
}
其中插入如下:
Collection<Foo> foos = ...;
mongoTemplate.insert(foos, Foo.class);
如何在一次调用中保存所有结果而忽略所有重复键异常?
我搜索了 spring 数据 mongo 文档和其他资源,但没有找到预期的答案。
似乎 Mongo 插入批处理文档直到满足唯一键约束,由 DB 决定。
因此,例如,如果您需要插入 100 个文档,而位置 50 上的文档已经存在于数据库中,那么将插入前 49 个,而不会插入第二个 50 个。
我想到的是下一个解决方案:
Set<String> ids = foos.stream().map(Foo::getBar).collect(toSet()); // collect all ids from docs that will be inserted
WriteResult writeResult = mongoTemplate.remove(new Query(Criteria.where("_id").in(ids)), Foo.class); // perform remove with collected ids
mongoTemplate.insert(foos, Foo.class); // now can safely insert batch
所以DB会被调用两次。
此外,由于 bar
是索引字段,因此删除操作会很快。
在我的例子中,不适合允许 modification/overwriting 现有文档,如@marknorkin 的回答。相反,我只想插入 new 文档。我使用 MongoOperations
想出了这个,它可以在 Spring 中注入。下面的代码是在 Kotlin 中。
try {
// we do not want to overwrite existing documents, especially not behind the event horizon
// we hence use unordered inserts and supresss the duplicate key exceptions
// as described in: https://docs.mongodb.com/v3.2/reference/method/db.collection.insertMany/#unordered-inserts
mongoOps.bulkOps(BulkOperations.BulkMode.UNORDERED, EventContainer::class.java)
.insert(filtered)
.execute()
} catch (ex: BulkOperationException) {
if (!isDuplicateKeyException(ex)) {
throw ex
}
}
有了这个小帮手
private fun isDuplicateKeyException(ex: BulkOperationException): Boolean {
val duplicateKeyErrorCode = 11000
return ex.errors.all { it.code == duplicateKeyErrorCode }
}
我有以下域对象:
@Document
class Foo {
@Id
private final String bar;
private final String baz;
// getters, setters, constructor omitted
}
其中插入如下:
Collection<Foo> foos = ...;
mongoTemplate.insert(foos, Foo.class);
如何在一次调用中保存所有结果而忽略所有重复键异常?
我搜索了 spring 数据 mongo 文档和其他资源,但没有找到预期的答案。
似乎 Mongo 插入批处理文档直到满足唯一键约束,由 DB 决定。
因此,例如,如果您需要插入 100 个文档,而位置 50 上的文档已经存在于数据库中,那么将插入前 49 个,而不会插入第二个 50 个。
我想到的是下一个解决方案:
Set<String> ids = foos.stream().map(Foo::getBar).collect(toSet()); // collect all ids from docs that will be inserted
WriteResult writeResult = mongoTemplate.remove(new Query(Criteria.where("_id").in(ids)), Foo.class); // perform remove with collected ids
mongoTemplate.insert(foos, Foo.class); // now can safely insert batch
所以DB会被调用两次。
此外,由于 bar
是索引字段,因此删除操作会很快。
在我的例子中,不适合允许 modification/overwriting 现有文档,如@marknorkin 的回答。相反,我只想插入 new 文档。我使用 MongoOperations
想出了这个,它可以在 Spring 中注入。下面的代码是在 Kotlin 中。
try {
// we do not want to overwrite existing documents, especially not behind the event horizon
// we hence use unordered inserts and supresss the duplicate key exceptions
// as described in: https://docs.mongodb.com/v3.2/reference/method/db.collection.insertMany/#unordered-inserts
mongoOps.bulkOps(BulkOperations.BulkMode.UNORDERED, EventContainer::class.java)
.insert(filtered)
.execute()
} catch (ex: BulkOperationException) {
if (!isDuplicateKeyException(ex)) {
throw ex
}
}
有了这个小帮手
private fun isDuplicateKeyException(ex: BulkOperationException): Boolean {
val duplicateKeyErrorCode = 11000
return ex.errors.all { it.code == duplicateKeyErrorCode }
}