mongodb Aggregation聚合操作之$unwind

在上一篇 mongodb Aggregation聚合操作之$project 中详细介绍了mongodb聚合操作中的$project使用以及参数细节。本篇将开始介绍Aggregation聚合操作中的unwind操作。

说明:

解析输入文档中的数组字段,为每个元素输出一个文档。每个输出文档都是输入文档,数组字段的值被元素替换。如果字段值为null、缺失或数组为空,则$unwind不会输出文档。


语法:

{ $unwind: <field path> }


可以将文档传递给$unwind以指定各种行为选项。

{

  $unwind:

    {

      path: <field path>,

      includeArrayIndex: <string>,

      preserveNullAndEmptyArrays: <boolean>

    }

}


参数解析:

path:string类型,数组字段的字段路径。若要指定字段路径,请在字段名称前加上美元符号$并将其括在引号中。

includeArrayIndex:string类型,可选的。用于保存元素的数组索引的新字段的名称。新字段名称不能以美元符号$开头。

preserveNullAndEmptyArrays:boolean类型,可选的。如果为真,如果路径为空、丢失或数组为空,则$unwind输出文档。如果为false,如果路径为空、丢失或数组为空,则$unwind不输出文档。默认值为false。

1. 示例

1.1. 简单示例


初始化数据:

db.inventory.insertOne({ "_id" : 1, "item" : "ABC1", sizes: [ "S", "M", "L"] })


示例:按照sizes字段拆分数据

db.inventory.aggregate( [ { $unwind : "$sizes" } ] )


结果:

{ "_id" : 1, "item" : "ABC1", "sizes" : "S" }

{ "_id" : 1, "item" : "ABC1", "sizes" : "M" }

{ "_id" : 1, "item" : "ABC1", "sizes" : "L" }

1.2. 拆分数组字段是null或者[]数据


初始化数据:

db.inventory2.insertMany([

  { "_id" : 1, "item" : "ABC", price: NumberDecimal("80"), "sizes": [ "S", "M", "L"] },

  { "_id" : 2, "item" : "EFG", price: NumberDecimal("120"), "sizes" : [ ] },

  { "_id" : 3, "item" : "IJK", price: NumberDecimal("160"), "sizes": "M" },

  { "_id" : 4, "item" : "LMN" , price: NumberDecimal("10") },

  { "_id" : 5, "item" : "XYZ", price: NumberDecimal("5.75"), "sizes" : null }

])


示例:

下面的$unwind操作是等效的,并为size字段中的每个元素返回一个文档。如果size字段没有解析为数组,但没有丢失、null或空数组,则$unwind将非数组操作数视为单个元素数组。

db.inventory2.aggregate( [ { $unwind: "$sizes" } ] )

db.inventory2.aggregate( [ { $unwind: { path: "$sizes" } } ] )


结果:

{ "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "S" }

{ "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "M" }

{ "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "L" }

{ "_id" : 3, "item" : "IJK", "price" : NumberDecimal("160"), "sizes" : "M" }


示例:下面的$unwind操作使用includeArrayIndex选项在输出中包含数组索引。

db.inventory2.aggregate( [

  {

    $unwind:

      {

        path: "$sizes",

        includeArrayIndex: "arrayIndex"

      }

   }])


结果:

{ "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "S", "arrayIndex" : NumberLong(0) }

{ "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "M", "arrayIndex" : NumberLong(1) }

{ "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "L", "arrayIndex" : NumberLong(2) }

{ "_id" : 3, "item" : "IJK", "price" : NumberDecimal("160"), "sizes" : "M", "arrayIndex" : null }


示例:下面的$unwind操作使用preserveNullAndEmptyArrays选项来包含size字段为null、缺失或空数组的文档。


db.inventory2.aggregate( [

   { $unwind: { path: "$sizes", preserveNullAndEmptyArrays: true } }

] )


{ "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "S" }

{ "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "M" }

{ "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "L" }

{ "_id" : 2, "item" : "EFG", "price" : NumberDecimal("120") }

{ "_id" : 3, "item" : "IJK", "price" : NumberDecimal("160"), "sizes" : "M" }

{ "_id" : 4, "item" : "LMN", "price" : NumberDecimal("10") }

{ "_id" : 5, "item" : "XYZ", "price" : NumberDecimal("5.75"), "sizes" : null }




1.3. 拆分数组字段并分组


初始化数据:

db.inventory2.insertMany([

  { "_id" : 1, "item" : "ABC", price: NumberDecimal("80"), "sizes": [ "S", "M", "L"] },

  { "_id" : 2, "item" : "EFG", price: NumberDecimal("120"), "sizes" : [ ] },

  { "_id" : 3, "item" : "IJK", price: NumberDecimal("160"), "sizes": "M" },

  { "_id" : 4, "item" : "LMN" , price: NumberDecimal("10") },

  { "_id" : 5, "item" : "XYZ", price: NumberDecimal("5.75"), "sizes" : null }

])


示例:下面的管道将展开大小数组,并将产生的文档按展开后的大小值进行分组:

db.inventory2.aggregate( [

   // First Stage

   {

     $unwind: { path: "$sizes", preserveNullAndEmptyArrays: true }

   },

   // Second Stage

   {

     $group:

       {

         _id: "$sizes",

         averagePrice: { $avg: "$price" }

       }

   },

   // Third Stage

   {

     $sort: { "averagePrice": -1 }

   }

] )


结果:

{ "_id" : "M", "averagePrice" : NumberDecimal("120") }

{ "_id" : "L", "averagePrice" : NumberDecimal("80") }

{ "_id" : "S", "averagePrice" : NumberDecimal("80") }

{ "_id" : null, "averagePrice" : NumberDecimal("45.25") }

1.4. 拆分嵌入式数组文档


初始化数据:

db.sales.insertMany([

  {

    _id: "1",

    "items" : [

     {

      "name" : "pens",

      "tags" : [ "writing", "office", "school", "stationary" ],

      "price" : NumberDecimal("12.00"),

      "quantity" : NumberInt("5")

     },

     {

      "name" : "envelopes",

      "tags" : [ "stationary", "office" ],

      "price" : NumberDecimal("1.95"),

      "quantity" : NumberInt("8")

     }

    ]

  },

  {

    _id: "2",

    "items" : [

     {

      "name" : "laptop",

      "tags" : [ "office", "electronics" ],

      "price" : NumberDecimal("800.00"),

      "quantity" : NumberInt("1")

     },

     {

      "name" : "notepad",

      "tags" : [ "stationary", "school" ],

      "price" : NumberDecimal("14.95"),

      "quantity" : NumberInt("3")

     }

    ]

  }

])


示例:下面的操作将按标签销售的商品分组,并计算每个标签的总销售额。

db.sales.aggregate([

  // First Stage

  { $unwind: "$items" },


  // Second Stage

  { $unwind: "$items.tags" },


  // Third Stage

  {

    $group:

      {

        _id: "$items.tags",

        totalSalesAmount:

          {

            $sum: { $multiply: [ "$items.price", "$items.quantity" ] }

          }

      }

  }

])


结果:

{ "_id" : "writing", "totalSalesAmount" : NumberDecimal("60.00") }

{ "_id" : "stationary", "totalSalesAmount" : NumberDecimal("264.45") }

{ "_id" : "electronics", "totalSalesAmount" : NumberDecimal("800.00") }

{ "_id" : "school", "totalSalesAmount" : NumberDecimal("104.85") }

    { "_id" : "office", "totalSalesAmount" : NumberDecimal("1019.60") }

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342

推荐阅读更多精彩内容