mongodb 数组操作运算符

作者:杨润炜
日期:2016/1/27 12:02

mongodb对于使用node.js开发的人来说应该不陌生,她与node.js的结合,十分的和谐且高效。接下来介绍一下mongodb的数组运算符,它们分别是$(update), $addToSet, $pop, $pullAll, $pull, $pushAll, $push, $each, $slice, $sort 与 $position,让我们来看看它们是怎样来为我们操作mongodb的数组字段的。

$(update)

简述:可以用来更新数组字段里是某个属性
用法:

  1. { "<array>.$" : value }

示例:
students表的原始数据

  1. { "_id" : 1, grade: 80, mean: 75, "grades" : [ 80, 85, 90 ], std 1 }
1.更新数组中的元素

具体代码

  1. db.students.update(
  2. { _id: 1, grades: 80 },
  3. { $set: { "grades.$" : 82 } }
  4. )

结果

  1. { "_id" : 1, grade: 80, mean: 75, "grades" : [ 82, 85, 90 ], std 6 }
2.多条件更新

具体代码

  1. db.students.update(
  2. {
  3. _id: 4,
  4. grades: { $elemMatch: { grade: { $lte: 90 }, mean: { $gt: 70 } } }
  5. },
  6. { $set: { "grades.$.std" : 6 } }
  7. )

结果

  1. { "_id" : 1, grade: 80, mean: 75, "grades" : [ 80, 85, 90 ], std 6 }

小结:可以将$(update)运算符类比为js数组操作里的splice方法,只是不通过索引,而是通过具体的元素值比较。
用到的相关运算符: $set$elemMatch

$addToSet

简述:将元素添加进数组字段,且不重复。这也正是set这一数据结构的特性。
用法:

  1. { $addToSet: { <field1>: <value1>, ... } }

示例:
inventory表的原始数据

  1. { _id: 1, item: "polarizing_filter", tags: [ "electronics", "camera" ] }
1.添加一个元素

具体代码

  1. db.inventory.update(
  2. { _id: 1 },
  3. { $addToSet: { tags: "accessories" } }
  4. )

结果

  1. { _id: 1, item: "polarizing_filter", tags: [ "electronics", "camera", "accessories" ] }
2.添加多个元素

具体代码

  1. db.inventory.update(
  2. { _id: 1 },
  3. { $addToSet: { tags: { $each: [ "camera", "electronics", "accessories", "supplies" ] } } }
  4. )

结果

  1. { _id: 1, item: "polarizing_filter", tags: [ "electronics", "camera", "accessories", "supplies" ] }

小结:利用$addToSet操作数组,我们可以不用去检测重复,十分方便,结合$each,可以直接传入数组,类似于js数组的concat方法,区别在于结果是个元素唯一的数组。

$pop

简述:删除数组字段的第一个或最后一个元素。
用法:

  1. { $pop: { <field>: <-1 | 1>, ... } }

示例:
students表的原始数据

  1. { _id: 1, scores: [ 8, 9, 10 ] }
1.删除第一个元素

具体代码

  1. db.students.update( { _id: 1 }, { $pop: { scores: -1 } } )

结果

  1. { _id: 1, scores: [ 9, 10 ] }
2.删除最后一个元素

具体代码

  1. db.students.update( { _id: 1 }, { $pop: { scores: 1 } } )

结果

  1. { _id: 1, scores: [ 8, 9 ] }

小结:传入-1参数时,相当于js数组操作的shift方法;传入1参数时,相当于js数组操作的pop方法。

$pullAll

简述:删除匹配的列表值的所有数组字段元素。
用法:

  1. { $pullAll: { <field1>: [ <value1>, <value2> ... ], ... } }

示例:
survey表的原始数据

  1. { _id: 1, scores: [ 0, 2, 5, 5, 1, 0 ] }

具体代码

  1. db.survey.update( { _id: 1 }, { $pullAll: { scores: [ 0, 5 ] } } )

结果

  1. { "_id" : 1, "scores" : [ 2, 1 ] }

小结:与$pull不同的是,$pullAll只是通过匹配列表值来删除元素,也就是说可以传入数组参数,然后比较两个数组的交集来进行删除,而前者是通过指定一个查询条件。

$pull

简述:通过指定一个查询条件来删除所有符合条件的数组字段元素
用法:

  1. { $pull: { <field1>: <value|condition>, <field2>: <value|condition>, ... } }

示例:

通过指定值删除

stores表的原始数据

  1. {
  2. _id: 1,
  3. fruits: [ "apples", "pears", "oranges", "grapes", "bananas" ],
  4. vegetables: [ "carrots", "celery", "squash", "carrots" ]
  5. }
  6. {
  7. _id: 2,
  8. fruits: [ "plums", "kiwis", "oranges", "bananas", "apples" ],
  9. vegetables: [ "broccoli", "zucchini", "carrots", "onions" ]
  10. }

具体代码

  1. db.stores.update(
  2. { },
  3. { $pull: { fruits: { $in: [ "apples", "oranges" ] }, vegetables: "carrots" } },
  4. { multi: true }
  5. )

结果

  1. {
  2. "_id" : 1,
  3. "fruits" : [ "pears", "grapes", "bananas" ],
  4. "vegetables" : [ "celery", "squash" ]
  5. }
  6. {
  7. "_id" : 2,
  8. "fruits" : [ "plums", "kiwis", "bananas" ],
  9. "vegetables" : [ "broccoli", "zucchini", "onions" ]
  10. }
通过查询条件删除

原始数据

  1. { _id: 1, votes: [ 3, 5, 6, 7, 7, 8 ] }

具体代码

  1. db.profiles.update( { _id: 1 }, { $pull: { votes: { $gte: 6 } } } )

结果

  1. { _id: 1, votes: [ 3, 5 ] }
删除数组里的所有子项

survey表的原始数据

  1. {
  2. _id: 1,
  3. results: [
  4. { item: "A", score: 5 },
  5. { item: "B", score: 8, comment: "Strongly agree" }
  6. ]
  7. }
  8. {
  9. _id: 2,
  10. results: [
  11. { item: "C", score: 8, comment: "Strongly agree" },
  12. { item: "B", score: 4 }
  13. ]
  14. }

具体代码

  1. db.survey.update(
  2. { },
  3. { $pull: { results: { score: 8 , item: "B" } } },
  4. { multi: true }
  5. )

结果

  1. {
  2. "_id" : 1,
  3. "results" : [ { "item" : "A", "score" : 5 } ]
  4. }
  5. {
  6. "_id" : 2,
  7. "results" : [
  8. { "item" : "C", "score" : 8, "comment" : "Strongly agree" },
  9. { "item" : "B", "score" : 4 }
  10. ]
  11. }

这里需要注意的是在$pull操作中,会将查询条件应用到每个元素,所以不能使用$elemMatch,用了反而匹配不到正确的元素。
如:

  1. db.survey.update(
  2. { },
  3. { $pull: { results: { $elemMatch: { score: 8 , item: "B" } } } },
  4. { multi: true }
  5. )

这样去对原始数据的操作是不能达到预期结果的,结果数据将没有变化。

然而,当我们需要匹配的是数组里嵌套的数组元素,且需要多个条件,那就需要$elemMatch的帮忙了。
原始数据

  1. {
  2. _id: 1,
  3. results: [
  4. { item: "A", score: 5, answers: [ { q: 1, a: 4 }, { q: 2, a: 6 } ] },
  5. { item: "B", score: 8, answers: [ { q: 1, a: 8 }, { q: 2, a: 9 } ] }
  6. ]
  7. }
  8. {
  9. _id: 2,
  10. results: [
  11. { item: "C", score: 8, answers: [ { q: 1, a: 8 }, { q: 2, a: 7 } ] },
  12. { item: "B", score: 4, answers: [ { q: 1, a: 0 }, { q: 2, a: 8 } ] }
  13. ]
  14. }

具体代码

  1. db.survey.update(
  2. { },
  3. { $pull: { results: { answers: { $elemMatch: { q: 2, a: { $gte: 8 } } } } } },
  4. { multi: true }
  5. )

结果

  1. {
  2. "_id" : 1,
  3. "results" : [
  4. { "item" : "A", "score" : 5, "answers" : [ { "q" : 1, "a" : 4 }, { "q" : 2, "a" : 6 } ] }
  5. ]
  6. }
  7. {
  8. "_id" : 2,
  9. "results" : [
  10. { "item" : "C", "score" : 8, "answers" : [ { "q" : 1, "a" : 8 }, { "q" : 2, "a" : 7 } ] }
  11. ]
  12. }

小结:$pull可以通过指定查询条件来删除所有匹配的元素,如果需要像$pullAll的那样能传入数组进行匹配,那需要$in运算符的帮助。$pull的查询条件会应用到每个数组下的“顶级”元素,所以一般不需要使用$elemMatch,但如果需要匹配到数组里的嵌套数组元素,那就需要$elemMatch运算符的帮助了。

$pushAll

2.4版本后移除,可使用$push,$each替代。这里将不作介绍

$push

简述:添加数组元素。
用法:

  1. { $push: { <field1>: <value1>, ... } }

  1. { $push: { <field1>: { <modifier1>: <value1>, ... }, ... } }

示例:

1.添加一个元素

students表的原始数据

  1. {_id: 1, scores: [60, 80, 89]}

具体代码

  1. db.students.update(
  2. { _id: 1 },
  3. { $push: { scores: 89 } }
  4. )

结果

  1. {_id: 1, scores: [60, 80, 89, 89]}
2.添加多个元素

具体代码

  1. db.students.update(
  2. { name: "joe" },
  3. { $push: { scores: { $each: [ 90, 92, 85 ] } } }
  4. )

结果

  1. {_id: 1, scores: [60, 80, 89, 90, 92, 85]}
使用多个运算符

students表的原始数据

  1. {
  2. "_id" : 5,
  3. "quizzes" : [
  4. { "wk": 1, "score" : 10 },
  5. { "wk": 2, "score" : 8 },
  6. { "wk": 3, "score" : 5 },
  7. { "wk": 4, "score" : 6 }
  8. ]
  9. }

具体代码

  1. db.students.update(
  2. { _id: 5 },
  3. {
  4. $push: {
  5. quizzes: {
  6. $each: [ { wk: 5, score: 8 }, { wk: 6, score: 7 }, { wk: 7, score: 6 } ],
  7. $sort: { score: -1 },
  8. $slice: 3
  9. }
  10. }
  11. }
  12. )

结果

  1. {
  2. "_id" : 5,
  3. "quizzes" : [
  4. { "wk" : 1, "score" : 10 },
  5. { "wk" : 2, "score" : 8 },
  6. { "wk" : 5, "score" : 8 }
  7. ]
  8. }

小结:与$addToSet很类似,区别在于$push可能会使数组字段的元素重复,而$addToSet不会。与js的push效果一样。

$each

简述:与$push,$addToSet结合,实现向数组字段添加多个元素的效果,类似于js的concat
用法与示例请见上方$addToSet与$push。

$slice

简述:限制指定数值的数组字段元素。
用法:

  1. {
  2. $push: {
  3. <field>: {
  4. $each: [ <value1>, <value2>, ... ],
  5. $slice: <num>
  6. }
  7. }
  8. }

示例

1.从数组末尾开始截取

原始数据

  1. { "_id" : 1, "scores" : [ 40, 50, 60 ] }

具体代码

  1. db.students.update(
  2. { _id: 1 },
  3. {
  4. $push: {
  5. scores: {
  6. $each: [ 80, 78, 86 ],
  7. $slice: -5
  8. }
  9. }
  10. }
  11. )

结果

  1. { "_id" : 1, "scores" : [ 50, 60, 80, 78, 86 ] }
2.从数组头部开始截取

原始数据

  1. { "_id" : 2, "scores" : [ 89, 90 ] }

具体代码

  1. db.students.update(
  2. { _id: 2 },
  3. {
  4. $push: {
  5. scores: {
  6. $each: [ 100, 20 ],
  7. $slice: 3
  8. }
  9. }
  10. }
  11. )

结果

  1. { "_id" : 2, "scores" : [ 89, 90, 100 ] }
只使用$slice来更新数组

原始数据

  1. { "_id" : 3, "scores" : [ 89, 70, 100, 20 ] }

具体代码

  1. db.students.update(
  2. { _id: 3 },
  3. {
  4. $push: {
  5. scores: {
  6. $each: [ ],
  7. $slice: -3
  8. }
  9. }
  10. }
  11. )

结果

  1. { "_id" : 3, "scores" : [ 70, 100, 20 ] }
与$push的相关运算符一起使用

见$push运算符的讲解。

小结:$slice主要是与$push相结合,来从数组头部或末尾开始截取数组元素的。在后面版本(官方上称2.6开始)将不强制$slice与$each结合使用。

$sort

简述:对数组字段的元素进行排序。
用法:

  1. {
  2. $push: {
  3. <field>: {
  4. $each: [ <value1>, <value2>, ... ],
  5. $sort: <sort specification>
  6. }
  7. }
  8. }

示例:

1.利用数组元素对数组进行排序

原始数据

  1. {
  2. "_id": 1,
  3. "quizzes": [
  4. { "id" : 1, "score" : 6 },
  5. { "id" : 2, "score" : 9 }
  6. ]
  7. }

具体代码

  1. db.students.update(
  2. { _id: 1 },
  3. {
  4. $push: {
  5. quizzes: {
  6. $each: [ { id: 3, score: 8 }, { id: 4, score: 7 }, { id: 5, score: 6 } ],
  7. $sort: { score: 1 }
  8. }
  9. }
  10. }
  11. )

结果

  1. {
  2. "_id" : 1,
  3. "quizzes" : [
  4. { "id" : 1, "score" : 6 },
  5. { "id" : 5, "score" : 6 },
  6. { "id" : 4, "score" : 7 },
  7. { "id" : 3, "score" : 8 },
  8. { "id" : 2, "score" : 9 }
  9. ]
  10. }
2.直接对数组进行排序

原始数据

  1. { "_id" : 2, "tests" : [ 89, 70, 89, 50 ] }

具体代码

  1. db.students.update(
  2. { _id: 2 },
  3. { $push: { tests: { $each: [ 40, 60 ], $sort: 1 } } }
  4. )

结果

  1. { "_id" : 2, "tests" : [ 40, 50, 60, 70, 89, 89 ] }
3.只排序,不更新数组

原始数据

  1. { "_id" : 3, "tests" : [ 89, 70, 100, 20 ] }

具体代码

  1. db.students.update(
  2. { _id: 3 },
  3. { $push: { tests: { $each: [ ], $sort: -1 } } }
  4. )

结果

  1. { "_id" : 3, "tests" : [ 100, 89, 70, 20 ] }
4.与$push的相关运算符一起使用

见$push运算符的讲解。

小结:$sort为1时,表示按数值的从小到大排序;为-1时,则反过来。在后面版本(官方上称2.6开始)将不强制$slice与$each结合使用,这点与$slice相同。

$position

简述:与$push结合使用,表示元素添加的开始位置。
用法:

  1. {
  2. $push: {
  3. <field>: {
  4. $each: [ <value1>, <value2>, ... ],
  5. $position: <num>
  6. }
  7. }
  8. }

示例:

1.从数组的起始位置开始添加

原始数据

  1. { "_id" : 1, "scores" : [ 100 ] }

具体代码

  1. db.students.update(
  2. { _id: 1 },
  3. {
  4. $push: {
  5. scores: {
  6. $each: [ 50, 60, 70 ],
  7. $position: 0
  8. }
  9. }
  10. }
  11. )

结果

  1. { "_id" : 1, "scores" : [ 50, 60, 70, 100 ] }
2.把元素添加到原来的数组元素之间

原始数据

  1. { "_id" : 1, "scores" : [ 50, 60, 70, 100 ] }

具体代码

  1. db.students.update(
  2. { _id: 1 },
  3. {
  4. $push: {
  5. scores: {
  6. $each: [ 20, 30 ],
  7. $position: 2
  8. }
  9. }
  10. }
  11. )

结果

  1. { "_id" : 1, "scores" : [ 50, 60, 20, 30, 70, 100 ] }

小结:本来$push是默认从数组的末尾开始添加的元素的,但$position可以指定元素添加的开始位置。

总结

在mongodb中利用这些数组操作的运算符,将大大提高操作的效率和方便性,让你感觉就是在利用动态语言操作数组一样方便快捷。

参考

mongodb-v3.2操作手册
js数组操作

感谢您的阅读!
如果看完后有任何疑问,欢迎拍砖。
欢迎转载,转载请注明出处:http://www.yangrunwei.com/a/21.html
邮箱:glowrypauky@gmail.com
QQ: 892413924