Fixes #4522: Mongo upsert with dotted keys

Break down dotted key-value pairs into an object when simulating
the way Mongo generates an object from a selector in upsert.
This commit is contained in:
gsuess
2015-06-07 20:39:06 +02:00
committed by David Greenspan
parent 0674fc0714
commit e02bf0fb30
2 changed files with 161 additions and 1 deletions

View File

@@ -636,8 +636,38 @@ var simulateUpsertWithInsertedId = function (collection, selector, mod,
// the behavior of modifiers is concerned, whether `_modify`
// is run on EJSON or on mongo-converted EJSON.
var selectorDoc = LocalCollection._removeDollarOperators(selector);
LocalCollection._modify(selectorDoc, mod, {isInsert: true});
newDoc = selectorDoc;
// Convert dotted keys into objects. (Resolves issue #4522).
_.each(newDoc, function (value, key) {
var trail = key.split(".");
if (trail.length > 1) {
//Key is dotted. Convert it into an object.
delete newDoc[key];
var obj = newDoc,
leaf = trail.pop();
// XXX It is not quite certain what should be done if there are clashing
// keys on the trail of the dotted key. For now we will just override it
// It wouldn't be a very sane query in the first place, but should look
// up what mongo does in this case.
while ((key = trail.shift())) {
if (typeof obj[key] !== "object") {
obj[key] = {};
}
obj = obj[key];
}
obj[leaf] = value;
}
});
LocalCollection._modify(newDoc, mod, {isInsert: true});
} else {
newDoc = mod;
}

View File

@@ -2665,6 +2665,136 @@ if (Meteor.isServer) {
name: 'david',
elements: ['Y', 'A', 'B', 'C']});
});
Tinytest.add("mongo-livedata - upsert handles dotted selectors corrrectly", function (test) {
var collection = new Mongo.Collection(Random.id());
var result1 = collection.upsert({
"subdocument.a": 1
}, {
$set: {message: "upsert 1"}
});
test.equal(collection.findOne(result1.insertedId),{
_id: result1.insertedId,
subdocument: {a: 1},
message: "upsert 1"
});
var result2 = collection.upsert({
"subdocument.a": 1
}, {
$set: {message: "upsert 2"}
});
test.equal(result2, {numberAffected: 1});
test.equal(collection.findOne(result1.insertedId),{
_id: result1.insertedId,
subdocument: {a: 1},
message: "upsert 2"
});
var result3 = collection.upsert({
"subdocument.a.b": 1,
"subdocument.c": 2
}, {
$set: {message: "upsert3"}
});
test.equal(collection.findOne(result3.insertedId),{
_id: result3.insertedId,
subdocument: {a: {b: 1}, c: 2},
message: "upsert3"
});
var result4 = collection.upsert({
"subdocument.a": 4
}, {
$set: {"subdocument.a": "upsert 4"}
});
test.equal(collection.findOne(result4.insertedId), {
_id: result4.insertedId,
subdocument: {a: "upsert 4"}
});
var result5 = collection.upsert({
"subdocument.a": "upsert 4"
}, {
$set: {"subdocument.a": "upsert 5"}
});
test.equal(result5, {numberAffected: 1});
test.equal(collection.findOne(result4.insertedId), {
_id: result4.insertedId,
subdocument: {a: "upsert 5"}
});
var result6 = collection.upsert({
"subdocument.a": "upsert 5"
}, {
$set: {"subdocument": "upsert 6"}
});
test.equal(result6, {numberAffected: 1});
test.equal(collection.findOne(result4.insertedId), {
_id: result4.insertedId,
subdocument: "upsert 6"
});
var result7 = collection.upsert({
"subdocument.a.b": 7
}, {
$set: {
"subdocument.a.c": "upsert7"
}
});
test.equal(collection.findOne(result7.insertedId), {
_id: result7.insertedId,
subdocument: {
a: {b: 7, c: "upsert7"}
}
});
var result8 = collection.upsert({
"subdocument.a.b": 7
}, {
$set: {
"subdocument.a.c": "upsert8"
}
});
test.equal(result8, {numberAffected: 1});
test.equal(collection.findOne(result7.insertedId), {
_id: result7.insertedId,
subdocument: {
a: {b: 7, c: "upsert8"}
}
});
var result9 = collection.upsert({
"subdocument.a.b": 7
}, {
$set: {
"subdocument.a.b": "upsert9"
}
});
test.equal(result9, {numberAffected: 1});
test.equal(collection.findOne(result7.insertedId), {
_id: result7.insertedId,
subdocument: {
a: {b: "upsert9", c: "upsert8"}
}
});
});
}
// This is a VERY white-box test.