我正在尝试在Grails / GORM / mongodb插件/ MongoDB中实现一个简单的“插入或更新”(所谓的“upsert”)方法。
我与Hibernate一起使用的方法(使用merge)因重复键错误而失败。我认为也许merge()不是mongodb GORM中支持的操作,并尝试通过GMongo获取本机upsert方法。
我终于有了一个可行的版本(如下所示),但它可能不是最好的方法,因为向正在保存的对象添加任何字段都会以静默方式破坏代码。
public void upsertPrefix(p) {
def o = new BasicDBObject()
o.put("_id", p.id)
o.put("someValue", p.someValue)
o.put("otherValue", p.otherValue)
// DBObject o = p as DBObject // No signature of method: mypackage.Prefix.keySet() is applicable for argument types: () values: []
db.prefix.update([_id : p.id], o, true, false)
// I actually would want to pass p instead of o here, but that fails with:
// No signature of method: com.gmongo.internal.DBCollectionPatcher$__clinit__closure2.doCall() is applicable for argument types: (java.util.ArrayList) values: [[[_id:keyvalue], mypackage.Prefix : keyvalue, ...]]
/* All of these other more "Hibernatesque" approaches fail:
def existing = Prefix.get(p.id)
if (existing != null) {
p.merge(flush:true) // E11000 duplicate key error
// existing.merge(p) // Invocation failed: Message: null
// Prefix.merge(p) // Invocation failed: Message: null
} else {
p.save(flush:true)
}
*/
}
我想我可以在混合中引入另一个POJO-DbObject映射框架,但这会使事情复杂化,重复GORM已经在做的事情,并可能引入额外的元数据。
如何以最简单的方式解决这个问题?
编辑#1:我现在尝试了其他的东西:
def existing = Prefix.get(p.id)
if (existing != null) {
// existing.properties = p.properties // E11000 duplicate key error...
existing.someValue = p.someValue
existing.otherValue = p.otherValue
existing.save(flush:true)
} else {
p.save(flush:true)
}
非评论版本再次起作用,但不能很好地维护。我想做的评论版本失败了。
编辑#2:
有效的版本:
public void upsertPrefix(p) {
def o = new BasicDBObject()
p.properties.each {
if (! (it.key in ['dbo'])) {
o[it.key] = p.properties[it.key]
}
}
o['_id'] = p.id
db.prefix.update([_id : p.id], o, true, false)
}
从未插入任何内容的版本:
def upsertPrefix(Prefix updatedPrefix) {
Prefix existingPrefix = Prefix.findOrCreateById(updatedPrefix.id)
updatedPrefix.properties.each { prop ->
if (! prop.key in ['dbo', 'id']) { // You don't want to re-set the id, and dbo is r/o
existingPrefix.properties[prop.key] = prop.value
}
}
existingPrefix.save() // Never seems to insert anything
}
仍然因重复键错误而失败的版本:
def upsertPrefix(p) {
def existing = Prefix.get(p.id)
if (existing != null) {
p.properties.each { prop ->
print prop.key
if (! prop.key in ['dbo', 'id']) {
existingPrefix.properties[prop.key] = prop.value
}
}
existing.save(flush:true) // Still fails with duplicate key error
} else {
p.save(flush:true)
}
}
答案 0 :(得分:1)
假设您拥有对象的更新版本或需要使用新值更新的属性的映射,您可以遍历这些属性并为每个属性应用更新。
这样的事情:
def upsert(Prefix updatedPrefix) {
Prefix existingPrefix = Prefix .findOrCreateById(updatedPrefix.id)
updatedPrefix.properties.each { prop ->
if (prop.key != 'id') { // You don't want to re-set the id
existingPrefix.properties[prop.key] = prop.value
}
}
existingPrefix.save()
}
如何排除更新ID可能不太正确,因此您可能需要稍微玩一下。如果属性的相应新值与现有属性不同,您也可以考虑仅更新属性,但这基本上只是一种优化。
如果您有地图,您可能还会考虑按默认控制器脚手架的方式进行更新:
prefixInstance.properties = params
答案 1 :(得分:0)
MongoDB本身支持upsert。请参阅findAndModify Command with upsert参数true。