功能需求:插入数据时,根据库中原有数据中一个或多个字段判断当前插入的数据是否存在,存在则执行更新,不存在则执行插入。
MongoDB依赖为spring-boot-starter-data-mongodb默认版本
思路:MongoDB有提供更新或插入的API,update中将upsert参数的值设置为true即可实现不存在数据执行插入,判断存在的数据则需要将用来当做判断条件的字段建立唯一索引,多个字段需要建立复合唯一索引。
官方文档说明:
db.collection.updateMany(
<filter>,
,
{
upsert: ,
writeConcern: ,
collation: ,
arrayFilters: [ , ... ],
hint: |string> // Available starting in MongoDB 4.2.1
}
)
源码:
/**
* Update all documents in the collection according to the specified arguments.
*
* @param filter a document describing the query filter, which may not be null.
* @param update a document describing the update, which may not be null. The update to apply must include only update operators.
* @param updateOptions the options to apply to the update operation
* @return the result of the update many operation
* @throws com.mongodb.MongoWriteException if the write failed due some other failure specific to the update command
* @throws com.mongodb.MongoWriteConcernException if the write failed due being unable to fulfil the write concern
* @throws com.mongodb.MongoException if the write failed due some other failure
* @mongodb.driver.manual tutorial/modify-documents/ Updates
* @mongodb.driver.manual reference/operator/update/ Update Operators
*/
UpdateResult updateMany(Bson filter, Bson update, UpdateOptions updateOptions);
@Override
public UpdateResult updateMany(final Bson filter, final Bson update, final UpdateOptions updateOptions) {
return executeUpdate(null, filter, update, updateOptions, true);
}
项目代码:
//筛选条件
public Document filter(Map<String, Object> map){
List<Map<String,Object>> listMap;
Document filter = new Document();
if (map.containsKey("queryType") && map.get("queryType").equals("or")){
if (map.containsKey("data") && map.get("data")instanceof List){
listMap = (List<Map<String, Object>>) map.get("data");
filter = new Document("$or", listMap);
}
if (map.containsKey("filter") && map.get("filter")instanceof List){
listMap = (List<Map<String, Object>>) map.get("filter");
filter = new Document("$or", listMap);
}
}else {
if (map.containsKey("data") && map.get("data")instanceof List) {
listMap = (List<Map<String, Object>>) map.get("data");
filter = new Document("$and", listMap);
}
if (map.containsKey("filter") && map.get("filter")instanceof List){
listMap = (List<Map<String, Object>>) map.get("filter");
filter = new Document("$and", listMap);
}
}
return filter;
}
//更新
@Override
public Map updateManyByCollectionName(Map<String, Object> map) {
//需改内容
List<Map<String, Object>> list;
Map<String,Object> set = new HashMap<>();
if (map.containsKey("set") && map.get("set")instanceof Map){
set = (Map<String, Object>) map.get("set");
}else if (map.containsKey("data") && map.get("data")instanceof List){
list = (List<Map<String, Object>>) map.get("data");
for (int i=0;i<list.size();i++){
set.putAll(list.get(i));
}
}
Document document = new Document("$set", new Document(MongoDBUtil.setParameter(set)));
//记录存在执行更新,不存在执行新增
UpdateResult updateResult = getDBCollection(map.get("collectionName").toString()).updateMany(filter(map), document,new UpdateOptions().upsert(true));
return new HashMap<String, Object>(){{put("code","200");put("message","匹配数"+updateResult.getMatchedCount()+";修改数"+updateResult.getModifiedCount());}};
}
记录一个偷懒导致的坑
把filter(过滤条件)参数和set(待更新)参数合并用,upsert的插入操作能执行,执行更新时会报重复的唯一索引异常( E11000 duplicate key error collection)。
最后看官方文档发现,对upsert参数作说明时提到filter参数必须被唯一索引。
(To avoid multiple upserts, ensure that the filter fields are uniquely indexed.)
看文档要仔细!!!
官方文档:
http://docs.mongodb.org/manual/core/write-operations-atomicity/
http://docs.mongodb.org/manual/core/index-unique/#index-type-unique