6

I want something like this:

  1. find one record in mongo db old_record
  2. update this record to new_record
  3. return old_record

I write code like this:

ret = nil
// First, Find the obj
obj := &orm.QuerySetObj{}
err2 := this.querySetCollection.With(session).Find(objKey).One(obj)
if nil != err2 {
    this.logger.Println("Error find obj")
    return
}

ret = obj

// Then, update this obj
obj.updateTime = time.Now().Unix()
err3 := this.querySetCollection.With(session).Upsert(objKey, obj)
if nil != err3 {
    this.logger.Println("Error update obj")
    return
}

return

but, I think find and update should be an atomic operation, so my code is not safe.

how can I do this in an atomic operation

roger
  • 9,063
  • 20
  • 72
  • 119

1 Answers1

16

The method here is .Apply() which takes a Change type and returns ChangeInfo.

Direct example in the documentation:

change := mgo.Change{
        Update: bson.M{"$inc": bson.M{"n": 1}},
        ReturnNew: false,
}
info, err = col.Find(M{"_id": id}).Apply(change, &doc)
fmt.Println(doc.N)

Where doc is document that is found, and it's state depending on the value of ReturnNew in the Change arguments, being false where you want the original document.

Basically all arguments are in the same form as with .findAndModify()

Blakes Seven
  • 49,422
  • 14
  • 129
  • 135
  • actually I have found this answer before I ask, but I just find one record, I should replace `One(obj)` with `limit(1)`? – roger Aug 11 '15 at 09:16
  • @roger That is not a `.findAndModify()`. That is called retriving the object then saving it back. `.findAndModify()` or `.Change()` in this case makes the modification directly on the server and either returns the updated object or original object. Just as stated here already. – Blakes Seven Aug 11 '15 at 09:20
  • @roger No the oposite. `.Apply()` with the `Change` set here **is atomic**, your code is not. That is the problem I am solving here. There is no `.One()` call. This "only" modifies one document at a time. This **is** the mgo version of `.findAndModify()`, which is the question you asked, – Blakes Seven Aug 11 '15 at 09:35
  • OK, now I just understand you meaning, you mean your code will **just find one and update exactly the one**, but I get another question, I want to `find and modify n records`, can I do this? – roger Aug 11 '15 at 09:40
  • @roger That is what the `.findAndModify()` method "your question asks for" does. You cannot modify "n" records **and** return them all at once. MongoDB has no method for this as it simply does not make sense. Either `.findAndModify()` one at a time, **or** `.update()` all at once. But with multiple, you **cannot** return the modified or orginal data. That is a seperate query. – Blakes Seven Aug 11 '15 at 09:45
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/86683/discussion-between-roger-and-blakes-seven). – roger Aug 11 '15 at 09:49
  • 1
    How do we do the same with Go's official MongoDB Driver? – Arka Mukherjee Apr 17 '19 at 09:28