Search code examples
mongodbgomgo

Simulating an Upsert with mgo.txn


Since there's no Upsert in mgo/txn, I'm doing an Insert followed by an Update when I don't know whether a document already exists. Like this (bear in mind this is a simple example, in reality I would also be changing different documents) --

ops := []txn.Op{{
    C:      "test",
    Id:     t.Id,
    Insert: t,
}, {
    C:      "test",
    Id:     t.Id,
    Update: bson.M{"$set": bson.M{"num": 123}},
}}

This works fine. Unfortunately it requires me to know exactly which fields have been changed. I normally run this inside a Save() function that receives an object and saves a bunch of related documents so I don't usually know what fields have been changed. I tried doing something like this instead --

ops := []txn.Op{{
    C:      "test",
    Id:     t.Id,
    Insert: t,
}, {
    C:      "test",
    Id:     t.Id,
    Update:t,
}}

But that doesn't seem to work as I get a "Modifiers and non-modifiers cannot be mixed" error. The only solution I came up with was to "$set" every individual field --

ops := []txn.Op{{
    C:      "test",
    Id:     t.Id,
    Insert: t,
}, {
    C:      "test",
    Id:     t.Id,
    Update: bson.M{"$set": bson.M{"num": 123}},
}, {
    C:      "test",
    Id:     t.Id,
    Update: bson.M{"$set": bson.M{"other": 234}},
}}

But this seems... clunky. Am I missing something? Is there a way to update the whole document?


Solution

  • Although it looks a bit dubious given you'll be resending all the content over again, you can set every field in a value by offering the value itself to $set:

        {
            C:      "test",
            Id:     t.Id,
            Update: bson.M{"$set": t},
        }
    

    Also note that even if you choose to send the values manually, there's no reason to send them in multiple operations; this would work:

        {
            C:      "test",
            Id:     t.Id,
            Update: bson.M{"$set": bson.M{"foo": 1, "bar": 2}},
        }
    

    Finally, please keep in mind that when you use a document with the transaction machinery, it gets extra fields which are necessary for the machinery itself. If you replace the whole document with some custom content, these fields will go away, and the txn package won't behave properly.