MongoDB 數組

MongoDB是文檔型數據庫,每一個文檔(doc)表示數據的一項記錄。相比關係型DB的row只能使用簡單的數據類型,doc可以使用複雜的數據類型:內嵌doc,數組。MongoDB的數組是一系列元素的集合,使用中括號 [] 表示數組,例如:[1,2,3]的元素是整數值,[{name:"t5"}, {name:"t7"}],[ {name:"t5", age:21}, {name:"t7", age:22} ]的元素是doc。mongodb

在MongoDB中,數組元素容許重複,元素的位置是固定的。若是兩個數組相等,那麼這兩個數組的元素和及其位置都相同。數據庫

建立示例collection,使用db.collection.insert()函數和數組參數,一次性向集合中插入3個doc。數組

user1={ name:"t1", age:21}
user2={ name:"t2", age:22}
user3={ name:"t3", age:23}

db.users.insert([user1,user2,user3])

一,使用dot標記法(dot notation)訪問數組元素函數

MongoDB使用 dot 訪問數組的元素,或內嵌doc字段。spa

MongoDB uses the dot notation to access the elements of an array and to access the fields of an embedded document.3d

users=
{
name:"t1",
age:21,
address: {phone:123,email:"xxx@163.com"},
followers:[{name:"a"},{name:"b"},{name:"c"}]
}

address 字段是內嵌文檔,查找phone是123的doccode

db.users.find({"addr.phone":123})

followers 字段是數組,查詢followers中存在name=「b」的docblog

db.users.find({"followers.name":"b"})

二,修改字段的值排序

在MongoDB中,修改操做主要使用兩個修改器:$set 和 $inc,這兩個修改器具備upsert特性:若是doc中存在相應的字段,那麼修改該字段的值;若是doc中不存在相應的字段,那麼在doc中建立新的字段。$inc用於整數型字段,增長或減小字段的值,$set用於任意數據類型,替換字段的值。隊列

1,使用$set修改器,增長followers數組,使用empty filter:{},表示更新集合中的全部doc

db.users.updateMany(
{},
{$set:
   {
    followers:[ {name:"t5"},{name:"t7"} ]
   }
}
)

2,使用$inc修改器,增長age字段的值,每一個doc的age字段的都增長1

db.users.updateMany(
{},
{$inc:{age:1}}
)

3,$set 和 $inc的不一樣之處在於:$set是替換現有字段的值,而$inc是在現有字段值的基礎上,增長或減小指定的數值

示例,使用$set修改age的值,更新結束後,每一個doc的age字段都是1

db.users.updateMany(
{},
{$set:{age:1}}
)

三,修改數組

若是要向數組中增長或刪除一個元素,$set和$inc 都不能很好的知足這種需求,MongoDB有專用的 Array Operator,用於修改數組字段。

1,使用$push向doc中增長數組,或插入新的元素

$push:若是doc中存在相應的數組字段,那麼向數組的尾部插入一個元素;若是doc中不存在相應的數組字段,那麼向doc中建立一個數組字段,並初始化。

示例,第一次調用$push,因爲doc中不存在comments字段,所以MongoDB向doc中新建comments 數組字段,並初始化數組

db.users.updateMany(
{},
{$push:{comments:{msg:"c1"}}}
)

示例,後續再次調用$push,向已有的數組字段的尾部追加一個元素

db.users.updateMany(
{},
{$push:{comments:{msg:"c2"}}}
)

圖示,左邊是數組的第一個元素,右邊是數組的最後一個元素,使用$push每次向數組的尾部追加一個元素

2,向數組字段插入多個元素

$push 修改器每次只能向數組字段的尾部插入一個元素,搭配使用$each 修改器,每次能向數組字段中插入多個元素

db.users.updateMany(
{},
{$push:
   {
     comments:{ $each:[ {msg:"c3"}, {msg:"c4"} ] }
   }
}
)

圖示,使用$each,一次將多個元素插入到數組的尾部

3,從數組字段的特定位置處開始插入元素

使用$push 修改器只能將元素插入到數組字段的尾部,搭配使用$position 修改器,可以指定元素插入的開始位置,$position 必須和$each搭配使用。數組的下標是從0開始的。

db.users.updateMany
(
{},
{$push:
    {comments:
        {
           $each:[ { msg:"c5"}, {msg:"c6"} ],
           $position:2
        }
    }
}
)

圖示,使用$position 指定元素插入的開始位置,將c5,c6 依次插入都數組的2,3位置處,因爲數組的小標是從0開始的,c5,c6 是數組的第3,4個元素

若是不使用$position 修改器,那麼 $push 每次都向數組的末尾寫入元素;使用$position 修改器,指定$push插入元素的開始位置。

$position :The $position modifier specifies the location in the array at which the $push operator insert elements. Without the $position modifier, the $push operator inserts elements to the end of the array.

4,限制數組中元素的數量

在$push 元素時,使用$slice=MaxNum限制數組元素的最大數量。只要沒有達到最大數量,就會向數組中插入新的元素,直到達到最大值。$slice必須和$each搭配使用,若是數組字段的元素的數量已經達到最大值,根據MaxNum值的不一樣,會有不一樣的行爲:

  • 若是MaxNum=0,表示將數組清空;
  • 若是MaxNum是正整數,表示數組只保留前面的MaxNum個元素;
  • 若是MaxNum是負整數,表示數組只保留後面的MaxNum個元素;

示例,保留每一個comments的最後5個元素

db.users.updateMany(
{},
{$push:
   {comments:
      {
$each:[ {msg:
"c7"}, {msg:"c8"}, {msg:"c9"}], $slice:-5 } } } )

圖示,$slice:-5,MongoDB先將新的元素插入到數組中,將保留數組末尾的5個元素,將數組的其餘元素刪除

5,對數組字段的元素進行排序

在限制數組字段的元素數量以前,使用$sort 操做符對元素進行排序,是數組元素有序排列。在$sort操做以後使用 $slice:MaxNum 修改器,因爲數組元素是有序的,可以只保留序列前面或後面的特定數量的元素。

db.users.updateMany(
{},
{$push:
   {comments:
      {
        $each:[ {msg:"c7"}, {msg:"c8"}, {msg:"c9"}],
        $sort:{msg:-1},
        $slice:-5
      }
   }
}
)

圖示,$each 向現有的數組的尾部中插入三個元素:c7,c8,c9,$sort:{msg:-1} 對數組按照msg的降序排序,$slice:-5 操做符限制數組的元素數量,只保留數組尾部的5個元素。

若是數組是[1,2,3]這種類型,那麼$sort:1,按照1,2,3 升序排列; $sort:-1,安裝3,2,1 降序排列。

6,使用$addToSet向數組插入無重複的元素

經過$push 插入元素,有可能插入重複的元素,MongoDB容許數組中的元素重複;若是一個數組不能插入重複值,可使用$addToSet修改器, $addToSet在向數組插入元素時,首先檢查元素是否已經存在於數組中,若是不存在,那麼$addToSet將元素插入到數組中;若是存在,那麼$addToSet不會插入任何元素。$addToSet只會保證不會插入重複的元素,不該影響數組中已經存在的重複元素。

$addToSet 可以一次性向數組中插入多個元素。

$addToSet only ensures that there are no duplicate items added to the set and does not affect existing duplicate elements. $addToSet does not guarantee a particular ordering of elements in the modified set.

示例,向comments 數組中插入三個messge

db.users.updateMany(
{},
{$addToSet:
   {comments:[ {msg:"c7"}, {msg:"c8"}, {msg:"c9"}] }
}
)

7,使用$pop刪除數組的第一個或最後一個元素

把數組看做是隊列,下標爲0的元素是在隊列頭部,是數組的第一個元素,小標最大的元素是數組的最後一個元素。使用$pop刪除元素時,{$pop:{array:1}} 表示刪除數組的最後一個元素,{$pop:{array:-1}} 表示刪除數組的第一個元素。

db.users.updateMany(
{},
{$pop:{comments:1}}
)

圖示,刪除數組的最後一個元素

8,根據queyr filter刪除數組元素

db.users.updateMany(
{},
{$pull:{comments:{msg:"c7"}}}
)

圖示,刪除數組中msg字段是"c7"的全部元素

9,根據數組的下標修改元素,數組下標是從0開始的

對於js的數組 arr,包含兩個element,修改第一個元素的like 字段,將其值設置爲2.

var arr=[{name:"t1",like:1},{name:"t2",like:2}]
arr[0].like=2
print(tojoson(arr))

在MongoDB中,若是要修改doc中的數組,可使用 dot notation,使用 arrary.index.field 對數組中特定位置的元素進行修改。

db.users.updateMany(
{},
{$inc:{"comments.0.likes":1}}
)

圖示,向數組的第一個元素中增長likes字段,並初始化爲1

若是不知道數組元素的下標,MongoDB提供佔位符 $,用於表示從數組中查找知足query filter的第一個元素。佔位符 $ 須要對數組進行查找,查找的query filter必須顯式提供,若是存在數據元素知足query filter,那麼$ 佔位符表示第一個匹配的數組元素的position,若是沒有數組元素知足query filter,那麼MongoDB不會對數據任何做用。

$佔位符的使用格式,跟數組的元素類型有關:

  • 若是數組元素是doc,那麼使用 $ 佔位符的格式是:arrary.$.field
  • 若是數組的元素類型是原子類型,例如,[1,2,3]等,那麼使用那麼使用 $ 佔用符的格式是:arrary.$

$: Acts as a placeholder to update the first element that matches the query condition in an update.

示例1,使用empty filter做爲query filter

db.users.updateMany(
{},
{$inc:{"comments.$.unlikes":1}}
)

MongoDB拋出錯誤消息:

"errmsg" : "The positional operator did not find the match needed from the query. Unexpanded update: comments.$.unlikes"

說明 query filter 不能使用empty filter,必須顯式提供query filter。

示例2,對數組元素進行查詢,只要存在任何一個元素的msg字段的值c4,就在該元素中增長一個unlikes字段,並初始化爲1.

db.users.updateMany(
{comments:
    {$elemMatch:{msg:"c4"}}
},
{$inc:
    {"comments.$.unlikes":1}
}
)

圖示,$ 佔位符表示匹配query filter的第一個元素

四,數組的查詢

1,元素匹配符 $elemMatch,使用數組元素進行條件匹配

$elemMatch 是對數組元素的字段進行匹配,若是元素或元素的字段知足查詢條件,那麼返回該元素所在的doc。

格式是:{array:{$elemMatch:{field_query_filter,,,,}}}

The $elemMatch operator matches documents that contain an array field with at least one element that matches all the specified query criteria.

db.users.find({comments:{$elemMatch:{like:1}}})

示例1,數組元素是整數類型(原子類型)

{ _id: 1, results: [ 82, 85, 88 ] }
{ _id: 2, results: [ 75, 88, 89 ] }
db.scores.find(
   { results: { $elemMatch: { $gte: 80, $lt: 85 } } } )

查詢結果是:只有_id爲1的doc的數組元素82知足query filter

{ "_id" : 1, "results" : [ 82, 85, 88 ] }

示例2,數組元素是doc

{ _id: 1, results: [ { product: "abc", score: 10 }, { product: "xyz", score: 5 } ] }
{ _id: 2, results: [ { product: "abc", score: 8 }, { product: "xyz", score: 7 } ] }
{ _id: 3, results: [ { product: "abc", score: 7 }, { product: "xyz", score: 8 } ] }
db.survey.find(
   { results: { $elemMatch: { product: "xyz", score: { $gte: 8 } } } } )

查詢結果是:

{ "_id" : 3, "results" : [ { "product" : "abc", "score" : 7 }, { "product" : "xyz", "score" : 8 } ] }

2,數組的比較,使用數組進行條件匹配

使用數組進行比較時,只要數組中的任何元素知足query filter,就匹配成功。

若是有如下三個doc,每一個doc中都有一個grades 數組:

{ "_id" : 1, "grades" : [ 80, 85, 90 ] }
{ "_id" : 2, "grades" : [ 88, 90, 92 ] } { "_id" : 3, "grades" : [ 85, 100, 90 ] }

示例1,對於query filter:{grades:{$gt:85, $lt:100}},分析這3個數組是否知足:

  • 第1個數組:元素 90 知足大於 85,全部的元素都小於100
  • 第2個數組:全部的元素知足條件
  • 第3個數組:元素90,100 知足大於85的條件,元素85,90知足小於100的條件 

所以,只要數組中有任何一個元素知足qeury filter,就算知足qeury filter,這3個數組都知足query filter。
示例2,query filter:{grades:90}

只要數組中有一個元素的值是90,就知足query filter,所以,這3個數組都知足條件。

3,查詢數組元素的數量

$size操做符,若是doc中存在數組,而且數組的元素知足$size指定的條件,那麼返回該doc。

The $size operator matches any array with the number of elements specified by the argument.

db.collection.find( { array: { $size: n } } );

4,數組包含指定的多個元素

The $all operator selects the documents where the value of a field is an array that contains all the specified elements.

{ array : { $all: [ <value1> , <value2> ... ] } }

$all 表示集合的包含關係,全集包含子集的全部元素。若是數組A包含數組B,那麼A是B的全集,B是A的子集。子集中的全部元素,都存在於全集;全集中的元素,不必定存在於子集。

若是array包含指定的數組,那麼知足$all條件,返回doc,表示指定數組的元素都存在於array。

示例1,若是兩個數組相等,那麼這兩個數組的元素和及其位置都相同,即在數組的相同位置上,其元素相同。

這三個數組互不相同,arr1 和 arr2 元素數量相同,可是存在相同位置上(下標是:1,2)的元素不一樣;

arr1=[1,2,3] 
arr2=[1,3,2] arr3=[1,2]

示例2,包含關係(數組元素是int,字符串等原子類型)

$all表示的是包含關係,對於arr=[1,2,3],知足條件{arr:{$all:[2,1]}},arr存在元素1,2。

示例,查詢數組中同時存在2,3的doc

{ "_id" : 1, "g" : [ 1, 2, 3 ] }
{ "_id" : 2, "g" : [ 4, 2, 3 ] } { "_id" : 3, "g" : [ 4, 2, 5 ] }

db.foo.find({g:{$all:[2,3]}})

查詢結果是

{ "_id" : 1, "g" : [ 1, 2, 3 ] }
{ "_id" : 2, "g" : [ 4, 2, 3 ] }

示例3,包含關係(數組元素是doc)

示例,對於如下集合,每一個doc中都有一個數組字段qty,每一個數組中包含三個元素,每一個元素都是內嵌doc。

{_id:1,
   qty: [
          { size: "S", num: 10, color: "yellow" },
          { size: "M", num: 45, color: "blue" },
          { size: "L", num: 100, color: "green" }
        ]
}
{_id:2,
   qty: [
          { size: "S", num: 10, color: "blue" },
          { size: "M", num: 100, color: "red" },
          { size: "L", num: 100, color: "green" }
        ]
}

查詢數組元素中color字段同時存在blue 和 green的doc,這兩個doc都知足條件。

 db.foo.find({"qty.color":{$all:["blue","green"]}})

 

參考doc:

Update Operators

Array Update Operators

Query and Projection Operators

相關文章
相關標籤/搜索