代码之家  ›  专栏  ›  技术社区  ›  John M

来自Grails的MongoDB“upstart”

  •  2
  • John M  · 技术社区  · 13 年前

    我试图在Grails/GORM/mongodb-plugin/mongodb中实现一个简单的“插入或更新”(所谓的“upstart”)方法。

    我在Hibernate中使用的方法(使用merge)失败了,出现了一个重复的键错误。我认为merge()可能不是mongodb GORM中支持的操作,并试图通过GMongo获得本机的upstart方法。

    我终于有了一个可以工作的版本(如下所述),但这可能不是最好的方法,因为在保存的对象中添加任何字段都会无声地破坏代码。

     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)
        }
    }
    
    2 回复  |  直到 13 年前
        1
  •  1
  •   cdeszaq Sudhir N    13 年前

    假设您有对象的更新版本,或者需要使用其新值更新的属性的映射,则可以循环这些属性,并为每个属性应用更新。

    类似于以下内容:

    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
    
        2
  •  0
  •   M. Mennan Kara    13 年前

    MongoDB对upstart有原生支持。请参阅 findAndModify Command 其中upstart参数为true。

    推荐文章