簡體   English   中英

在子文檔上使用Mongodb findAndModify

[英]Using the Mongodb findAndModify on subdocuments

我有一個集合,該集合的文檔包含稱為“標簽”的子文檔數組。 我希望能夠在標簽級別上找到findAndModify 目的是鎖定每個標簽,以便在不鎖定整個文檔的情況下,用戶界面一次不允許多個用戶查看每個標簽。 此刻,我只是試圖為每個標簽添加一個名為“ lock”的字段,因為它被我的findAndModify查詢擊中了。

該查詢當前部分起作用。 它將找到一個文檔,該文檔至少具有一個不包含“鎖定”字段的標簽。 但是,即使第一個標簽已被鎖定,它也只會將鎖添加到第一個標簽。 這是我當前的查詢:

db.manual_ncsc_test.findAndModify({query: {$and: [{"labels.x": 201}, {"labels": {$elemMatch: {$exists: {"lock": false}}}}]}, sort:{"labels.lock": -1}, update: {$set: {"labels.$.lock": "TEST!!!!"}}, upsert: false})

$and的第一部分只是確保我每次都在同一文檔上進行測試。 然后,我使用$elemMatch檢查每個標簽是否有鎖。

我嘗試了各種嘗試來解決問題,但我相信它只是在主文檔級別進行排序,而不是標簽本身。

您可以看到我正在使用位置運算符將鎖添加到匹配的標簽。 這確實讓我感到困惑,位置運算符應該只擊中與查詢匹配的標簽,但是無論是否有鎖,它每次都會修改數組中的第一個標簽(通過更改$set的文本來確認) 。

編輯:防止任何其他不正確的假設; 這些標簽對象無需輸入即可提供給用戶。 用戶僅在由node.js服務器選擇標簽后才與標簽進行交互,因此唯一的要求是標簽是標簽,並且尚未被其他用戶檢查(鎖定)。 在運行findAndModify之前,服務器無法知道有關標簽的任何信息,因此無法搜索ID和其他唯一信息。

確實,您似乎對如何將.findAndModify()以及其他一些使用問題與“ sort”選項一起使用產生了.findAndModify()

當語句的“查詢”部分與多個文檔匹配時,將應用“排序”的目的。 在這種情況下,由於.findAndModify() 一次僅作用於一個文檔,因此“排序”用於確定將使用“更新”或其他操作(例如“刪除”)來處理“哪個文檔”。 它不會對數組進行排序,實際上沒有標准操作能對數組進行排序,只是$sort修飾符可用於聚合框架可在查詢中進行的更新和操作。

將特定的“索引”匹配到數組的主要問題是,需要比較數組元素的“多個”屬性以確定位置$匹配。 $elemMatch運算符通過“查詢”所提供條件的每個元素來執行此操作。

對於您正在描述的事情,您想要的是打算更改或編輯的數組元素上的“唯一標識符”。 我將在此處使用每個數組元素的ObjectId()值作為示例:

{
    "_id" : ObjectId("53cf1b1c71c5e451279fce3b"),
    "labels" : [
            {
                    "_id" : ObjectId("53cf1b1c71c5e451279fce37"),
                    "name" : "A"
            },
            {
                    "_id" : ObjectId("53cf1b1c71c5e451279fce38"),
                    "name" : "B"
            },
            {
                    "_id" : ObjectId("53cf1b1c71c5e451279fce39"),
                    "name" : "C"
            },
            {
                    "_id" : ObjectId("53cf1b1c71c5e451279fce3a"),
                    "name" : "D"
            }
    ]
}

要“獲取”當前修飾符的數據,請向您的.findAndModify()發出斷言,即元素“ _id”必須匹配並且使用$exists對該元素不存在鎖定屬性。 在“更新”部分,您將$set被鎖定的屬性:

db.sample.findAndModify({
    "query": {
        "_id": ObjectId("53cf1b1c71c5e451279fce3b"),
        "labels": {
            "$elemMatch": {           
                "_id": ObjectId("53cf1b1c71c5e451279fce38"),
                "locked": { "$exists": false }
            }
        }
    },
    "update": {
        "$set": { "labels.$.locked": true },
    },
    "new": true
});

您可以選擇使用“ fields”選項來投影匹配的字段,盡管在某些語言中這可能不是實際驅動程序實現的一部分,並且將數組元素與查詢中的參數進行匹配很簡單。 但是通常,文檔現在看起來像這樣:

{
    "_id" : ObjectId("53cf1b1c71c5e451279fce3b"),
    "labels" : [
            {
                    "_id" : ObjectId("53cf1b1c71c5e451279fce37"),
                    "name" : "A"
            },
            {
                    "_id" : ObjectId("53cf1b1c71c5e451279fce38"),
                    "name" : "B",
                    "locked": true
            },
            {
                    "_id" : ObjectId("53cf1b1c71c5e451279fce39"),
                    "name" : "C"
            },
            {
                    "_id" : ObjectId("53cf1b1c71c5e451279fce3a"),
                    "name" : "D"
            }
    ]
}

如您所見,具有相同參數的任何后續查詢實際上都不會與任何文檔匹配,因為所選數組元素實際上具有“鎖定”屬性。 這將阻止其他進程同時訪問相同的“標簽”進行編輯。

但是,可以返回任何其他數組元素以及獲得所需的“鎖”。 此處的$elemMatch確保同時滿足兩個條件並選擇正確的元素,當然,只有一個元素會匹配給定的查詢。 因此,下面的代碼將允許另一個進程“鎖定”數組中的最后一個元素進行編輯:

db.sample.findAndModify({
    "query": {
        "_id": ObjectId("53cf1b1c71c5e451279fce3b"),
        "labels": {
            "$elemMatch": {           
                "_id": ObjectId("53cf1b1c71c5e451279fce3a"),
                "locked": { "$exists": false }
            }
        }
    },
    "update": {
        "$set": { "labels.$.locked": true },
    },
    "new": true
});

現在在數據庫中並到返回的文檔中,“兩個”元素當前已“鎖定”:

{
    "_id" : ObjectId("53cf1b1c71c5e451279fce3b"),
    "labels" : [
            {
                    "_id" : ObjectId("53cf1b1c71c5e451279fce37"),
                    "name" : "A"
            },
            {
                    "_id" : ObjectId("53cf1b1c71c5e451279fce38"),
                    "name" : "B",
                    "locked": true
            },
            {
                    "_id" : ObjectId("53cf1b1c71c5e451279fce39"),
                    "name" : "C"
            },
            {
                    "_id" : ObjectId("53cf1b1c71c5e451279fce3a"),
                    "name" : "D",
                    "locked": true
            }
    ]
}

並且,當您要修改文檔時,請發出“ locked”對於元素為true的信息,然后$unset the locked屬性以及發出更改的值:

db.sample.findAndModify({
    "query": {
        "_id": ObjectId("53cf1b1c71c5e451279fce3b"),
        "labels": {
            "$elemMatch": {           
                "_id": ObjectId("53cf1b1c71c5e451279fce38"),
                "locked": { "$exists": true }
            }
        }
    },
    "update": {
        "$unset": { "labels.$.locked": true },
        "$set": { "labels.$.name": "C" }
    },
    "new": true
});

因此,這現在修改了文檔中的數組元素,表明此過程具有“鎖定”功能,並且不會影響其他鎖定元素。 剩下的另一個過程是發布自己的更新以更改該元素:

{
    "_id" : ObjectId("53cf1b1c71c5e451279fce3b"),
    "labels" : [
            {
                    "_id" : ObjectId("53cf1b1c71c5e451279fce37"),
                    "name" : "A"
            },
            {
                    "_id" : ObjectId("53cf1b1c71c5e451279fce38"),
                    "name" : "C"
            },
            {
                    "_id" : ObjectId("53cf1b1c71c5e451279fce39"),
                    "name" : "C"
            },
            {
                    "_id" : ObjectId("53cf1b1c71c5e451279fce3a"),
                    "name" : "D",
                    "locked": true
            }
    ]
}

這是獲取所需原子更新的一般方法,同時確保在進行編輯時,沒有其他更新語句可以修改同一元素。

對於獎勵積分,還可以添加一個timestamp屬性或簡單地將locked屬性設置為當前時間。 這樣,您就可以定期檢查和“解鎖”可能已經“過期”或以其他方式沒有完成的任何編輯,並單獨對其進行解鎖。

暫無
暫無

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

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