简体   繁体   中英

Match, update and return last subdocument in array

I want to match subdocuments in an array and update the last element. This query only updates the document with the changeId 0, although I got a document with the id 2 in the array...

const oldDocument = await schemas.Collection.findOneAndUpdate(
                        {
                            "_id": new ObjectId(_id),
                            "manualChanges.id": update.id,
                            "manualChanges.discarded": false
                        },
                        {
                            $set: {
                                "manualChanges.$.discarded": true
                            }
                        },
                        {
                            sort: {'manualChanges.changeId': -1},
                            projection: {"manualChanges.$": 1}
                        }
                    );
                    console.log(oldDocument);

Afterwards I want to return the single updated document, which works, sadly also only without the sort..

From the $ (the positional operator) docs:

the positional $ operator acts as a placeholder for the first element that matches the query document, and

The positional operator always matches the first document in the array, hence is why i'm assuming you tried to descend sort. However while you would think this approach could work it won't as sort is a cursor operator meaning it only takes affect for matching the document. It is actually impossible to sort a nested array in Mongo prior to the upcoming 4.4 version where to plan to introduce custom functions.

So what can you do? if you're using Mongo version 4.2+ they introduced pipelined updates which gives us more power by allowing us to use aggregation expressions within an update, here is a sample of what can be done with it:

db.test.updateOne(
    {
        "_id": new ObjectId(_id),
    },
    [
        {
            $set: {
                filtered: {
                    $filter: {
                        input: "$manualChanges",
                        as: "change",
                        cond: {$ne: ["$$change.discarded", true]}
                    }
                }
            }
        },
        {
            $set: {
                last_id: {
                    $max: "$filtered.id",
                }
            }
        },
        {
            $set: {
                manualChanges: {
                    $map: {
                        input: "$manualChanges",
                        as: "change",
                        in: {
                            $mergeObjects: [
                                "$$change",
                                {
                                    discarded: {$cond: [{$eq: ["$$change.id", "$last_id"]}, true, "$$change.discarded"]}
                                }
                            ]
                        }
                    }
                }
            }
        },
        {
            $unset: ["last_id", "filtered"]
        }
    ])

If you are on a lower Mongo version then you'll have to split this into 2 queries, first find the sub document id and only then you can update it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM