猫鼬更新数组或添加到数组 [英] mongoose update array or add to the array

查看:61
本文介绍了猫鼬更新数组或添加到数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经尝试使它运行一段时间了,但是我不知道自己在做什么错.

I've been trying to get this running for a while now but I can't figure out what I'm doing wrong.

我有两个这样的模式

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

然后,我想更新现有值,或者如果该值不可用,我想将其添加到数组中.

Then I want to update the existing value or if that value is not available I'd like to add it to the array.

假设我在数据库中有以下值:

Let's say I have this values in the DB:

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

现在,我想使用此代码将year_month 2020_02的状态更改为true,并且它可以正常工作:

Now I'd like to change the status of year_month 2020_02 to true with this code and it works:

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

我尝试执行此操作时出现了问题

The problem appears when I try to do this

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得到此消息的...

I get this message from the 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(),这应该工作,但我犯了一些错误,我无法弄清楚到底是什么. 我知道匹配查询是个问题,但是我不确定如何更改它,以便应用upsert.

According to the docs Mongoose.findOneAndUpdate() this should work but I'm making some mistake, and I can't figure out what exactly. I know the match query is the issue but I'm not sure how to change it so the upsert gets applied.

最后我像这样解决了它:

In the end I solved it like this:

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.如果不存在则添加

But I'm making two requests here which could cause the race condition issues. 1. to update and 2. to add if it doesn't exist

我想知道是否有更好的方法可以使它使用upsert并避免出现竞争情况.

I'd like to know if there is a better way to make it use the upsert and avoid the race conditions.

猫鼬页面上还有一个很好的简短教程,其中描述了findOneAndUpdate,但它不包含数组,这可能会使我的问题复杂化.

There is also a nice short tutorial on mongoose page which describes the upsert on findOneAndUpdate but it doesn't include arrays and this is probably what complicates the issue in my case.

基于joe& amp;的答复的最终解决方案prasad_. 实际上,一旦您花时间了解这里发生的事情,事情就没那么复杂了.

Final solution based on the responses from joe & prasad_. Actually it is not that complicated once you take the time to understand what's going on here.

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,如果找到匹配项,则将更新应用于第一个匹配的文档.

The main problem is that findOneAndUpdate does exactly what its name implies. It executes a find using the provided filter, and if a match is found, applies the updates to the first matching document.

如果集合仅包含此文档:

If the collection contains only this document:

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

最初的查找部分基本上是

The initial find part is essentially

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

这没有任何匹配,并且由于upsert设置为true,fineOneAndUpdate尝试创建一个全新的文档.即使它能够使用不匹配的位置运算符创建数组,它要尝试添加的文档也会是:

This matches nothing, and since upsert is set to true, fineOneAndUpdate attempts to create a brand new document. Even if it were able to create an array from an unmatched positional operator, the document it would be trying to add would be:

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

这是不正确的,并且由于重复的_id值而无法插入.

This is not correct, and would fail to insert due to duplicate _id value anyway.

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

If you are using MongoDB 4.2, you could use an aggregation pipeline as the second argument to findAndUpdate to check the array for the element you are interested in and add it if it is missing.

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

One not very pretty method is below. The findOneAndUpdate will match the _id, and the pipeline will:
- check if any element in the array matches the desired year_month
- If so, $reduce the array to update the status field in that element
- If not, append a new element
- Assign the result back to 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}]
                  ]}
          ]}
     }}]
)

这篇关于猫鼬更新数组或添加到数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆