2

Grails / GORM /mongodbプラグイン/MongoDBに単純な「挿入または更新」(いわゆる「アップサート」)メソッドを実装しようとしています。

私がHibernateで使用したアプローチ(マージを使用)は、重複キーエラーで失敗します。おそらく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)
    }
}
4

2 に答える 2

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
于 2012-08-02T21:04:46.590 に答える
0

MongoDB は upsert をネイティブでサポートしています。upsertパラメータが trueのfindAndModify コマンドを参照してください。

于 2012-08-02T22:35:41.223 に答える