【mongoDB查詢進階】聚合管道(二) -- 階段操做符

上篇最後說到管道操做符,本篇文章將詳細說一下管道操做符。mongodb

mongoDB查詢進階--聚合管道(一)回顧數據庫

什麼是管道操做符(Aggregation Pipeline Operators)

mongoDB有4類操做符用於文檔的操做,例如find查詢裏面會用到的$gte,$in等。操做符以$開頭,分爲查詢操做符,更新操做符,管道操做符,查詢修飾符4大類。其中管道操做符是用於聚合管道中的操做符。express

管道操做符的分類

管道操做符能夠分爲三類:json

  1. 階段操做符(Stage Operators)
  2. 表達式操做符(Expression Operators)
  3. 累加器(Accumulators)

此處中文勉強翻譯,以英文爲準,歡迎大神給意見,謝謝。segmentfault

參考MongoDB官網:https://docs.mongodb.com/manual/reference/operator/aggregation/#accumulators數組

階段操做符(Stage Operators)

階段操做符是使用於db.collection.aggregate方法裏面,數組參數中的第一層。函數

db.collection.aggregate( [ { 階段操做符:表述 }, { 階段操做符:表述 }, ... ] )
表達式操做符(Expression Operators)

表達式操做符主要用於在管道中構建表達式時使用,使用相似於函數那樣須要參數,主要用於$project操做符中,用於構建表達式,使用方法通常以下:this

方法1:翻譯

{ <operator>: [ <argument1>, <argument2> ... ] }

方法2:code

{ <operator>: <argument> }
累加器(Accumulators)

累加器原本只能使用與$groud下,可是版本3.2或以上,部分累加器還能使用於$project。當在$group中使用時,累加器是針對每一個分組使用的;當在$project中使用時,累加器則是針對每一個字面量起做用,具體用法下一篇文章闡述。

因爲操做符比較多,本篇文章先說第一類階段操做符,後面兩類在下一篇再說。

經常使用階段操做符

操做符 簡述
$match 匹配操做符,用於對文檔集合進行篩選
$project 投射操做符,用於重構每個文檔的字段,能夠提取字段,重命名字段,甚至能夠對原有字段進行操做後新增字段
$sort 排序操做符,用於根據一個或多個字段對文檔進行排序
$limit 限制操做符,用於限制返回文檔的數量
$skip 跳過操做符,用於跳過指定數量的文檔
$count 統計操做符,用於統計文檔的數量
$group 分組操做符,用於對文檔集合進行分組
$unwind 拆分操做符,用於將數組中的每個值拆分爲單獨的文檔
$lookup 鏈接操做符,用於鏈接同一個數據庫中另外一個集合,並獲取指定的文檔,相似於populate

更多操做符介紹詳見官網:https://docs.mongodb.com/manual/reference/operator/aggregation/

階段操做符詳解

假設有一個保存用戶的集合Users,一個文章的集合Articles,數據大體以下:
users:

[
    { name: 'John', age: 16, sex: male, city: guangzhou, _id: 1, ...},
    { name: 'Rose', age: 18, sex: female, city: beijing, _id: 2, ...},
    { name: 'Jack', age: 29, sex: male, city: guangzhou, _id: 3, ...},
    { name: 'Allen', age: 18, sex: female, city: beijing, _id: 4, ...},
    { name: 'Cruz', age: 22, sex: male, city: guangzhou, _id: 5, ...},
    { name: 'Peter', age: 18, sex: male, city: guangzhou, _id: 6, ...},
    { name: 'Kelly', age: 23, sex: female, city: shanghai, _id: 7, ...},
    ...
]

articles:

[
    { title: 'this is article A', author: 'John', _id: 1, ... },
    { title: 'this is article B', author: 'Jack', _id: 2, ... },
    { title: 'this is article C', author: 'Rose', _id: 3, ... },
    { title: 'this is article D', author: 'John', _id: 4, ... },
    { title: 'this is article E', author: 'John', _id: 5, ... },
    ...
]

$match 匹配操做符

說明:

用於重構每個文檔的字段,能夠提取字段,重命名字段,甚至能夠對原有字段進行操做後新增字段

用法:
{ $match: { <query> } }
示例:
  • 查詢用戶年齡是18歲的用戶
db.users.aggregate([{ $match : { age : "18" } }]);

$project 投射操做符

說明:

用於對文檔集合進行篩選

用法:
{ $project: { <specification(s)> } }

specification的規則

規則 描述
<字段名>: 1 or true 選擇須要返回什麼字段
_id: 0 or false 不返回_id(默認返回)
<字段名>: 表達式 使用表達式,能夠用於重命名字段,或對其值進行操做,或新增字段
<字段名>: 0 or false 選擇須要不返回什麼字段,注意:當使用這種用法時,就不要用上面的方法
示例1:
  • 用戶集合投射用戶姓名
  • 不返回_id
db.users.aggregate([{ $project : { name: 1 } }]);
示例2:
  • 將_id重命名爲userId
  • 不返回_id_
db.users.aggregate([{ $project : { ueserId: '$_id', _id: 0 } }]);
示例3:
  • 返回新字段username,並使用表達式讓它的值爲name的大寫。
db.users.aggregate([ 
    { 
        $project : {
            name: 1, 
            username: { $toUpper: '$name' }, 
            _id: 0 
        } 
    } 
]);

關於管道表達式:最簡單的「$project」表達式是包含和排除字段(如: { name: 1 }),以及字段名稱$fieldname(如: { userId: '$_id' })。除此之外,還可使用表達式操做符(如: $toUpper)構成更豐富的表達式,將多個字面量和變量組合在一塊兒使用,獲得更多有意思的值,更多表達式操做符的說明及使用在另外的篇章中詳細闡述。

$sort 排序操做符

說明:

用於根據一個或多個字段對文檔進行排序

用法:
{ $sort: { <field1>: <sort order>, <field2>: <sort order> ... } }
示例:
  • users集合按照年齡age從低到高排序
db.users.aggregate([{ $sort : { age: 1 } }]);

$limit 限制操做符

說明:

用於限制返回文檔的數量

用法:
{ $limit: <positive integer> }
示例:
  • 返回5篇article
db.articles.aggregate({ $limit : 3 });

$skip 跳過操做符

說明:

用於跳過指定數量的文檔

用法:
{ $skip: <positive integer> }
示例:
  • 跳過1個文檔
db.users.aggregate([{ $skip : 1 }]);

$count 統計操做符

說明:

用於統計文檔的數量

用法:
{ $count: <string> }

string是統計以後輸出統計結果的字段名

示例:
  • 統計文章的總數,以totalArticle返回
db.articles.aggregate([{ totalArticle : 1 }]);

$group 分組操做符

說明:

用於對文檔集合進行分組

用法:
{ $group: { _id: <expression>, <field1>: { <accumulator1> : <expression1> }, ... } }

_id是必須的,用做分組的依據條件

示例:
  • 將用戶(users)按性別(sex)分組
db.users.aggregate([{ $group : { _id: '$sex' } }]);

返回結果:

[
  { _id: 'male' },
  { _id: 'female' }
]
進階示例:
  • 將用戶(users)按性別(sex)分組
  • 分組後使用計算各自性別的平均年齡
  • 統計不一樣的性別的人數,並以count返回
db.users.aggregate([
    { 
        $group : {
            _id: '$sex', 
            avgAge: { $avg: '$age' }, 
            conut: { $sum: 1 } 
        } 
    }
]);

返回結果:

[
  { _id: 'male', avgAge: <男性平均年齡>, count: <男性人數> },
  { _id: 'female', avgAge: <女性平均年齡>, count: <女性人數> }
]

此處用到的表達式 { $avg: '$age' } 用於求平均年齡,$avg是求均值的操做符,$sum用於彙總, 都只能在$group中使用的累加器,mongoDB3.2以上版本則還能夠在$project中使用,詳細會在另外的篇章中闡述。

$unwind 拆分操做符

說明:

用於將數組中的每個值拆分爲單獨的文檔

用法:
{ $unwind: <field path> }
3.2+版本的用法:

增長icludeArrayIndex,preserveNullAndEmptyArrays兩個可選配置

{
  $unwind:
    {
      path: <field path>,
      includeArrayIndex: <string>,
      preserveNullAndEmptyArrays: <boolean>
    }
}
字段 類型 描述
path string 必填,數組的字段名,指定須要拆分的字段
includeArrayIndex string 可選,定義返回的字段名,返回的值是拆分前值在原數組的位置
preserveNullAndEmptyArrays boolean 可選,配置在path的值爲空或缺失的狀況下是否拆分, 默認false
示例:

假設articles文檔集合是這樣:

{ title: 'this is article A', author: 'John', _id: 1, comments: ['a', 'b', 'c']}
db.articles.aggregate([{ $unwind: '$comments' }]);

結果:

[
    { title: 'this is article A', author: 'John', _id: 1, comments: 'a'},
    { title: 'this is article A', author: 'John', _id: 1, comments: 'b'},
    { title: 'this is article A', author: 'John', _id: 1, comments: 'c'},
]
進階示例(v3.2+):

假設articles文檔集合是這樣:

[
    { title: 'this is article A', author: 'John', _id: 1, comments: ['a', 'b', 'c'] }
    { title: 'this is article B', author: 'Jack', _id: 2 },
    { title: 'this is article C', author: 'Amy', _id: 3, comments: [] },
    { title: 'this is article D', author: 'Lam', _id: 4, comments: null },
]

操做:

db.articles.aggregate([
    { 
        $unwind: {
            path: '$comments',
            includeArrayIndex: 'arrayIndex',
        }
    }
]);

結果:

[
    { title: 'this is article A', author: 'John', _id: 1, comments: 'a', arrayIndex: NumberLong(0) },
    { title: 'this is article A', author: 'John', _id: 1, comments: 'b', arrayIndex: NumberLong(1) },
    { title: 'this is article A', author: 'John', _id: 1, comments: 'c', arrayIndex: NumberLong(2) },
]

操做:

db.articles.aggregate([
    { 
        $unwind: {
            path: '$comments',
            preserveNullAndEmptyArrays: true,
        }
    }
]);

結果:

[
    { title: 'this is article A', author: 'John', _id: 1, comments: 'a' },
    { title: 'this is article A', author: 'John', _id: 1, comments: 'b' },
    { title: 'this is article A', author: 'John', _id: 1, comments: 'c' },
    { title: 'this is article B', author: 'Jack', _id: 2 },
    { title: 'this is article C', author: 'Amy', _id: 3 },
    { title: 'this is article C', author: 'Amy', _id: 3, comments: null }
]

$lookup 鏈接操做符

說明:

用於鏈接同一個數據庫中另外一個集合,並獲取指定的文檔,相似於populate

用法:
{
   $lookup:
     {
       from: <collection to join>,
       localField: <field from the input documents>,
       foreignField: <field from the documents of the "from" collection>,
       as: <output array field>
     }
}
字段 描述
from 須要關聯的集合名
localField 本集合中須要查找的字段
foreignField 另一個集合中須要關聯的字段
as 輸出的字段名
示例:
  • ariticles中的author關聯到user表
  • authoer字段返回詳細的用戶的信息
db.articles.aggregate([
  {
    $lookup:
      {
        from: "users",
        localField: "author",
        foreignField: "name",
        as: "author"
      }
  }
])

結果:

[
    { 
        title: 'this is article A', 
        author: { 
            name: 'John',
            age: 16,
            sex: male,
            city: guangzhou,
            _id: 1, 
            ...
        }, 
        _id: 1, 
        ... 
    },
    { 
        title: 'this is article B', 
        author: { 
            name: 'Jack',
            age: 29,
            sex: male,
            city: guangzhou,
            _id: 3, 
            ...
        }, 
        _id: 2, 
        ... 
    },
    { 
        title: 'this is article C', 
        author: { 
            name: 'Rose',
            age: 18,
            sex: male,
            city: beijing,
            _id: 2, 
            ...
        }, 
        _id: 3, 
        ... 
    },
    { 
        title: 'this is article D', 
        author: { 
            name: 'John',
            age: 16,
            sex: male,
            city: guangzhou,
            _id: 1, 
            ...
        },
        _id: 4, 
        ... 
    },
    { 
        title: 'this is article E', 
        author: { 
            name: 'John',
            age: 16,
            sex: male,
            city: guangzhou,
            _id: 1,
            ...
        },
        _id: 5, 
        ... 
    },
    ...
]

綜合示例

需求

找出發表文章最多的5位做者,按發表文章排序,顯示他的發表文章的總次數,和他本身的信息

  • 文章按照做者分組,統計次數
  • 按照次數從高到低排序
  • 截取頭5名
  • 關聯用戶信息
  • 不輸出文章_id
操做
db.articles.aggregate([
  {
    $group:
      {
        _id: "$author",
        count: { $sum: 1 },
      }
  }, 
  {
        $sort: { count: -1 }
  },
  {
      $skip: 5
  },
  {
      $lookup:
        {
          from: "users",
          localField: "author",
          foreignField: "name",
          as: "author"
        }
  },
  {
      $project: {
          _id: 0,
      }
  }
])

總結

本文介紹了幾個使用聚合管道查詢時經常使用的管道操做符的用法,熟練地綜合使用以上操做符能夠對數據進行多樣的處理,組合,統計,得出多樣化的數據。另外再加以配合表達式操做符(Expression Operators)組成的表達式, 或者在$project或$group中使用累加器(Accumulators)能查詢統計的內容會更加的多樣化。

感謝閱讀~

相關文章
相關標籤/搜索