簡體   English   中英

MongoDB $ inc和$ setOnInsert用於嵌套數組

[英]MongoDB $inc and $setOnInsert for nested array

我已經搜索了SO,並檢索了Mongoose / Mongo文檔,但無濟於事,因此是我的問題。

我想在嵌套數組中的對象中增加$inc的值,或者創建$setOnInsert此對象(如果尚不存在)。

我擁有的Mongo文件如下:

{
  "_id": "123",
  "members": [
    {
      "first": "johnny",
      "last": "knoxville",
      "score": 2
    },
    {
      "first": "johnny",
      "last": "cash",
      "score": 3
    },
    // ...and so on
  ],
}

根據此示例,我的用例是:

  • 如果存在,則增加數組對象內的count變量(根據firstlast
  • 如果對象尚不存在,則將其添加到score為1的集合中

這篇文章中,我了解到我不能$set我想同時$inc的變量。 好的-這很有道理。

這篇文章有助於理解位置$運算符,以便找到嵌套對象並對其進行遞增。

如果我知道該文檔存在,則可以按如下所示進行更新:

myDoc = { first: 'johnny', last: 'cash' };

myCollection.findOneAndUpdate(
  {
    _id: '123',
    'members.first': 'johnny',
    'members.last': 'cash'
  },
  {
    $inc: { "members.$.score": 1 }
  }
);

但是,如果我還想插入該成員( score: 1 ),該怎么辦呢?

我的問題是,當我使用upsert:true時,位置運算符會引發錯誤,因為它可能無法與upsert一起使用(請參閱官方文檔 )。

我嘗試了各種組合,並希望避免2個數據庫訪問(讀/寫)。

有沒有一種方法可以做到這一點?

在MongoDB 4.2及更高版本中,更新方法現在可以獲取文檔或聚合管道 ,可以在其中使用以下階段:

  1. $addFields及其別名$set
  2. $project及其別名$unset
  3. $replaceRoot及其別名$replaceWith

有了以上內容,您對聚合管道的更新操作將根據條件(即模板如下)覆蓋members字段:

var myDoc = { first: 'johnny', last: 'cash', score: 1 };

db.getCollection('myCollection').update(
    { "_id" : "123" },
    [
        "$set": {
            "members": {
                "$cond": {
                    "if": {}, // does the members array contain myDoc?
                    "then": {}, // map the members array and increment the score
                    "else": {} // add myDoc to the existing members array
                }
            }
        }
    ]
);

為了獲得第一個條件的表達式, 成員數組是否包含myDoc? ,您需要一種方法,根據滿足與myDoc分別具有相同值的first和last屬性的條件來對數組進行$filter ,即

{
    "$filter": {
        "input": "$members",
        "cond": {
            "$and": [
                { "$eq": ["$$this.first", myDoc.first] },
                { "$eq": ["$$this.last", myDoc.last] },
            ]
        }
    }
}

使用$arrayElemAt檢查結果數組的第一個元素

{
    "$arrayElemAt": [
        {
            "$filter": {
                "input": "$members",
                "cond": {
                    "$and": [
                        { "$eq": ["$$this.first", myDoc.first] },
                        { "$eq": ["$$this.last", myDoc.last] },
                    ]
                }
            }
        },
        0
    ]
}

如果沒有匹配項,則上面的內容將為null,我們可以用可以用作$ifNull的主要條件的值替換null:

{
    "$ifNull": [
        {
            "$arrayElemAt": [
                {
                    "$filter": {
                        "input": "$members",
                        "cond": {
                            "$and": [
                                { "$eq": ["$$this.first", myDoc.first] },
                                { "$eq": ["$$this.last", myDoc.last] },
                            ]
                        }
                    }
                },
                0
            ]
        },
        0
    ]   
}

以上是我們使用$ne檢查不等式的第一條IF語句的條件的基礎:

{ "$ne": [
    {
        "$ifNull": [
            {
                "$arrayElemAt": [
                    {
                        "$filter": {
                            "input": "$members",
                            "cond": {
                                "$and": [
                                    { "$eq": ["$$this.first", myDoc.first] },
                                    { "$eq": ["$$this.last", myDoc.last] },
                                ]
                            }
                        }
                    },
                    0
                ]
            },
            0
        ]   
    },
    0
] }

如果上述條件成立,則$map表達式變為

{                        
    "$map": {
        "input": "$members",
        "in": {
            "$cond": [
                { "$eq": [{ "$ifNull": ["$$this.score", 0 ] }, 0] },
                { "$mergeObjects": ["$$this", { "score": 1 } ] },
                { "$mergeObjects": ["$$this", { "score": { "$sum": ["$$this.score", 1] } } ] }
            ]
        }

    }
}

否則使用$concatArrays將新文檔添加到現有成員數組中

{
    "$concatArrays": [
        { "$ifNull": ["$members", []] },
        [ myDoc ]
    ]
}

您的最終更新操作將變為:

var myDoc = { first: 'johnny', last: 'cash', score: 1 };

db.getCollection("myCollection").update(
    { "_id" : "123" },
    [
        { "$set": {
            "members": {
                "$cond": {
                    "if": {
                        "$ne": [
                            {
                                "$ifNull": [
                                    {
                                        "$arrayElemAt": [
                                            {
                                                "$filter": {
                                                    "input": "$members",
                                                    "cond": {
                                                        "$and": [
                                                            { "$eq": ["$$this.first", myDoc.first] },
                                                            { "$eq": ["$$this.last", myDoc.last] },
                                                        ]
                                                    }
                                                }
                                            },
                                            0
                                        ]
                                    },
                                    0
                                ]   
                            },
                            0
                        ]
                    },
                    "then": {
                        "$map": {
                            "input": "$members",
                            "in": {
                                "$cond": [
                                    { "$eq": [{ "$ifNull": ["$$this.score", 0 ] }, 0] },
                                    { "$mergeObjects": ["$$this", { "score": 1 } ] },
                                    { "$mergeObjects": ["$$this", { "score": { "$sum": ["$$this.score", 1] } } ] }
                                ]
                            }

                        }
                    },
                    "else": {
                        "$concatArrays": [
                            { "$ifNull": ["$members", []] },
                            [ myDoc ]
                        ]
                    }
                }
            }
        } }
    ],
    { "upsert": true } 
);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM