Spring Data Mongo:如何保存批处理并忽略所有重复的键错误?

mar*_*kin 5 java spring mongodb spring-data spring-data-mongodb

我有以下域对象:

@Document
class Foo {
    @Id
    private final String bar;
    private final String baz;
    // getters, setters, constructor omitted
}
Run Code Online (Sandbox Code Playgroud)

插入如下:

Collection<Foo> foos = ...;
mongoTemplate.insert(foos, Foo.class);
Run Code Online (Sandbox Code Playgroud)

如何在一次调用中保存所有结果,忽略所有重复的键异常?

Joh*_*lph 5

就我而言,不适合允许修改/覆盖现有文档,如 @marknorkin 的答案。相反,我只想插入文档。我想出了这个 using 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
        }
      }
Run Code Online (Sandbox Code Playgroud)

有了这个小帮手

private fun isDuplicateKeyException(ex: BulkOperationException): Boolean {
    val duplicateKeyErrorCode = 11000
    return ex.errors.all { it.code == duplicateKeyErrorCode }
}
Run Code Online (Sandbox Code Playgroud)


mar*_*kin 4

我搜索了 spring data mongo 文档和其他资源,但没有找到预期的答案。

似乎 Mongo 会插入批处理文档,直到满足唯一键约束,这由数据库决定。

例如,如果您需要插入 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
Run Code Online (Sandbox Code Playgroud)

所以DB会被调用两次。此外,由于bar索引字段,删除操作会很快。