我已经尝试让它运行一段时间了,但我不知道我做错了什么。
我有两个这样的模式
const paymentSchema = new Schema({
year_month: {
type: String,
required: true
},
status: {
type: Boolean,
required: true
}
});
const testSchema = new Schema({
name: {
type: String,
required: true
},
payments: [{
type: paymentSchema,
required: false,
}]
});
然后我想更新现有值,或者如果该值不可用,我想将其添加到数组中。
假设我在数据库中有这个值:
[
{
"_id": "5e90ae0e0ed9974174e92826",
"name": "User 1",
"payments": [
{
"_id": "5e90c3fb79bba9571ae58a66",
"year_month": "2020_02",
"status": false
}
]
}
]
现在我想使用以下代码将year_month 2020_02 的状态更改为 true 并且它有效:
testSchema.findOneAndUpdate(
{
_id: '5e90ae0e0ed9974174e92826',
payments: { $elemMatch: { year_month: '2020_02' }}
},
{ $set: {
'payments.$': {
year_month: '2020_02',
status: false
}
}
},
{
new: true,
upsert: true
}
).then( result => {
response.send(result);
});
当我尝试这样做时出现问题
testSchema.findOneAndUpdate(
{
_id: '5e90ae0e0ed9974174e92826',
payments: { $elemMatch: { year_month: '2020_03' }}
},
{ $set: {
'payments.$': {
year_month: '2020_03',
status: false
}
},
},
{
new: true,
upsert: true
}
).then( result => {
response.send(result);
});
我从 upsert 收到此消息...
(node:8481) UnhandledPromiseRejectionWarning: MongoError: The positional operator did not find the match needed from the query.
at Connection.<anonymous> (/home/vedran/Documents/Projekt/node_modules/mongodb/lib/core/connection/pool.js:466:61)
at Connection.emit (events.js:223:5)
at Connection.EventEmitter.emit (domain.js:475:20)
at processMessage (/home/vedran/Documents/Projekt/node_modules/mongodb/lib/core/connection/connection.js:384:10)
at TLSSocket.<anonymous> (/home/vedran/Documents/Projekt/node_modules/mongodb/lib/core/connection/connection.js:553:15)
at TLSSocket.emit (events.js:223:5)
at TLSSocket.EventEmitter.emit (domain.js:475:20)
at addChunk (_stream_readable.js:309:12)
at readableAddChunk (_stream_readable.js:290:11)
at TLSSocket.Readable.push (_stream_readable.js:224:10)
at TLSWrap.onStreamRead (internal/stream_base_commons.js:181:23)
(node:8481) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:8481) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
根据文档Mongoose.findOneAndUpdate() https://mongoosejs.com/docs/api/model.html#model_Model.findOneAndUpdate这应该可行,但我犯了一些错误,而且我不知道到底是什么。
我知道匹配查询是问题所在,但我不确定如何更改它以便应用更新插入。
最后我是这样解决的:
testSchema.findOneAndUpdate(
{
_id: '5e90ae0e0ed9974174e92826',
payments: { $elemMatch: { year_month: '2020_03' }}
},
{
$set: {
'payments.$': {
year_month: '2020_02',
status: false
}
}
},
{new: true}
).then( success => {
// response === null if no match is found
if( success ) {
response.send(success);
} else {
testSchema.findOneAndUpdate(
{ _id: '5e90ae0e0ed9974174e92826' },
{
$push: {
'payments': request.body
}
},
{new: true}
).then(success => {
response.send(success);
});
}
},
error => {
response.send(error);
}
);
但我在这里提出两个请求,这可能会导致竞争条件问题。
1. 更新和
2.如果不存在则添加
我想知道是否有更好的方法让它使用 upsert 并避免竞争条件。
还有一个不错的短篇猫鼬页面上的教程 https://mongoosejs.com/docs/tutorials/findoneandupdate.html它描述了 findOneAndUpdate 上的更新插入,但它不包括数组,这可能是我的情况下问题变得复杂的原因。
最终解决方案基于 joe 和 prasad_ 的回复。
实际上,一旦您花时间了解这里发生的事情,事情并没有那么复杂。
testSchema.findOneAndUpdate(
{ "_id": customerId },
[{
$set: {
payments: {
$cond: [
{
$gt: [
{
$size: {
$filter: {
input: "$payments",
cond: {
$eq: [
"$$this.year_month",
testData.payments.year_month
]
}
}
}
},
0
]
},
{
$reduce: {
input: "$payments",
initialValue: [],
in: {
$concatArrays: [
"$$value",
[{
$cond: [
{ $eq: ["$$this.year_month", testData.payments.year_month] },
{ $mergeObjects: ["$$this", { status: testData.payments.status }] },
"$$this"
]
}]
]
}
}
},
{
$concatArrays: [
"$payments",
[testData.payments]
]
}
]
}
}
}],
{ new: true }
).then(
success => {
response.send(success);
},
error => {
response.send(error);
}
);