學習mongodb,體會mongodb的每一個使用細節,歡迎閱讀威贊的文章。這是威贊發布的第63篇mongodb技術文章,歡迎瀏覽本專欄威贊發布的其他文章。
閱讀了不少Mongodb的文章,也和同事交流過。Mongodb數組更新是比較難理解的地方,而且使用到的頻率也相對較低。但Mongodb官網對這部分知識介紹的很詳細,深入閱讀和實踐后對工作和學習也有很大的啟發。所以整理出來,共大家研究參考。也希望可以和您一起探討Mongodb的知識。
定義
位置操作符$, 指定了數組中需要更新的元素。使用$符號,避免了顯示指定數組中需要更新元素的位置。
語法
位置操作符$,要按照下面的形式使用
{"<array>.$": value}
其中,位置操作符$符號,表示符合查詢結果的第一個元素。而需要更新的數組字段名稱,必須存在于查詢過濾條件當中。
如
db.collection.updateOne({<array>: value, ...},{<update operator>: {"<array>.$": value}}
)
行為
- 自mongodb5.0開始,UPDATE操作按照字段名稱的字典順序更新字段。當字段中包含數字時,按照數字順序依次更新字段。當然,對一個文檔的多個字段操作,是原子性的。
- 使用upsert時,不可以使用位置操作符$。插入數據時,$符號會被看做字段名稱。
- 位置$操作符不能用于遍歷多個數組的查詢,例如遍歷嵌套在其他數組中的數組的查詢,因為$占位符的替換是單個值
- 在$unset操作中,位置操作符更新數據時,不會刪除數組元素,而是將該數組元素設置為null。
- 位置操作符不能用在反向操作中,如$ne, $not, $nin. 但在$elemMatch操作中使用反向過濾條件時,用戶可以使用位置操作符更新字段。
- 在對多個數組字段進行過濾時,位置$ update操作符的行為是有歧義的。當服務器執行update方法時,它首先運行一個查詢來確定要更新哪些文檔。如果update過濾了多個數組字段,那么后續對位置$ update操作符的調用并不總是更新數組中所需的位置。
應用
更新數組中的元素
創建集合students并插入數據。其中字段grades是包含三個元素的數組。
db.students.insertMany([{ "_id" : 1, "grades" : [ 85, 80, 80 ] },{ "_id" : 2, "grades" : [ 88, 90, 92 ] },{ "_id" : 3, "grades" : [ 85, 100, 90 ] }
])
將grades數組中第一個為80的元素值更新到82. 若用戶不清楚80在數組中的位置是,可以使用位置操作符$。 但要注意,查詢條件中必須包含要更新的數組。
db.students.updateOne({ _id: 1, grades: 80}, //查詢_id是1,數組中包含80的文檔{ $set: {"grades.$": 82}} //將查詢到的文檔中,第一個為80的數組元素更新為80)
在查詢語序中,位置操作符$符號,找到了第一個符合查詢條件的數組元素。
更新數組中的文檔
在UPDATE中使用位置操作符$,也可以更新包含文檔類型的數組。使用$操作符更新文檔數組時,需要使用點操作符。
db.collection.updateOne({<query selector>},{<update operator>: {"array.$.field": value}}
)
在students集合中添加下面的數據文檔
db.students.insertOne({_id: 4,grades: [{ grade: 80, mean: 75, std:8},{ grade: 85, mean: 90, std:5},{ grade: 85, mean: 85, std: 8 }]
})
構造數據更新語句,更新符合grade等于85的第一個數組元素,將std值更新為6
db.students.updateOne({_id: 4, "grades.grade": 85}, //此處必須包含grades數組作為過濾條件{ $set: {"grades.$.std": 6}})
使用符合查詢條件更新內嵌數組文檔
使用位置操作符$,可以更新符合復合查詢條件數組中的第一個元素。
向students集合中,插入數據。
db.students.insertOne({_id: 5,grades: [{ grade: 80, mean: 75, std:8},{ grade: 85, mean: 90, std:5},{ grade: 90, mean: 85, std: 3 }]
})
在下面的更新語句中,使用$elemMatch構建了一個符合查詢條件,過濾數組grades中的文檔。
db.students.updateOne({_id: 5,grades: { $elemMatch: {grade: {$lte:90}, mean: {$gt:80}}}
}, {$set: {"grades.$.std": 6}})
過濾grades小于等于80, mean大于80的數據,即數組中第二個和第三個元素。使用$更新第一個找到的元素。只是將std:5更新到了6.
多數組匹配更新
在對多個數組字段進行過濾時,位置$ update操作符的行為是有歧義的。
構建集合students_deans_list. 其中包括三個數組字段, activity_ids, grades, deans_list.
db.students_deans_list.insertMany([{_id: 8,activity_ids: [ 1, 2 ],grades: [ 90, 95 ],deans_list: [ 2021, 2020 ]}])
構建下面的數據更新語句。用戶嘗試更新字段deans_list. 使用activity_ids, deans_list和grades字段進行過濾。將數值2021更新成2022.
db.students_deans_list.updateOne({ activity_ids: 1, grades: 95, deans_list:2021},{ $set: {"deans_list.$": 2022}})
當執行更新語句時,盡管使用過濾條件,查詢出了_id為8的數據 ,但位置操作符$更新了數值2020的數據。
{"_id" : 8,"activity_ids" : [ 1, 2 ],"grades" : [ 90, 95 ],"deans_list" : [ 2021, 2022 ]
}
為了避免這個問題,Mongodb給出了位置操作符的另外一種使用方法。$[<identifier>].我們下一篇文章就詳細描述這個方法。