func convertToUpdate(namespace string, obj, oplogEntry bson.M) (*operation.Op, error) { op := operation.Op{Namespace: namespace, Type: "update"} id, ok := oplogEntry["o2"].(bson.M)["_id"] if !ok { return nil, fmt.Errorf("Update missing o._id field %#v\n", oplogEntry) } var err error op.ID, err = convertIdToString(id) if err != nil { return nil, err } // Check to make sure the object only has $ fields we understand // Note that other Mongo update commands (afaict) are converted to either direct // set commands or $set and $unset commnands. For example an $addToSet command // becoomes {"$set" : {"key.1" : "value"}} for key := range obj { if strings.Contains(key, "$") && key != "$set" && key != "$unset" { return nil, fmt.Errorf("Invalid key %s in update object %#v\n", key, oplogEntry) } } op.Obj = obj // Technically cmd.applyOp supports "upserts" on updates ("b" -> "upsert"), but AFAICT // they never come from oplogs. See comment for oplogEntryToOp for details. if _, ok = oplogEntry["b"]; ok { return nil, fmt.Errorf("Unknown field 'b' in update %#v\n", oplogEntry) } return &op, nil }
func TestUpdate(t *testing.T) { db := setupDb(t) // Update a doc and check that it's changed obj := bson.M{"_id": bson.NewObjectId(), "key": "value"} assert.NoError(t, db.C("test").Insert(obj)) updatedObj := bson.M{"key": "value2"} op := operation.Op{ ID: obj["_id"].(bson.ObjectId).Hex(), Type: "update", Namespace: "throttle.test", Obj: updatedObj, } assert.NoError(t, applyOp(op, db.Session)) var result bson.M assert.NoError(t, db.C("test").Find(bson.M{}).One(&result)) assert.Equal(t, "value2", result["key"].(string)) // Try with the $set syntax op.Obj = bson.M{"$set": bson.M{"key": "value3"}} assert.NoError(t, applyOp(op, db.Session)) assert.NoError(t, db.C("test").Find(bson.M{}).One(&result)) assert.Equal(t, "value3", result["key"].(string)) // Updating a doc that doesn't exist doesn't fail op.ID = bson.NewObjectId().Hex() assert.NoError(t, applyOp(op, db.Session)) }
func convertToInsert(namespace string, obj bson.M) (*operation.Op, error) { op := operation.Op{Namespace: namespace, Type: "insert"} id, ok := obj["_id"] if !ok { // This is valid for indexes, so let's see if the key field exists, and if so assume it's an index if _, ok := obj["key"]; ok { return nil, nil } return nil, fmt.Errorf("Insert missing or 'o._id' field %#v\n", obj) } var err error op.ID, err = convertIdToString(id) if err != nil { return nil, err } op.Obj = obj return &op, nil }