MongoDB聚合操作

管道的聚合

管道在Unix和Linux中一般用于将当前命令的输出结果作为下一个命令的参数。

MongoDB的聚合管道将MongoDB文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的。

表达式:处理输入文档并输出。表达式是无状态的,只能用于计算当前聚合管道的文档,不能处理其它的文档。

这里我们介绍一下聚合框架中常用的几个操作:

  • $project:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。
  • match:用于过滤数据,只输出符合条件的文档。match使用MongoDB的标准查询操作。
  • $limit:用来限制MongoDB聚合管道返回的文档数。
  • $skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。
  • $unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
  • $group:将集合中的文档分组,可用于统计结果。
  • $sort:将输入文档排序后输出。
  • $geoNear:输出接近某一地理位置的有序文档。

1、$project实例

db.mycol.aggregate({$project:{name : 1, score : 1}})

这样的话结果中就只还有_id,name和score三个字段了,默认情况下_id字段是被包含的,如果要想不包含_id话可以这样:

db.mycol.aggregate({$project:{_id : 0, name : 1, score : 1}})

2、$match实例

m a t c h 用于获取分数大于 30 小于并且小于 100 的记录,然后将符合条件的记录送到下一阶段 match用于获取分数大于30小于并且小于100的记录,然后将符合条件的记录送到下一阶段 match用于获取分数大于30小于并且小于100的记录,然后将符合条件的记录送到下一阶段group管道操作符进行处理

db.mycol.aggregate([{ KaTeX parse error: Expected '}', got 'EOF' at end of input: …atch :{score: { gt: 30, KaTeX parse error: Expected 'EOF', got '}' at position 8: lt: 100}̲}},{ group:{_id:'KaTeX parse error: Expected '}', got 'EOF' at end of input: sex',count:{ sum:1}}}])

Aggregation-聚合查询和mysql sql语句对应

Aggregation:

  • 参数说明:sql(Operators)

where ( m a t c h ) 、 g r o u p b y ( match) 、group by ( match)groupby(group) 、having( m a t c h ) 、 s e l e c t ( match)、select( match)select(project)、order by( s o r t ) 、 l i m i t ( sort)、limit( sort)limit(limit) sum( s u m ) 、 c o u n t ( sum)、count( sum)count(sum)、join($lookup)

SELECT cust_id, SUM(price) as total
FROM orders
WHERE status = 'A'
GROUP BY cust_id
HAVING total > 250

db.orders.aggregate([
    {$match: {status: 'A'}},
    {$group: {_id: "$cust_id",total: { $sum: "$price"}}},
    {$match: {total: { $gt: 250}}}
])

更加字段长度排序

db.collection.aggregate(
    [
        {$project: {
            "field": 1,
            "field_length": { $strLenCP: "$field" }
        }},
        {$sort: {"field_length": -1}},
        {$project: {"field_length": 0}},
    ]
)

聚合统计之$count表达式

普通查询:db.foo.find({name:{$ne:null}}).count()

$count 表达式等价于以下形式的 $sum 表达式:

{ $sum: 1 }

$count 示例

接下来我们将会使用以下集合进行演示:

db.sales.insertMany([
	{ "_id" : 1, "item" : "Americanos", "price" : 5, "size": "Short", "quantity" : 22, "date" : ISODate("2022-01-15T08:00:00Z") },
	{ "_id" : 2, "item" : "Cappuccino", "price" : 6, "size": "Short","quantity" : 12, "date" : ISODate("2022-01-16T09:00:00Z") },
	{ "_id" : 3, "item" : "Lattes", "price" : 15, "size": "Grande","quantity" : 25, "date" : ISODate("2022-01-16T09:05:00Z") },
	{ "_id" : 4, "item" : "Mochas", "price" : 25,"size": "Tall", "quantity" : 11, "date" : ISODate("2022-02-17T08:00:00Z") },
	{ "_id" : 5, "item" : "Americanos", "price" : 10, "size": "Grande","quantity" : 12, "date" : ISODate("2022-02-18T21:06:00Z") },
	{ "_id" : 6, "item" : "Cappuccino", "price" : 7, "size": "Tall","quantity" : 20, "date" : ISODate("2022-02-20T10:07:00Z") },
	{ "_id" : 7, "item" : "Lattes", "price" : 25,"size": "Tall", "quantity" : 30, "date" : ISODate("2022-02-21T10:08:00Z") },
	{ "_id" : 8, "item" : "Americanos", "price" : 10, "size": "Grande","quantity" : 21, "date" : ISODate("2022-02-22T14:09:00Z") },
	{ "_id" : 9, "item" : "Cappuccino", "price" : 10, "size": "Grande","quantity" : 17, "date" : ISODate("2022-02-23T14:09:00Z") },
	{ "_id" : 10, "item" : "Americanos", "price" : 8, "size": "Tall","quantity" : 15, "date" : ISODate("2022-02-25T14:09:00Z")}
]);

示例一:分组统计文档的数量

以下示例使用 $count 表达式计算不同种类咖啡的数量:

db.sales.aggregate([
  {
    $group: {
      _id: '$item',
      itemCount: { $count: {} },
    },
  },
])

返回结果如下:

[
  { _id: 'Mochas', itemCount: 1 },
  { _id: 'Americanos', itemCount: 4 },
  { _id: 'Lattes', itemCount: 2 },
  { _id: 'Cappuccino', itemCount: 3 }
]

其中,

  • _id: “$item” 用于将文档按照 item 字段进行分组,返回 4 个组;
  • $count: {} 用于统计每个分组内的文档数据,并将结果赋予 itemCount 字段。
示例二:统计与过滤

以下示例使用 $count 表达式计算不同种类咖啡的数量,并且返回数量大于 2 的结果:

db.sales.aggregate([
  {
    $group: {
      _id: '$item',
      itemCount: { $count: {} },
    },
  },
  {
    $match: { itemCount: { $gt: 2 } },
  },
]);

返回结果如下:

[
  { _id: 'Americanos', itemCount: 4 },
  { _id: 'Cappuccino', itemCount: 3 }
]

MongoDB 聚合操作- < / f o n t > a n d </font>and </font>andor

Booking.aggregate([
  {
    $match:
    {
    $and: [
      {
    $or: [
          {
    isDoubleRoom },
          {
    chosenRoom }
        ]},
      {
    month },
      {
    year },
    ] }},
  {
    $group: {
    _id: "$fullDate", count: {
    $sum: 1 } } }
]

MongoDB 聚合操作- $lookup

数据准备

db.orders.insert([
    {
    "_id" : 1, "item" : "almonds", "price" : 12, "quantity" : 2 },
    {
    "_id" : 2, "item" : "pecans", "price" : 20, "quantity" : 1 },
    {
    "_id" : 3  }
])

db.inventory.insert([
    {
    "_id" : 1, "sku" : "almonds", "description": "product 1", "instock" : 120 },
    {
    "_id" : 2, "sku" : "bread", "description": "product 2", "instock" : 80 },
    {
    "_id" : 3, "sku" : "cashews", "description": "product 3", "instock" : 60 },
    {
    "_id" : 4, "sku" : "pecans", "description": "product 4", "instock" : 70 },
    {
    "_id" : 5, "sku": null, "description": "Incomplete" },
    {
    "_id" : 6 }
])

查询

'''
SELECT *, inventory_docs
FROM orders
WHERE inventory_docs IN (SELECT *
FROM inventory
WHERE sku= orders.item);
'''

db.orders.aggregate([
   {
   
     $lookup:
       {
   
         from: "inventory",
         localField: "item",
         foreignField: "sku",
         as: "inventory_docs"
       }
  }
])

db.getCollection('A').aggregate([
        {
   
        $lookup:{
   
                    from:'B',
                    localField:'userid',
                    foreignField:'userid',
                    as:'userinfo'
                }
        }, 
        {
   
         $unwind:'$userrole'//把一个数组展成多个,就比如说按多表连查的userrole数组中有10数据,那么用$unwind将把一条带数组的数据分成10条,这10条数据除了userrole不同之外,其它数据都是相同的,就类似于一个展开操作
        },
        {
   
         $match:{
   'username':'zhangsan'}
        },
        {
   
          $group:{
   
                    _id:{
   
                            userid:'$userid',//这个属性必须是要A表中有的
                            userrole:'$userrole.roleid',//A表中有一个集合,里面存放的对象有一个名为roleid的属性
                        },
                    operateTime:{
   
                            $last:'$operateTime'//取A表操作时间最后一条件数
                        }
                    info:{
   
                            $first:'$userinfo'//因为数组的扩展,造成了大量的重复数据(只有userrole不同),$first是只取最新的一条
                        }
                }
        }{
   
            $sort:{
   'operateTime':-1}//操作时间倒序,-1:倒序,1:升序
        },
        {
   
            $skip:0//跳过几条数据,也就是从第几条数据开始取
        }{
   
            $limit:5//每页显示几条数据
        }
]);

参考案例

1. 主表

主表id为ObjectId类型

db.getCollection('note').find();

查询结果:

{
    "_id" : ObjectId("5f9faba46b299d1336f9d316"),
    "noteCode" : "20201102144804000001",
    "userId" : 93,
    "title" : "标题",
    "content" : "内容"
},
{
    "_id" : ObjectId("5f9fabb06b299d1336f9d31c"),
    "noteCode" : "20201102144816000001",
    "userId" : 93,
    "title" : "标题",
    "content" : "内容"
}

2. 子表

外键noteId为String类型

/* 1 */
{
    "_id" : ObjectId("5f9faba46b299d1336f9d317"),
    "noteId" : "5f9faba46b299d1336f9d316",
    "imgId" : 316,
    "imgUrl" : "https://xxx/selection1577778815396.png",
    "createTime" : ISODate("2020-11-02T14:48:04.356+08:00")
}

/* 2 */
{
    "_id" : ObjectId("5f9faba46b299d1336f9d318"),
    "noteId" : "5f9faba46b299d1336f9d316",
    "imgId" : 3165,
    "imgUrl" : "https://xxx/selection157777881521.png",
    "createTime" : ISODate("2020-11-02T14:48:04.356+08:00")
}

3. 关联查询,将关联ID类型转换为一致(objectId to string)
db.getCollection("note").aggregate(
    [{
        "$project": 
        {
            "id": 
            {
                "$convert": {
                    "input": "$_id",
                    "to": "string"
                }
            },
            "noteCode": 1
        }
    }, {
        "$lookup": 
        {
            "from": "noteImage",
            "localField": "id",
            "foreignField": "noteId",
            "as": "image_docs"
        }
    }]
);

输出结果:

{
    "_id" : ObjectId("5f9faba46b299d1336f9d316"),
    "noteCode" : "20201102144804000001",
    "id" : "5f9faba46b299d1336f9d316",
    "image_docs" : [ 
        {
            "_id" : ObjectId("5f9faba46b299d1336f9d317"),
            "noteId" : "5f9faba46b299d1336f9d316",
            "imgId" : 316,
            "imgUrl" : "https://xxx/selection1577778815396.png",
            "createTime" : ISODate("2020-11-02T14:48:04.356+08:00")
        }, 
        {
            "_id" : ObjectId("5f9faba46b299d1336f9d318"),
            "noteId" : "5f9faba46b299d1336f9d316",
            "imgId" : 3165,
            "imgUrl" : "https://xxx/selection1577778815396.png",
            "createTime" : ISODate("2020-11-02T14:48:04.356+08:00")
        }
    ]
}

4. 关联查询,将关联ID类型转换为一致(string to objectId)
db.getCollection("noteImage").aggregate(
    [{
        "$project": 
        {
            "nid": 
            {
                "$convert": {
                    "input": "$noteId",
                    "to": "objectId"
                }
            },
            "imgId": 1
        }
    }, {
        "$lookup": 
        {
            "from": "note",
            "localField": "nid",
            "foreignField": "_id",
            "as": "noteDocs"
        }
    }]
);

输出结果:

// 1
{
    "_id": ObjectId("5fa9eab6e7e2af281425d0c9"),
    "imgId": 2686,
    "nid": ObjectId("5fa9eab6e7e2af281425d0c8"),
    "noteDocs": [
        {
            "_id": ObjectId("5fa9eab6e7e2af281425d0c8"),
            "noteCode": "9223372036854775807",
            "userId": NumberInt("99"),
            "title": "联调专用",
            "content": "联调数据"           
        }
    ]
}

// 2
{
    "_id": ObjectId("5fa9ee7ae7e2af281425d10a"),
    "imgId": 2872,
    "nid": ObjectId("5fa9ee7ae7e2af281425d109"),
    "noteDocs": [
        {
            "_id": ObjectId("5fa9ee7ae7e2af281425d109"),
            "noteCode": "9223372036854775807",
            "userId": NumberInt("90"),
            "title": "吃饭",
            "content": "吃饭"
        }
    ]
}

两表关联,每个表都有条件

db.Rel_QQDetails.aggregate([
   { $match: {
   				ReconciliationId:CSUUID("bb54bee7-187f-4d38-85d7-88926000ac7a")
   			}
   	},
    { $lookup:
       {
         from: "Fct_QQStatements",
         localField: "OrderId",
         foreignField: "OrderStatementsId",
         as: "inventory_docs"
       }
  	},
    { $match : {
    			"inventory_docs.StatementsPriceException" :false
    			} 
    }
])

MongoDB 聚合操作- $group

https://docs.mongodb.com/manual/reference/operator/aggregation/group/

语法示例:

{
   
  $group:
    {
   
      _id: <expression>, // Group By Expression
      <field1>: {
    <accumulator1> : <expression1> },
      ...
    }
 }
db.sales.insertMany([
  {
    "_id" : 1, "item" : "abc", "price" : NumberDecimal("10"), "quantity" : NumberInt
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值