mongoose 更新数组或添加到数组

2023-12-29

我已经尝试让它运行一段时间了,但我不知道我做错了什么。

我有两个这样的模式

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);
    }
);

主要问题是findOneAndUpdate正如它的名字所暗示的那样。它执行一个find使用提供的过滤器,如果找到匹配项,则将更新应用到第一个匹配的文档。

如果集合仅包含此文档:

[
    {
        "_id": "5e90ae0e0ed9974174e92826",
        "payments": [
            {
                "year_month": "2020_02",
                "status": false
            }
        ]
    }
]

最初的查找部分本质上是

.find({
        _id: '5e90ae0e0ed9974174e92826',
        payments: { $elemMatch: { year_month: '2020_03' }}
})

这不匹配任何内容,并且由于 upsert 设置为 true,fineOneAndUpdate 尝试创建一个全新的文档。即使它能够从不匹配的位置运算符创建数组,它尝试添加的文档将是:

 {
        "_id": "5e90ae0e0ed9974174e92826",
        "payments": [
            {
                "year_month": "2020_03",
                "status": false
            }
        ]
}

这是不正确的,并且由于重复而无法插入_id无论如何,价值。

如果您使用的是 MongoDB 4.2,您可以使用聚合管道作为第二个参数findAndUpdate检查数组中是否有您感兴趣的元素,如果缺少则添加它。

下面是一种不太漂亮的方法。 findOneAndUpdate 将匹配 _id,管道将:
- 检查数组中的任何元素是否与所需的year_month匹配
- 如果是这样,$reduce 数组来更新该元素中的状态字段
- 如果没有,则附加一个新元素
- 将结果分配回payments

.findOneAndUpdate(
    { "_id": "5e90ae0e0ed9974174e92826" },
    [{$set: {
         payments: {$cond:[
                 {$gt:[
                       {$size:
                             {$filter:{
                                  input:"$payments", 
                                  cond:{$eq:["$$this.year_month","2020_03"]}
                       }}},
                       1
                  ]},
                  {$reduce:{
                        input:"$payments",
                        initialValue:[],
                        in:{$concatArrays:[
                                  "$$value",
                                  [{$cond:[
                                       {$eq:["$$this.j",3]},
                                       {$mergeObjects:["$$this",{status:true}]},
                                       "$$this"
                                  ]}]
                        ]}
                  }},
                  {$concatArrays:[
                       "$payments",
                       [{year_month:"2020_03", status:true}]
                  ]}
          ]}
     }}]
)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

mongoose 更新数组或添加到数组 的相关文章

随机推荐