/** * Shard key invariant: * * A document must be created with a full non-array-or-regex shard key, and the value of that shard * key can never change. * * To enforce this invariant, we have the following mongos rule: * * - Upserts must always contain the full shard key and must only be targeted* to the applicable shard. * * and the following mongod rules: * * - Upserted shard key values must not be arrays (or regexes). * - If a shard key value is present in the update query, upserts must only insert documents which * match this value. * - Updates must not modify shard keys. * * *Updates are targeted by the update query if $op-style, or the update document if replacement-style. * * NOTE: The above is enough to ensure that shard keys do not change. It is not enough to ensure * uniqueness of an upserted document based on the upsert query. This is necessary due to the save() * style operation: * db.coll.update({ _id : xxx }, { _id : xxx, shard : xxx, key : xxx, other : xxx }, { upsert : true }) * * TODO: Minimize the impact of this hole by disallowing anything but save-style upserts of this form. * Save-style upserts of this form are not safe (duplicate _ids can be created) but the user is * explicitly responsible for this for the _id field. * * In addition, there is an rule where non-multi updates can only affect 0 or 1 documents. * * To enforce this, we have the following mongos rule: * * - Non-multi updates must be targeted based on an exact _id query or the full shard key. * * Test setup: * - replacement style updates have the multiUpdate flag set to false. * - $ op updates have multiUpdate flag set to true. */ var st = new ShardingTest({ shards: 2 }); st.adminCommand({ enablesharding: "test" }); st.adminCommand({ shardcollection: "test.col0", key: { a: 1, b: 1 }}); st.adminCommand({ shardcollection: "test.col1", key: { 'x.a': 1 }}); var db = st.s.getDB('test'); var compoundColl = db.getCollection('col0'); var dotColl = db.getCollection('col1'); // // Empty query update // compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({}, { a: 1 }, false); var gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); var doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); doc = compoundColl.findOne(); compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({}, { a: 1, b: 1 }, false); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({}, { a: 100, b: 100 }, false); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); // Cannot modify _id! compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({}, { a: 100, b: 100, _id: 1 }, false); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({}, { $set: { a: 1, b: 1 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({}, { $set: { a: 100, b: 100 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); // Cannot modify _id compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({}, { $set: { a: 1, b: 1, _id: 1 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({}, { $set: { c: 1 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100, c: 1 }), 'doc did not change: ' + tojson(doc)); // // Empty query upsert // compoundColl.remove({}, false); compoundColl.update({}, { a: 1 }, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc == null, 'doc upserted: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.update({}, { a: 1, b: 1 }, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 1, b: 1 }), 'doc not upserted properly: ' + tojson(doc)); // Cannot modify _id! compoundColl.remove({}, false); compoundColl.update({}, { a: 1, b: 1, _id: 1 }, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 1, b: 1 }), 'doc not upserted properly: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.update({}, { $set: { a: 1 }}, true, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc == null, 'doc upserted: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.update({}, { $set: { a: 1, b: 1 }}, true, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc == null, 'doc upserted: ' + tojson(doc)); // Cannot modify _id! compoundColl.remove({}, false); compoundColl.update({}, { $set: { a: 1, b: 1, _id: 1 }}, true, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc == null, 'doc upserted: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.update({}, { $set: { c: 1 }}, true, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc == null, 'doc upserted: ' + tojson(doc)); // // Partial skey query update // compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ a: 100 }, { a: 100 }, false); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ a: 100 }, { a: 2 }, false); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ a: 100 }, { a: 100, b: 1 }, false); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); // Inspecting query and update alone is not enough to tell whether a shard key will change. compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ a: 100 }, { a: 100, b: 100 }, false); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); // Cannot modify _id! compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ a: 100 }, { a: 100, b: 100, _id: 1 }, false); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ a: 100 }, { $set: { a: 100 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ a: 100 }, { $set: { b: 200 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); // Inspecting query and update alone is not enough to tell whether a shard key will change. compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ a: 100 }, { $set: { b: 100 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ a: 100 }, { $set: { a: 100, b: 200 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); // Inspecting query and update alone is not enough to tell whether a shard key will change. compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ a: 100 }, { $set: { a: 100, b: 100 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); // Cannot modify _id! compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ a: 100 }, { $set: { a: 100, b: 100, _id: 1 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ a: 100 }, { $set: { c: 1 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100, c: 1 }), 'doc did not change: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ a: 100 }, { $rename: { c: 'a' }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); // // Partial skey query upsert // compoundColl.remove({}, false); compoundColl.update({ a: 100 }, { a: 100 }, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc == null, 'doc upserted: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.update({ a: 100 }, { a: 2 }, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle), true); doc = compoundColl.findOne(); assert(doc == null, 'doc upserted: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.update({ a: 100 }, { a: 1, b: 1 }, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc == null, 'doc upserted: ' + tojson(doc)); // Cannot modify _id! compoundColl.remove({}, false); compoundColl.update({ a: 100 }, { a: 1, b: 1, _id: 1 }, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc == null, 'doc upserted: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.update({ a: 100 }, { $set: { a: 1 }}, true, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc == null, 'doc upserted: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.update({ a: 100 }, { $set: { b: 1 }}, true, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc == null, 'doc upserted: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.update({ a: 100 }, { $set: { a: 100, b: 1 }}, true, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc == null, 'doc upserted: ' + tojson(doc)); // Cannot modify _id! compoundColl.remove({}, false); compoundColl.update({ a: 100 }, { $set: { a: 100, b: 1, _id: 1 }}, true, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc == null, 'doc upserted: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.update({ a: 100 }, { $set: { c: 1 }}, true, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc == null, 'doc upserted: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.update({ a: 100 }, { $rename: { c: 'a' }}, true, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc == null, 'doc upserted: ' + tojson(doc)); // // Not prefix of skey query update // compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ b: 100 }, { b: 100 }, false); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ b: 100 }, { b: 2 }, false); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ b: 100 }, { a: 1 }, false); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); // Inspecting query and update alone is not enough to tell whether a shard key will change. compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ b: 100 }, { a: 100 }, false); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ b: 100 }, { a: 1, b: 100 }, false); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); // Inspecting query and update alone is not enough to tell whether a shard key will change. compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ b: 100 }, { a: 100, b: 100 }, false); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); // Cannot modify _id! compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ b: 100 }, { a: 1, b: 1, _id: 1 }, false); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ b: 100 }, { $set: { b: 1 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ b: 100 }, { $set: { a: 1 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ b: 100 }, { $set: { a: 100 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ b: 100 }, { $set: { a: 1, b: 100 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); // Inspecting query and update alone is not enough to tell whether a shard key will change. compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ b: 100 }, { $set: { a: 100, b: 100 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); // Cannot modify _id! compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ b: 100 }, { $set: { a: 100, b: 100, _id: 1 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ b: 100 }, { $set: { c: 1 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100, c: 1 }), 'doc did not change: ' + tojson(doc)); // // Not prefix of skey query upsert // compoundColl.remove({}, false); compoundColl.update({ b: 100 }, { b: 100 }, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc == null, 'doc upserted: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.update({ b: 100 }, { b: 2 }, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc == null, 'doc upserted: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.update({ b: 100 }, { a: 1 }, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc == null, 'doc upserted: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.update({ b: 100 }, { a: 1, b: 1 }, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc == null, 'doc upserted: ' + tojson(doc)); // Cannot modify _id! compoundColl.remove({}, false); compoundColl.update({ b: 100 }, { a: 1, b: 1, _id: 1 }, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc == null, 'doc upserted: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.update({ b: 100 }, { $set: { b: 1 }}, true, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc == null, 'doc upserted: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.update({ b: 100 }, { $set: { a: 1 }}, true, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc == null, 'doc upserted: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.update({ b: 100 }, { $set: { a: 1, b: 1 }}, true, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc == null, 'doc upserted: ' + tojson(doc)); // Cannot modify _id! compoundColl.remove({}, false); compoundColl.update({ b: 100 }, { $set: { a: 1, b: 1, _id: 1 }}, true, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc == null, 'doc upserted: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.update({ b: 100 }, { $set: { c: 1 }}, true, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc == null, 'doc upserted: ' + tojson(doc)); // // Full skey query update // compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ a: 100, b: 100 }, { a: 100 }, false); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ a: 100, b: 100 }, { a: 100, b: 100, c: 100 }, false); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100, c: 100 }), 'doc did not change: ' + tojson(doc)); // Cannot modify _id! compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ a: 100, b: 100 }, { a: 100, b: 100, _id: 100 }, false); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ a: 100, b: 100 }, { b: 100 }, false); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ a: 100, b: 100 }, { $set: { b: 100, c: 1 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100, c: 1 }), 'doc did not change: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ a: 100, b: 100 }, { $set: { a: 100, b: 100, c: 1 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100, c: 1 }), 'doc did not change: ' + tojson(doc)); // Cannot modify _id! compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ a: 100, b: 100 }, { $set: { a: 100, b: 100, _id: 1 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ a: 100, b: 100 }, { $set: { a: 100, b: 2, c: 1 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.insert({ a: 100, b: 100 }); compoundColl.update({ a: 100, b: 100 }, { $set: { c: 1 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100, c: 1 }), 'doc did not change: ' + tojson(doc)); // // Full skey query upsert // compoundColl.remove({}, false); compoundColl.update({ a: 100, b: 100 }, { a: 100 }, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc == null, 'doc was upserted: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.update({ a: 100, b: 100 }, { a: 100, b: 100, c: 1 }, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100, c: 1 }), 'wrong doc: ' + tojson(doc)); // Cannot modify _id! compoundColl.remove({}, false); compoundColl.update({ a: 100, b: 100 }, { a: 100, b: 100, _id: 100 }, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(friendlyEqual(doc, { _id: 100, a: 100, b: 100 }), 'wrong doc: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.update({ a: 100, b: 100 }, { b: 100 }, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc == null, 'doc was upserted: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.update({ a: 100, b: 100 }, { $set: { b: 100, c: 1 }}, true, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc != null, 'doc was not upserted: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.update({ a: 100, b: 100 }, { $set: { a: 100, b: 100, c: 1 }}, true, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc != null, 'doc was not upserted: ' + tojson(doc)); // Can upsert with new _id compoundColl.remove({}, false); compoundColl.update({ a: 100, b: 100 }, { $set: { a: 100, b: 100, _id: 1 }}, true, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc != null, 'doc was not upserted: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.update({ a: 100, b: 100 }, { $set: { a: 100, b: 2, c: 1 }}, true, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc == null, 'doc was upserted: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.update({ a: 100, b: 100 }, { $set: { c: 1 }}, true, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100, c: 1 }), 'wrong doc: ' + tojson(doc)); // // _id query update // compoundColl.remove({}, false); compoundColl.insert({ _id: 1, a: 100, b: 100 }); compoundColl.update({ _id: 1 }, { a: 1 }); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); // Special case for _id. This is for making save method work. compoundColl.remove({}, false); compoundColl.insert({ _id: 1, a: 100, b: 100 }); compoundColl.update({ _id: 1 }, { a: 100, b: 100 }); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.insert({ _id: 1, a: 100, b: 100 }); compoundColl.update({ _id: 1 }, { a: 1, b: 1 }); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.insert({ _id: 1, a: 100, b: 100 }); compoundColl.update({ _id: 1 }, { $set: { a: 1 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.insert({ _id: 1, a: 100, b: 100 }); compoundColl.update({ _id: 1 }, { $set: { a: 100 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.insert({ _id: 1, a: 100, b: 100 }); compoundColl.update({ _id: 1 }, { $set: { b: 1 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.insert({ _id: 1, a: 100, b: 100 }); compoundColl.update({ _id: 1 }, { $set: { b: 100 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.insert({ _id: 1, a: 100, b: 100 }); compoundColl.update({ _id: 1 }, { $set: { a: 1, b: 1 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc)); // // _id query upsert // compoundColl.remove({}, false); compoundColl.update({ _id: 1 }, { a: 1 }, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc == null, 'doc was upserted: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.update({ _id: 1 }, { a: 1, b: 1 }, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { a: 1, b: 1 }), 'bad doc: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.update({ _id: 1 }, { $set: { a: 1 }}, true, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc == null, 'doc was upserted: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.update({ _id: 1 }, { $set: { b: 1 }}, true, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = compoundColl.findOne(); assert(doc == null, 'doc was upserted: ' + tojson(doc)); compoundColl.remove({}, false); compoundColl.update({ _id: 1 }, { $set: { a: 1, b: 1 }}, true, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); assert.eq(0, compoundColl.count(), 'doc should not be inserted'); // // Dotted query update // dotColl.remove({}, false); dotColl.insert({ x: { a: 100 }}); dotColl.update({ 'x.a': 100 }, { x: { a: 100, b: 2 }}); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = dotColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { x: { a: 100, b: 2 }}), 'doc did not change: ' + tojson(doc)); // Dotted field names in the resulting objects should not be allowed. // This check currently resides in the client drivers. dotColl.remove({}, false); dotColl.insert({ x: { a: 100 }}); assert.throws(function() { dotColl.update({ 'x.a': 100 }, { x: { 'a.z': 100 }}); }); // Dotted field names in the resulting objects should not be allowed. // This check currently resides in the client drivers. dotColl.remove({}, false); dotColl.insert({ x: { a: 100 }}); assert.throws(function() { dotColl.update({ 'x.a': 100 }, { 'x.a': 100 }); }); // Dotted field names in the resulting objects should not be allowed. // This check currently resides in the client drivers. dotColl.remove({}, false); dotColl.insert({ x: { a: 100 }}); assert.throws(function() { dotColl.update({ 'x.a': 100 }, { 'x.a.z': 100 }); }); dotColl.remove({}, false); dotColl.insert({ x: { a: 100 }}); dotColl.update({ 'x.a': 100 }, { x: 100 }); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = dotColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { x: { a: 100 }}), 'doc changed: ' + tojson(doc)); dotColl.remove({}, false); dotColl.insert({ x: { a: 100 }}); dotColl.update({ 'x.a': 100 }, { x: { b: 100 }}); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = dotColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { x: { a: 100 }}), 'doc changed: ' + tojson(doc)); dotColl.remove({}, false); dotColl.insert({ x: { a: 100 }}); dotColl.update({ 'x.a': 100 }, { $set: { x: { a: 100, b: 2 }}}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = dotColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { x: { a: 100, b: 2 }}), 'doc did not change: ' + tojson(doc)); dotColl.remove({}, false); dotColl.insert({ x: { a: 100 }}); dotColl.update({ 'x.a': 100 }, { $set: { x: { a: 2 }}}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = dotColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { x: { a: 100 }}), 'doc changed: ' + tojson(doc)); dotColl.remove({}, false); dotColl.insert({ x: { a: 100 }}); dotColl.update({ 'x.a': 100 }, { $set: { x: { b: 100 }}}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = dotColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { x: { a: 100 }}), 'doc changed: ' + tojson(doc)); dotColl.remove({}, false); dotColl.insert({ x: { a: 100 }}); dotColl.update({ 'x.a': 100 }, { $set: { 'x.a': 100, b: 2 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = dotColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { x: { a: 100 }, b: 2 }), 'doc did not change: ' + tojson(doc)); dotColl.remove({}, false); dotColl.insert({ x: { a: 100 }}); dotColl.update({ 'x.a': 100 }, { $set: { x: { 'a.z': 100 }}}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = dotColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { x: { a: 100 }}), 'doc changed: ' + tojson(doc)); dotColl.remove({}, false); dotColl.insert({ x: { a: 100 }}); dotColl.update({ 'x.a': 100 }, { $set: { 'x.a.z': 100 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = dotColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { x: { a: 100 }}), 'doc changed: ' + tojson(doc)); dotColl.remove({}, false); dotColl.insert({ x: { a: 100 }}); dotColl.update({ 'x.a': 100 }, { $set: { x: 100 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = dotColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { x: { a: 100 }}), 'doc changed: ' + tojson(doc)); dotColl.remove({}, false); dotColl.insert({ x: { a: 100 }}); dotColl.update({ 'x.a': 100 }, { $set: { 'x.b': 200 }}, false, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = dotColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { x: { a: 100, b: 200 }}), 'doc did not change: ' + tojson(doc)); // // Dotted query upsert // dotColl.remove({}, false); dotColl.update({ 'x.a': 100 }, { x: { a: 100, b: 2 }}, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = dotColl.findOne(); assert(doc != null, 'doc was not upserted: ' + tojson(doc)); // Dotted field names in the resulting objects should not be allowed. // This check currently resides in the client drivers. dotColl.remove({}, false); assert.throws(function() { dotColl.update({ 'x.a': 100 }, { x: { 'a.z': 100 }}, true); }); // Dotted field names in the resulting objects should not be allowed. // This check currently resides in the client drivers. dotColl.remove({}, false); assert.throws(function() { dotColl.update({ 'x.a': 100 }, { 'x.a': 100 }, true); }); // Dotted field names in the resulting objects should not be allowed. // This check currently resides in the client drivers. dotColl.remove({}, false); assert.throws(function() { dotColl.update({ 'x.a': 100 }, { 'x.a.z': 100 }, true); }); dotColl.remove({}, false); dotColl.update({ 'x.a': 100 }, { x: 100 }, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = dotColl.findOne(); assert(doc == null, 'doc was upserted: ' + tojson(doc)); dotColl.remove({}, false); dotColl.update({ 'x.a': 100 }, { x: { b: 100 }}, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = dotColl.findOne(); assert(doc == null, 'doc was upserted: ' + tojson(doc)); dotColl.remove({}, false); dotColl.update({ 'x.a': 100 }, { $set: { x: { a: 100, b: 2 }}}, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = dotColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { x: { a: 100, b: 2 }}), 'bad doc: ' + tojson(doc)); dotColl.remove({}, false); dotColl.update({ 'x.a': 100 }, { $set: { x: { a: 2 }}}, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = dotColl.findOne(); assert(doc == null, 'doc was upserted: ' + tojson(doc)); dotColl.remove({}, false); dotColl.update({ 'x.a': 100 }, { $set: { x: { b: 100 }}}, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = dotColl.findOne(); assert(doc == null, 'doc was upserted: ' + tojson(doc)); dotColl.remove({}, false); dotColl.update({ 'x.a': 100 }, { $set: { 'x.a': 100, b: 3 }}, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = dotColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { x: { a: 100 }, b: 3 }), 'bad doc: ' + tojson(doc)); dotColl.remove({}, false); dotColl.update({ 'x.a': 100 }, { $set: { 'x.a': 2 }}, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = dotColl.findOne(); assert(doc == null, 'doc was upserted: ' + tojson(doc)); dotColl.remove({}, false); dotColl.update({ 'x.a': 100 }, { $set: { x: { 'a.z': 100 }}}, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = dotColl.findOne(); assert(doc == null, 'doc was upserted: ' + tojson(doc)); dotColl.remove({}, false); dotColl.update({ 'x.a': 100 }, { $set: { 'x.a.z': 100 }}, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = dotColl.findOne(); assert(doc == null, 'doc was upserted: ' + tojson(doc)); dotColl.remove({}, false); dotColl.update({ 'x.a': 100 }, { $set: { x: 100 }}, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err != null, 'gleObj: ' + tojson(gle)); doc = dotColl.findOne(); assert(doc == null, 'doc was upserted: ' + tojson(doc)); dotColl.remove({}, false); dotColl.update({ 'x.a': 100 }, { $set: { 'x.b': 2 }}, true); gle = db.runCommand({ getLastError: 1 }); assert(gle.err == null, 'gleObj: ' + tojson(gle)); doc = dotColl.findOne(); delete doc._id; assert(friendlyEqual(doc, { x: { a: 100, b: 2 }}), 'bad doc: ' + tojson(doc)); st.stop();