У меня проблема с findOneAndUpdate в мангусте. Дело в том, что я обновляю документ, найдя его. Запрос выглядит следующим образом:
UserModel.findOneAndUpdate({
individualId: 'some id'
}, {
$push: {
supporterOf: 'some string'
}
})
«supporterOf» — это ссылка на UserModel, а его тип — «ObjectId».
The issue i am facing here is that, 'some string' is being pushed twice under 'supporterOf' in the document.
Может ли кто-нибудь сказать мне, как вставить элемент массива внутрь документа?
Нет, я не пробовал ручную выборку, а затем обновление .save(), потому что я хочу, чтобы транзакция выполнялась за один раз. Я хочу найти и обновить результат, его обновление, но вставку повторяющегося элемента. У меня также есть отладка кода, я получаю одно значение из конечной точки. Не могли бы вы сказать мне, будет ли время транзакции одинаковым, когда я (ручная выборка и отправка) и когда я (использую findOneAndUpdate)?
Это будет медленнее, но если вы не планируете обновлять много (например, 1000+) документов одновременно или у вас очень низкие аппаратные ограничения, вы сможете пренебречь этими различиями. Тем не менее, я бы посоветовал вам попытаться отладить эту проблему, поскольку я считаю, что может быть основная проблема, которая может вызвать дальнейшие проблемы в будущем. Поэтому, не могли бы вы опубликовать доказательство концепции вашей проблемы?
Да, @BenSower, вы правы, это будет медленнее, но поверьте мне, это происходит прямо сейчас, когда findOneAndUpdate вставляет повторяющуюся запись в Pushed items. Чтобы повторить проблему, создайте схему, в которой у вас есть пустой массив. Затем запустите findOneAndUpdate и нажмите элементы, как в предыдущем посте, вы получите ту же проблему с повторяющимися записями.
Я создал этот gist gist.github.com/BenSower/9800a21c2ae4202d81a46fc64bc55b9e, который дважды вызывает findOneAndUpdate и каждый раз отправляет только одну строку. Какую версию мангуста вы используете?



Недавно я столкнулся с той же проблемой. Тем не менее, мне удалось решить эту проблему с помощью некоторых других логик (подробности приведены ниже), но я не мог понять причину того, почему найтиOneAndUpdate вставляет записи дубликат в mongodb.
Вы можете решить эту проблему, следуя логике.
Используйте найтиодин или найти по идентификатору вместо найтиOneAndUpdate для поиска документа в вашей коллекции, а затем вручную обновите документ и запустите спасти().
Вы можете лучше понять этот фрагмент кода
return new Promise(function (resolve, reject) {
Model.findOne({
someCondition...
}, function (err, item) {
if (err) {
reject(err);
} else {
item.someArray.push({
someKeyValue...
});
item.save().then((result) => {
resolve(result)
}).catch((err) => {
reject(err)
});
}
}).catch((err) => {
reject(err)
});
});
Это не будет вставлять повторяющийся элемент. Однако, если вы узнаете причину дублирования, обязательно обновите эту ветку.
@Faizy Вы знаете, что мангуст также напрямую возвращает обещание (если только вы не используете довольно старую версию), поэтому вам не нужно снова заключать его в другое обещание?
@BenSower По сути, Асад Улла рассказал мне обходной путь дублирования записей. Я хочу отменить повторяющиеся записи из базы данных, и прямо сейчас findOneAndUpdate вызывает проблему повторяющихся записей.
Да, но это не меняет того факта, что можно улучшить стиль кода, используя более понятные функции ;-)
У меня была такая же проблема, решение есть.
Я ждал, как показано ниже.
**await** schema.findOneAndUpdate(queryParms, {
"$push": {
"array1": arrayDetails,
"array2": array2Details
}
}, {
"upsert": true,
"new": true
},
function (error, updateResponse) {
if (error) {
throw new Error (error);
} else {
// do something with updateResponse;
}
});
простое удаление ожидания помогло мне решить эту проблему. Нужно найти первопричину. любой указатель для ссылок приветствуется.
«Смешивание обещаний и обратных вызовов может привести к дублированию записей в массивах», прямо из mongoosejs.com/docs/queries.html
У меня была такая же проблема. Мой код был:
const doc = await model.findOneAndUpdate(
{filter}, {update},
{new: true}, (err, item) => if (err) console.info(err) }
)
res.locals.doc = doc
next();
Дело в том, что по какой-то причине этот обратный вызов после «новой» опции создавал двойную запись. Я удалил обратный вызов, и это сработало.
У меня такая же проблема. Я нашел решение для себя:
Я использовал обратный вызов и обещание (поэтому использовал ключевое слово «ожидание») одновременно.
Using a callback and a promise simultaneously will result in the query being executed twice. You should be using one or the other, but not both.
options = {
upsert: true // creates the object if it doesn't exist. defaults to false.
};
await Company.findByIdAndUpdate(company._id,
{ $push: { employees: savedEmployees } },
options,
(err) => {
if (err) {
debug(err);
}
}
).exec();
к
options = {
upsert: true // creates the object if it doesn't exist. defaults to false.
};
await Company.findByIdAndUpdate(company._id,
{ $push: { employees: savedEmployees } },
options
).exec();
UserModel.findOneAndUpdate(
{ _id: id },
{ object }
)
Даже если вы используете _id в качестве параметра, не забудьте сделать фильтр явным по id
Проблема с принятым ответом заключается в том, что он решает проблему только путем заключения его в ненужное дополнительное обещание, когда метод findOneAndUpdate() уже возвращает обещание. Кроме того, он использует как обещания, так и обратные вызовы, чего вам почти никогда не следует делать.
Вместо этого я бы выбрал следующий подход:
Обычно я предпочитаю отделять логику запроса на обновление от других проблем как для удобочитаемости, так и для повторного использования. поэтому я бы сделал функцию-оболочку вроде:
const update = (id, updateObj) => {
const options = {
new: true,
upsert: true
}
return model.findOneAndUpdate({_id: id}, {...updateObj}, options).exec()
}
Затем эту функцию можно было бы повторно использовать во всем моем приложении, избавляя меня от необходимости переписывать повторяющиеся настройки параметров или вызовы exec.
Затем у меня была бы какая-то другая функция, отвечающая за вызов моего запроса, передачу ему значений и обработку того, что возвращается от него.
Что-то вроде:
const makePush = async () => {
try {
const result = await update('someObjectId', {$push: {someField: value}});
// do whatever you want to do with the updated document
catch (e) {
handleError(e)
}
}
Нет необходимости создавать ненужные обещания, никакого ада обратных вызовов, никаких дублирующих запросов и строгое соблюдение принципов единой ответственности.
Проблема, похоже, связана с объединением ожидания и обратного вызова. У меня была такая же проблема, пока я не понял, что использую обратный вызов (err, resp) а также a .catch(...).
models[auxType].findOneAndUpdate(
filter,
updateObject,
options,
(err, resp)=>{
if (err) {
console.info("Update failed:",err)
res.json(err)
} else if (resp) {
console.info("Update succeeded:",resp)
res.json(resp)
} else {
console.info("No error or response returned by server")
}
})
.catch((e)=>{console.info("Error saving Aux Edit:",e)}); // << THE PROBLEM WAS HERE!!
Проблема решилась, как только я удалил строку .catch(...).
Из документации мангуста:
В моем случае изменение обратного вызова асинхронный решило проблему.
изменить это:
await schema.findOneAndUpdate(
{ queryData },
{ updateData },
{ upsert: true },
(err) => {
if (err) console.info(err);
else await asyncFunction();
}
);
К этому:
await schema.findOneAndUpdate(
{ queryData },
{ updateData },
{ upsert: true },
(err) => {
if (err) console.info(err);
}
);
if (success) await asyncFunction();
Используйте $addToSet вместо $push, это должно решить проблему. Я считаю, что есть проблема со структурой данных, используемой при создании «Модели» мангуста. Как мы знаем, push — это операция массива (которая допускает дублирование), в то время как addToSet может быть операцией Set (Sets не допускают дублирования).
$addToSet вместо $push позволил мне предотвратить дублирование записи в моем поле массива mongoDb пользовательского документа, подобного этому.
const blockUserServiceFunc = async(req, res) => {
let filter = {
_id : req.body.userId
}
let update = { $addToSet: { blockedUserIds: req.body.blockUserId } };
await User.findOneAndUpdate(filter, update, (err, user) => {
if (err) {
res.json({
status: 501,
success: false,
message: messages.FAILURE.SWW
});
} else {
res.json({
status: 200,
success: true,
message: messages.SUCCESS.USER.BLOCKED,
data: {
'id': user._id,
'firstName': user.firstName,
'lastName': user.lastName,
'email': user.email,
'isActive': user.isActive,
'isDeleted': user.isDeleted,
'deletedAt': user.deletedAt,
'mobileNo': user.mobileNo,
'userName': user.userName,
'dob': user.dob,
'role': user.role,
'reasonForDeleting': user.reasonForDeleting,
'blockedUserIds': user.blockedUserIds,
'accountType': user.accountType
}
});
}
}
).catch(err => {
res.json({
status: 500,
success: false,
message: err
});
});
Ваш запрос выглядит нормально, я считаю, что ваша проблема кроется где-то еще в вашем коде. Не могли бы вы опубликовать остальную часть вашей конечной точки? Вы пытались вручную получить документ, поместив строку в массив и сохранив ее с помощью .save()?