Вы имеете в виду что-то вроде этого:
var updateBlock = {};
if (phoneNumber)
updateBlock['shared.phones.$.phone_number'] = phoneNumber;
if (emailAddress)
updateBlock['shared.email.$.email_address'] = emailAddress;
Contacts.updateMany(
{
"originator": profile.owner_id
"owner_id": { "$ne": profile.owner_id }
},
{ "$set": updateBlock },
function(err, numAffected) {
// work with callback
}
)
Это устраняет ваши два «основных» заблуждения, заключающиеся в том, что для «неравенства» в условии запроса требуется оператор $ne
, а not выражение !
JavaScript. MongoDB не использует здесь выражения JavaScript для условий запроса.
Вторым «основным» заблуждением является построение «блока обновления» с условными ключами. Это, напротив, «объект JavaScript», который вы создаете отдельно , чтобы указывать только те ключи, которые вы хотите применить.
Однако есть ЕЩЕ ПРОБЛЕМА , в которой вы хотите использовать позиционный оператор $
. Предполагая, что у вас на самом деле есть «массивы» в документе:
{
"originator": "Bill",
"owner_id": "Ted",
"shared": {
"phones": [ "5555 5555", "4444 4444" ],
"email": [ "bill@stalyns.org", "bill@example.com" ]
}
}
Тогда ваша «двойная» новая проблема такова:
Вы должны указать условие запроса, которое соответствует элементу массива «в блоке запроса», чтобы получить «согласованную позицию», в которой необходимо выполнить обновление.
Вы можете только вернуть ONE индекс согласованного массива с помощью оператора позиционного $
и NOT TWO как было бы присуще обновлению такого документа.
По этим (и другим) причинам настоятельно рекомендуется иметь "несколько массивов" в одном документе. Гораздо лучший подход заключается в использовании «единственного» массива и использовании свойств, чтобы указать, какой «тип» записи элемент списка содержит:
{
"originator": "Bill",
"owner_id": "Ted",
"shared": [
{ "type": "phone", "value": "5555 5555" },
{ "type": "phone", "value": "4444 4444" },
{ "type": "email", "value": "bill@stalyns.org" },
{ "type": "email", "value": "bill@example.com" }
]
}
Таким образом, вы можете обратиться к «подобранному» элементу для обновления:
// phoneNumberMatch = "4444 4444";
// phoneNumber = "7777 7777";
// emailAddress = null; // don't want this one
// emailAddressMatch = null; // or this one
// profile = { owner_id: "Bill" };
var query = {
"originator": profile.owner_id,
"owner_id": { "$ne": profile.owner_id },
"shared": {
"$elemMatch": {
"type": (phoneNumber) ? "phone" : "email",
"value": (phoneNumber) ? phoneNumberMatch : emailAddressMatch
}
}
};
var updateBlock = {
"$set": {
"shared.$.value": (phoneNumber) ? phoneNumber : emailAddress
}
};
Contacts.updateMany(query, updateBlock, function(err, numAffected) {
// work with callback
})
В таком случае и с «двоичным» выбором вы «можете» использовать троичные условия в конструкции, поскольку вы не полагаетесь на «именование ключей» в конструкции.
Если вы хотите, чтобы "либо, либо действительно оба" предоставили значения в комбинации, тогда вам нужно немного более сложное утверждение:
// phoneNumberMatch = "5555 5555";
// phoneNumber = "7777 7777";
// emailAddress = "bill@nomail.com";
// emailAddressMatch = "bill@example.com";
// profile = { owner_id: "Bill" };
var query = {
"originator": profile.owner_id,
"owner_id": { "$ne": profile.owner_id },
"$or": []
};
var updateBlock = { "$set": {} };
var arrayFilters = [];
if (phoneNumber) {
// Add $or condition for document match
query.$or.push(
{
"shared.type": "phone",
"shared.value": phoneNumberMatch
}
);
// Add update statement with named identifier
updateBlock.$set['shared.$[phone].value'] = phoneNumber;
// Add filter condition for named identifier
arrayFilters.push({
"phone.type": "phone",
"phone.value": phoneNumberMatch
})
}
if (emailAddress) {
// Add $or condition for document match
query.$or.push(
{
"shared.type": "email",
"shared.value": emailAddressMatch
}
);
// Add update statement with named identifier
updateBlock.$set['shared.$[email].value'] = emailAddress;
// Add filter condition for named identifier
arrayFilters.push({
"email.type": "email",
"email.value": emailAddressMatch
})
}
Contacts.updateMany(query, updateBlock, arrayFilters, function(err, numAffected) {
// work with callback
})
Здесь, конечно, следует отметить, что синтаксис с позиционной фильтрацией $[<identifier>]
от MongoDB 3.6 и выше требуется для того, чтобы создать несколько элементов массива в одном обновить заявление.
То же самое относится и к «исходной» структуре, которую я впервые описал, используя «множественные» массивы в документах вместо именованных свойств в «единственном» массиве, как в приведенных выше примерах:
var query = {
"originator": "Bill",
"owner_id": { "$ne": "Bill" },
"$or": []
};
var updateBlock = { "$set": {} };
var arrayFilters = [];
if (phoneNumber) {
query.$or.push({
"shared.phones": phoneNumberMatch
});
updateBlock.$set['shared.phones.$[phone]'] = phoneNumber;
arrayFilters.push({
"phone": phoneNumberMatch
});
}
if (emailAddress) {
query.$or.push({
"shared.email": emailAddressMatch
});
updateBlock.$set['shared.email.$[email]'] = emailAddress;
arrayFilters.push({
"email": emailAddressMatch
});
}
Contacts.updateMany(query, updateBlock, arrayFilters, function(err, numAffected) {
// work with callback
})
Конечно, если у вас вообще нет массивов (в опубликованном вопросе отсутствует какой-либо пример документа), то позиционные совпадения даже не нужны ни в какой форме, но вы все же «условно» конструируете «ключи» объекта JavaScript через строительные кодовые блоки. Вы не можете «условно» указать «ключ» в JSON-подобной нотации.