英文:
Mongodb migration causes duplicate key
问题
在一个旧项目中,我们正试图从基本的Spring迁移到Spring Boot 2.3.1。为了做到这一点,并且因为我们有一个Mongo数据库,我们必须从写了这段代码的spring-data-mongodb:1.10.18迁移到spring-data-mongodb:3.0.1.RELEASE,并进行了以下代码更新:
Map<String, List<StockType>> mapMultiUpdate = new HashMap<>();
StockType stockItem = stockMessage.getStockItem();
final Document id = new Document("storeID", stockItem.getStoreID()).append("productID", stockItem.getProductID());
HashMap<String, Object> stock = new HashMap<>();
Date currentDate = Calendar.getInstance().getTime();
Document searchQuery = new Document("_id", id).append("$or", dbList);
stock.put("value", stockItem.getValue());
if (stockItem.getAssociateDate() != null) {
stock.put("associateDate", stockItem.getAssociateDate());
}
if (stockItem.getLastAccessDateSource() != null) {
stock.put("lastAccessDateSource", stockItem.getLastAccessDateSource());
// check
Document ltLast = new Document("$lt", stockItem.getLastAccessDateSource());
List<Document> dbList = Lists.newArrayList();
dbList.add(new Document(stockItem.getStockCategory() + ".lastAccessDateSource", ltLast));
dbList.add(new Document(stockItem.getStockCategory() + ".lastAccessDateSource", null));
} else {
stock.put("lastAccessDateSource", currentDate);
}
// 批量写入选项
BulkWriteOptions bulkWriteOptions = new BulkWriteOptions();
bulkWriteOptions.ordered(false);
bulkWriteOptions.bypassDocumentValidation(true);
MongoCollection<Document> mongoCollection = this.mongoTemplate.getCollection("productStoreStock");
mongoCollection.bulkWrite(updateDocuments, bulkWriteOptions);
但是当在已经存在的对象上执行新代码时,我们会得到一个重复键错误:
com.mongodb.MongoBulkWriteException: 服务器 localhost:27017 上的批量写入操作错误。写入错误:[BulkWriteError{index=0, code=11000, message='E11000 duplicate key error collection: test.productStoreStock index: _id_ dup key: { : { storeID: 400, productID: 100000 } }', details={}}]。
我们还从mongo-java-driver:3.6.4切换到了mongodb-driver-sync:4.0.4。
编辑:
这个错误是在测试阶段的第3步中抛出的,在一个空的数据库/集合中。步骤如下:
- 用一个特定日期的一个产品的一个库存初始化集合
- 检查基础的值
- 在Java中修改库存值但不修改日期,并尝试更新它
- 检查值仍然是第一个值,因为在Mongo查询上有LT过滤器
我们在这个测试中从未达到检查值的阶段,并且在迁移之前,这个测试一切正常。
英文:
In an old project we are trying to migrate from basic spring to spring boot 2.3.1. To do so and because we have a mongo database, we have to migrate from spring-data-mongodb:1.10.18 where this code was written :
DBCollection contextCollection = this.mongoTemplate.getCollection("productStock");
BulkWriteOperation builder = contextCollection.initializeUnorderedBulkOperation();
StockType stockItem = stockMessage.getStockItem();
final BasicDBObject id = new BasicDBObject("storeID", stockItem.getStoreID()).append("productID", stockItem.getProductID());
BulkWriteRequestBuilder bulkWriteRequestBuilder = builder.find(new BasicDBObject("_id", id));
HashMap<String, Object> stock = new HashMap<>();
Date currentDate = Calendar.getInstance().getTime();
stock.put("value", stockItem.getValue());
if (stockItem.getAssociateDate() != null) {
stock.put("associateDate", stockItem.getAssociateDate());
}
if (stockItem.getLastAccessDateSource() != null) {
stock.put("lastAccessDateSource", stockItem.getLastAccessDateSource());
// check
BasicDBObject ltLast = new BasicDBObject("$lt", stockItem.getLastAccessDateSource());
BasicDBList dbList = new BasicDBList();
dbList.add(new BasicDBObject(stockItem.getStockCategory() + ".lastAccessDateSource", ltLast));
dbList.add(new BasicDBObject(stockItem.getStockCategory() + ".lastAccessDateSource", null));
bulkWriteRequestBuilder = builder.find(new BasicDBObject("_id", id).append("$or", dbList));
} else {
stock.put("lastAccessDateSource", currentDate);
}
stock.put("lastUpdateDate", currentDate);
BasicDBObject set = new BasicDBObject(stockItem.getStockCategory(), new Document(stock));
bulkWriteRequestBuilder.upsert().updateOne(new BasicDBObject("$set", set));
builder.execute();
to spring-data-mongodb:3.0.1.RELEASE with this updated code
Map<String, List<StockType>> mapMultiUpdate = new HashMap<>();
StockType stockItem = stockMessage.getStockItem();
final Document id = new Document("storeID", stockItem.getStoreID()).append("productID", stockItem.getProductID());
HashMap<String, Object> stock = new HashMap<>();
Date currentDate = Calendar.getInstance().getTime();
Document searchQuery = new Document("_id", id).append("$or", dbList);
stock.put("value", stockItem.getValue());
if (stockItem.getAssociateDate() != null) {
stock.put("associateDate", stockItem.getAssociateDate());
}
if (stockItem.getLastAccessDateSource() != null) {
stock.put("lastAccessDateSource", stockItem.getLastAccessDateSource());
// check
Document ltLast = new Document("$lt", stockItem.getLastAccessDateSource());
List<Document> dbList = Lists.newArrayList();
dbList.add(new Document(stockItem.getStockCategory() + ".lastAccessDateSource", ltLast));
dbList.add(new Document(stockItem.getStockCategory() + ".lastAccessDateSource", null));
} else {
stock.put("lastAccessDateSource", currentDate);
}
//Bulk write options
BulkWriteOptions bulkWriteOptions = new BulkWriteOptions();
bulkWriteOptions.ordered(false);
bulkWriteOptions.bypassDocumentValidation(true);
MongoCollection<Document> mongoCollection = this.mongoTemplate.getCollection("productStoreStock");
mongoCollection.bulkWrite(updateDocuments, bulkWriteOptions);
But when the new code is executed on an already existing object we get a duplicated key error
com.mongodb.MongoBulkWriteException: Bulk write operation error on server localhost:27017. Write errors: [BulkWriteError{index=0, code=11000, message='E11000 duplicate key error collection: test.productStoreStock index: _id_ dup key: { : { storeID: 400, productID: 100000 } }', details={}}].
We have also switched from mongo-java-driver:3.6.4 to mongodb-driver-sync:4.0.4
EDIT :
This error is thrown during the 3rd step of a test phase, on an empty database / collection. the steps :
- initiating the collection with one stock on a product at a specific date
- checking the value of the base
- modifying the value of the stock in java but not the date and trying to update it
- checking the value is still the first one because of the LT filter on the mongo query
We never reach the checking value and before the migration, everything was good on this test
答案1
得分: 1
test.productStoreStock索引:_id_重复键:{:{storeID:400,productID:100000}}',详细信息={}}
看起来您正在用自己的'构造' id替换内部的_id_字段。这不是一件好事。让MongoDB自己创建它自己的_id,这些在使用中保证是唯一的,永远不会重复。
拥有自己的companyId作为附加字段是没有问题的,但是用自己生成的_id字段替换mongodbs自动生成的_id字段是危险的...正如您已经发现的那样。
英文:
test.productStoreStock index: _id_ dup key: { : { storeID: 400, productID: 100000 } }', details={}}
Looks like you are replacing the internal id field with your own 'constructed' id. This is not a good thing. Let mongo create its own _id, these are guranteed unique across usage.. will never duplicate.
Nothing wrong with having your own companyId as an additional field, but its dangerous to replace mongodbs auto generated _id field... as you have found out.
答案2
得分: 0
Mongo没有在更新时进行更新,而是在找不到匹配项时进行了upsert(插入新行)。
英文:
Mongo is not updating it is upserting ( insert a new row ) when no match is found.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论