zoukankan      html  css  js  c++  java
  • MongoDB中数组类型相关的操作

    概述

    在MongoDB的模式中,我们经常将一些数据存储到数组类型中,即我们常见的嵌套模式设计的一种实现方式。数组的这种设计实现方式在关系数据库中是没有或者说不常见的。所以,通过本文我们来梳理一下MongoDB的数组的相关操作。关于数组的操作可以分成两类,一类是数组操作符,另一个是数组运算修饰符。

     数组操作符

    操作符 实现功能
    $ 根据查询选择器定位要更新的文档
    $push 添加值到数组中
    $pushAll 添加数组到一个数组中。(将被$rach取代)
    $addToSet

    添加值到数组中,重复了也不处理

    $pop 从数组中删除第一个或者最后一个值。
    $pull 从数组中删除匹配查询条件的值。
    $pullAll 从数组中删除多个值。

    数组运算修饰符

    修饰符 实现功能
    $each 与$push和$addToSet一起使用来操作多个值。
    $slice 与$push和$each一起使用来缩小更新后数组的大小。
    $sort 与$push、$each、$slice一起来排序数组中的子文档。

    1.$push操作符

    1.1 语法及功能描述

    $push 主要用来向数组中添加元素。

    语法:

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

    默认情况下,它会在数组尾部添加一个单独的元素。

    1.2 操作案例

    假如我们有一个学生成绩的集合studentscore,其文档格式如下:

    { "_id" : 1, "name" : "xiaoming", "score" : [ { "math" : 99, "english" : 89 } ] }
    { "_id" : 2, "name" : "xiaohong", "score" : [ { "math" : 98, "english" : 96 } ] }

    其中的需求为,更新_id 为1的文档记录,在分数数组的字段上,添加 物理学的成绩,修改代码为

    db.studentscore.update({_id:1},{$push: {score:{"physics":100}}})

    修改后,结果查询如下:

    { "_id" : 1, "name" : "xiaoming", "score" : [ { "math" : 99, "english" : 89 }, { "physics" : 100 } ] }
    { "_id" : 2, "name" : "xiaohong", "score" : [ { "math" : 98, "english" : 96 } ] }

     1.3 结合$each修饰符,批量插入

    如果一次将多个值添加到数组中,可结合 数组修改符  $each 一起使用。

    例如,我们将小红的(_id =2)的物理成绩、化学成绩、生物成绩一起添加到文档中。执行的语句如下:

    db.studentscore.update({ _id: 2 },
        {
            $push: {
                score: {
                    $each: [{ "physics": 100 }, { "chemistry": 90 }, { "biology": 99 }]
                }
    
            }
        }
    )

    查询的结果如下:

    { "_id" : 1, "name" : "xiaoming", "score" : [ { "math" : 99, "english" : 89 }, { "physics" : 100 } ] }
    { "_id" : 2, "name" : "xiaohong", "score" : [ { "math" : 98, "english" : 96 }, { "physics" : 100 }, { "chemistry" : 90 }, { "biology" : 99 } ] }

    1.4 数组修饰符 $sort 和 $slice的使用

    前面讲了$each 数组运算修饰符,那我们再举一个例子,将剩余的两个修饰符一起讲解了好了($sort 和 $slice)

    例如,我们有文档记录如下:

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

    现在我们,有个需求,就是 首先向文档的quizzes数组字段,追加三个记录,然后,我们再按照score排序,选取数组中的前三个元素。

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

    更新后的结果显示如下:

    {
      "_id" : 5,
      "quizzes" : [
         { "wk" : 1, "score" : 10 },
         { "wk" : 2, "score" : 8 },
         { "wk" : 5, "score" : 8 }
      ]
    }

     $slice操作修饰符是在MongoDB 2.4 里添加的,其目的是方便管理经常更新的数组。当向数组添加值但是不想数组太大的时候,这个操作符非常有用。它必须与$push、$each操作符一起使用,允许用来剪短数组的大小、删除旧的值。

    与$slice操作修饰符很像,MongoDB 2.4 新增了$sort操作修饰符,帮助更新数组。当使用$push和$slice时,有时候要先排序再删除它们。

    2. $pop 操作符

    2.1 语法及功能描述

    $pop操作符可以实现从数组中删除第一个或者是最好一个元素。

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

    参数为-1 ,代表要删除数组中的第一个元素;参数为1 ,代表要删除数组中的最后一个元素。

    2.2 操作案例

    例如集合students 中有以下文档:

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

    我们的需求是要把数组中的第一个元素(成绩为8)移除,SQL 语句如下:

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

    更新后,文档如下

    { _id: 1, scores: [ 9, 10 ] }

    继续演示,如果在现有的基础上,我们需要进一步把数组的最后一个元素移除(成绩为10),更新的sQL如下:

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

    查询结果 如下:

    { _id: 1, scores: [ 9 ] }

    3. $pull操作符

    3.1 语法及功能描述

    $pull是$pop的复杂形式。使用$pull,可以通过值精确指定要删除的元素。

    语法格式

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

     3.2 操作案例

    3.2.1 移除数组中等于指定值的元素

    测试文档如下:

    {
       _id: 1,
       fruits: [ "apples", "pears", "oranges", "grapes", "bananas" ],
       vegetables: [ "carrots", "celery", "squash", "carrots" ]
    }
    {
       _id: 2,
       fruits: [ "plums", "kiwis", "oranges", "bananas", "apples" ],
       vegetables: [ "broccoli", "zucchini", "carrots", "onions" ]
    }

    操作要求是将 数组字段fruits中的"apples" and "oranges" 移除,还要将vegetables数组字段中的"carrots" 移除,其更新语句如下:

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

    更新后的结果如下:

    {
      "_id" : 1,
      "fruits" : [ "pears", "grapes", "bananas" ],
      "vegetables" : [ "celery", "squash" ]
    }
    {
      "_id" : 2,
      "fruits" : [ "plums", "kiwis", "bananas" ],
      "vegetables" : [ "broccoli", "zucchini", "onions" ]
    }

     此时,集合文档中,fruit的数组字段 没有apples也没有oranges,vegetables数组字段也没有了carrots。

    3.2.2 移除数组中满足指定条件的元素

    假如我们有一个 profiles 的集合,其文档格式如下:

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

    我们要把votes大于等于6的元素移除,其语句如下:

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

    更新后的结果如下:

    { _id: 1, votes: [  3,  5 ] }
    3.2.3 移除数组中内嵌子文档(即此时数组元素是子文档,每一个{}中的内容是一个数组元素)

    假设我们有一个关于 调查的集合 survey,其数据如下:

    {
       _id: 1,
       results: [
          { item: "A", score: 5 },
          { item: "B", score: 8, comment: "Strongly agree" }
       ]
    }
    {
       _id: 2,
       results: [
          { item: "C", score: 8, comment: "Strongly agree" },
          { item: "B", score: 4 }
       ]
    }

    需求是将 score 为 8 并且 item 为 "B"的元素移除

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

    更新后的文档如下:

    {
       "_id" : 1,
       "results" : [ { "item" : "A", "score" : 5 } ]
    }
    {
      "_id" : 2,
      "results" : [
          { "item" : "C", "score" : 8, "comment" : "Strongly agree" },
          { "item" : "B", "score" : 4 }
       ]
    }
    3.2.4 如果数组类型的元素还内嵌一个数组(数组包数组),就要特别小心了。

    此时就要用到 $elemMatch操作符。

    例如 文档格式如下:

    {
       _id: 1,
       results: [
          { item: "A", score: 5, answers: [ { q: 1, a: 4 }, { q: 2, a: 6 } ] },
          { item: "B", score: 8, answers: [ { q: 1, a: 8 }, { q: 2, a: 9 } ] }
       ]
    }
    {
       _id: 2,
       results: [
          { item: "C", score: 8, answers: [ { q: 1, a: 8 }, { q: 2, a: 7 } ] },
          { item: "B", score: 4, answers: [ { q: 1, a: 0 }, { q: 2, a: 8 } ] }
       ]
    }

    需要将 results数组字段 移除,移除的条件是 results数组字段中的answers字段,符合  q 为 2 and a 大于等于 8。

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

    更新后的数据如下:

    {
       "_id" : 1,
       "results" : [
          { "item" : "A", "score" : 5, "answers" : [ { "q" : 1, "a" : 4 }, { "q" : 2, "a" : 6 } ] }
       ]
    }
    {
       "_id" : 2,
       "results" : [
          { "item" : "C", "score" : 8, "answers" : [ { "q" : 1, "a" : 8 }, { "q" : 2, "a" : 7 } ] }
       ]
    }

    4.$addToSet

    4.1 语法及功能描述

    使用$addToSet也会往数组后面添加值,但是它比较特殊:它只会添加数组里不存在的值。

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

    4.2 操作案例

    假如有一个集合 inventory  格式如下

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

     我们希望向向字段 tags 数组 ,添加一个元素accessories,则更新语句如下:

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

    更新后的结果为 

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

    如果想批量的增加如果元素,我们可以结合 $each 操作符一起使用。

    例如以下文档

    { _id: 2, item: "cable", tags: [ "electronics", "supplies" ] }

    我们想在字段 tags 数组,添加元素 "camera", "electronics", "accessories",则更新语句如下:

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

    更新后的结果如下:

    {
      _id: 2,
      item: "cable",
      tags: [ "electronics", "supplies", "camera", "accessories" ]
    }

    4.3 注意点

    需要注意是,如果添加的元素是数组格式,则会将新添加的元素保留为数组(将会出现数组嵌套数组)

    例如

    { _id: 1, letters: ["a", "b"] }

    执行的语句如下:

    db.test.update(
       { _id: 1 },
       { $addToSet: {letters: [ "c", "d" ] } }
    )

    查询结构显示为

    { _id: 1, letters: [ "a", "b", [ "c", "d" ] ] }

    本文部分例子来自官网及网络,在此感谢。

    本文版权归作者所有,未经作者同意不得转载,谢谢配合!!!

  • 相关阅读:
    常用算法解析-动态规划
    转载-通过ApplicationContext 去获取所有的Bean
    什么是crud?
    new 关键字 和 newInstance() 方法的 区别
    Java反射简单使用--第一次细致阅读底层代码
    动态创建管理定时任务-已完成
    springboot mail整合freemark实现动态生成模板
    20190930开始记录每天学习状态,更新至20200125结束
    hibernate的对象/关系映射结果为空,exists查不到值的问题-20190823
    转载-Java中LinkedList的一些方法—addFirst addFirst getFirst geLast removeFirst removeLast
  • 原文地址:https://www.cnblogs.com/xuliuzai/p/10331524.html
Copyright © 2011-2022 走看看