{
_id:xxxxstoreid
store:{
products:[
{
_id:xxxproductid,
name:xxx,
img:url,
}
]
}
}
поскольку я не могу предсказать запрос на обновление, параметры могут иметь только имя или могут иметь оба.
вот мой запрос, он успешно обновляется, но он удаляет другие поля, если они не присутствуют в параметрах. например:
var params={_id:xxid,name:'xxx',img:'xxx'}
или же
var params={_id:xxid,name:'xxx'}
в этом случае, если params имеют только имя, он удаляет поле img и обновления.
User.update({'store.products._id':params._id},{$set:{"store.products":params}},callback);
Вам необходимо предоставить несколько ключей в $set
с оператором positional $
для обновления обоих совпадающих ключей.
Я предпочитаю современный способ обработки объектов ES6:
let params = { "_id" : "xxxproductid", "name" : "xxx", "img" : "yyy" };
let update = [
{ 'store.products._id': params._id },
{ "$set": Object.keys(params).filter(k => k != '_id')
.reduce((acc,curr) =>
Object.assign(acc,{ ['store.products.$.${curr}']: params[curr] }),
{ })
}
];
User.update(...update,callback);
Что вызовет вызов MongoDB, так как (с mongoose.set('debug', true)
) включается, поэтому мы видим запрос:
Mongoose: users.update({'store.products._id': 'xxxproductid'}, {'$ set': {'store.products. $. Name': 'xxx', 'store.products. $. Img': 'yyy'}}, {})
Где в основном вы берете свои исходные params
и поставляете _id
в качестве первого аргумента для "запроса":
{ 'store.products._id': params._id },
Остальное берет "ключи" от объекта через Object.keys
который создает "массив", который мы можем "фильтровать" с помощью Array.filter()
а затем передать в Array.reduce
чтобы преобразовать эти ключи в Object
.
Внутри .reduce()
мы вызываем Object.assign()
который "объединяет" объекты с заданными ключами, сгенерированными в этой форме:
Object.assign(acc,{ ['store.products.$.${curr}']: params[curr] }),
Используя синтаксис шаблона, чтобы назначить "текущий" (текущий) "ключ" в новое имя ключа, снова используя синтаксис выделения ключей ES6 []:
который позволяет имена переменных в объектных литералах.
Полученный "объединенный" объект передается обратно для назначения "корневому" объекту, где для ключа обновления используется $set
, поэтому теперь "сгенерированные" ключи являются дочерними.
Я использую массив для аргументов исключительно для целей отладки, но затем это также позволяет более чистый синтаксис в действительном .update()
используя оператор "spread" ...
чтобы назначить аргументы:
User.update(...update,callback);
Чистые и простые, и некоторые методы JavaScript, которые вы должны изучить для обработки объектов и массивов. В основном, поскольку запрос MongoDB DSL в основном "Объекты" и "Массивы". Поэтому научитесь манипулировать ими.
function updateProducts(params) {
var query = {'store.products': {$elemMatch: {_id: params._id}}}
var updateObject = null;
if (params.name && params.img) {
updateObject = {$set: {'store.products.$': params}}
} else if(params.name && !params.img) {
updateObject = {$set: {'store.products.$.name': params.name}}
} else if (params.img && !params.name) {
updateObject = {$set: {'store.products.$.img': params.img}}
}
User.update(query, updateObject, callback)
}
products
представляет собой массив, поэтому вы не можете получить к нему доступ. Ваш вопрос нуждается в более подробной информации, например, что именно вы называете params.
В приведенном ниже запросе будет использоваться оператор $
positional для нахождения элемента массива по индексу, найденному в результате сопоставления массива с помощью _id
в документе запроса, за которым следует обновление полей для элемента с значениями params
.
var params = {_id:1, name:'xxx',img:'yyy'};
var id = params['_id']; // Get id to locate matching array index
delete params['_id']; // Remove id from the object
Object.keys(params).forEach(function (key){params['store.products.$.'+key] = params[key]; delete params[key];}) // Transforms remaining object keys to include the positional $ placeholder to { "store.products.$.name" : "xxx", "store.products.$.img" : "yyy" }
User.update('store.products._id':id},{$set:params},callback);