当前位置: 首页 > news >正文

MongoDB复杂查询 聚合框架

前言

前面已经对MongoDB基础的增删改查进行了分享,在增删改方面基本已经够用了,但是查询方面是远远不够的,find方法只能传入一些查询条件。涉及到稍微复杂点的查询就无法实现了,如分组、排序、映射(起别名)等这些操作的时候就没有办法实现。

所以我接下来给同志们分享专一用于MongoDB复杂查询的框架   -----    聚合框架

注意: 文档和JSON对象基本上是一个意思,我会在解释的时候说是JSON对象,便于理解,在写标题的写的是文档,文档是正式的概念

如果对基础的查询条件不熟悉的同志,可以查看一下我前面写的文章

MongoDB基础增删改查命令-CSDN博客

目录

聚合框架介绍

框架入口

聚合管道

常见的操作符

 常用操作符的操作参数介绍

条件过滤 $match

操作参数格式

查询操作符

逻辑操作符

案例解析

字段选择 $project

操作参数格式

案例解析

分组统计 $group

操作参数格式

常见的聚合函数

 案例解析

排序 $sort

操作参数格式

案例解析

限制结果数 $limit和跳过文档$skip

操作参数格式

案例解析

左连接 $lookup和拆分数组字段为多个文档$unwind

操作参数格式

案例解析


聚合框架介绍

MongoDB的聚合框架是一个基于管道概念的强大工具,它允许用户对集合中的文档进行一系列复杂的数据转换、过滤、分组、排序和计算操作,最终生成所需的结果。

框架入口

db.集合.aggregate(聚合管道)

聚合管道

下面我介绍一下aggregate方法的参数,聚合管道

这个参数是一个数组,数组里面记录的是每个聚合阶段,每个聚合阶段把文档进行过滤之后,把结果交给下一个聚合阶段,这就是聚合管道的执行流程

具体的聚合管道的格式

[{操作符1: 操作参数1},{操作符2,操作参数2},{操作符3,操作参数3}...]

常见的操作符

MongoDB的操作符和SQL中的关键字对比,快速入门,类比使用

功能MongoDB 操作符SQL 关键字/语法
条件过滤$matchWHERE
字段选择$projectSELECT 字段列表
分组统计$groupGROUP BY + 聚合函数
排序$sortORDER BY
限制结果数$limitLIMIT
跳过文档$skipOFFSET
左外连接$lookupLEFT JOIN
将数组字段拆分为多条文档$unwind配合MongoDB左连接后匹配多个右表数据,此时的匹配到的右表数据是以数组的形式添加到左表中的,$unwinds是为了展开这些右表数据的,是MongoDB特有的

 常用操作符的操作参数介绍

条件过滤 $match

条件过滤 $match 类比 SQL中的where关键字

操作参数格式

$match操作符的操作参数格式

{查询条件}

查询条件本质就是逻辑操作符和查询操作符的嵌套

查询操作符和逻辑操作符常用列表如下

查询操作符

操作符说明示例
$eq等于age: { $eq: 25 }
$ne不等于age: { $ne: 25 }
$gt大于age: { $gt: 25 }
$lt小于age: { $lt: 25 }
$in包含在数组中name: { $in: ["Alice", "Bob"] }
$regex正则匹配name: { $regex: /^A/ }

逻辑操作符

操作符说明示例
$and

显式写法                  $and: [{查询操作符1}, {查询操作符2}]

(推荐)隐式写法       查询操作符1,查询操作符2

$or$or: [{查询操作符1}, {查询操作符2}]
$not单个取反$not: {查询操作符}
$nor多个取反$nor: [{查询操作符1}, {查询操作符2}]

如果and和or都出现的情况下,推荐使用隐式and和显式or搭配使用,清晰明了  

案例解析

#案例: 查询status状态是completed 并且 total_price大于500# MongoDB的语句
{ $match: { status: { $eq: "completed" }, total_price: { $gt: 500 } } }#对应的SQL语句
WHERE status = 'completed' AND total_price > 500

字段选择 $project

字段选择 $project 类比 select的字段列表

操作参数格式

$project操作符的操作参数格式

# 大概的格式如下
{key1: value2, key1: value2...}# key: value 键值对的写法有三种,保留字段和排除字段不能同时使用
# _id字段是默认保留的,所以_id字段是可以在保留字段语句中显示排除的
# 第一种 保留字段,1代表保留字段
字段名: 1# 第二种 排除字段,0代表排除字段
字段名: 0# 第三种 保留字段并对字段重命名
字段新名字: "$原字段名"

案例解析

# 案例: 在查询一张表的时候,保留order_id、cust_id、uname字段,并且把uname重命名为username
# MongoDB的语句
{$project: {_id: 0, order_id: 1, cust_id: 1, username: "$uname"}}# 对应的SQL语句
SELECT order_id, cust_id, uname as username

分组统计 $group

分组统计 $group 类比 GROUP BY + 聚合函数

操作参数格式

$group操作符的操作参数格式

{$group: {_id: {$分组字段1,$分组字段2,$分组字段3...},聚合字段名称1: {聚合函数1},聚合字段名称2: {聚合函数2},聚合字段名称3: {聚合函数3},...}
}#分组字段只有一个的时候,形式为:      _id: $分组字段

常见的聚合函数

功能MongoDB 操作符SQL 关键字/语法
计数$sum: 1COUNT(*)
字段名的总和$sum: $字段名SUM(字段名)
字段名的平均值$avg: $字段名AVG(字段名)
字段名的最大值$max: $字段名MAX(字段名)

 案例解析

#案例: 在表中使用country字段进行分组统计,统计出每组的行数,每组的平均年龄
# MongoDB的语句
{$group: {_id: "$country",totalUsers: { $sum: 1 }, avgAge: { $avg: "$age" }}}# 对应的SQL语句
select country,count(*) as totalUsers,AVG(age) as avgAge
from 表名
group by country

排序 $sort

排序 $sort 类比 order by

操作参数格式

$sort操作符的操作参数格式

# key是指排序的字段名  value中1是升序,-1是降序
{ $sort: { key1: value1, key2: value2, ... } }

案例解析

# 案例,对查询到的数据先age字段降序排序,再按price字段升序排序
# MongoDB的语句
{ $sort: { age: -1, price: 1 } }# SQL语句
ORDER BY age DESC, price ASC# SQL中的用法比较固定,只能在数据查询的结果中进行排序
# 但是MongoDB中聚合查询是管道过滤的形式,只考虑拿着现有的数据进行排序过滤,因此可以在任何一步进行操作

限制结果数 $limit和跳过文档$skip

限制结果数 $limit 类比 limit

跳过文档数 $skip 类比 offset

操作参数格式

$limit操作符和$skip的操作参数格式

{ $limit: 返回前多少JSON对象的个数 }{ $skip: 跳过前多少JSON对象的个数 }# 这两个数据常常搭配用于分页# 在SQL中类似
limit 返回前多少行的个数
offset 跳过前多少行的个数SQL分页形式是 limit 返回前多少行的个数 offset 跳过前多少行的个数
经常简写为 limit 返回前多少行的个数 跳过前多少行的个数

案例解析

# 例如: SQL语句,跳过前20行,取接下来的10行
limit 10 20    # 写成MongoDB语句,是有顺序的,需要先跳过,在取值
{$skip: 20},{$limit: 10}

 

左连接 $lookup和拆分数组字段为多个文档$unwind

左连接 $lookup 类比 left join

拆分数组字段为多个文档$unwind 则是为了后续连接之后的数组拆分的过程

这两个是搭配使用的

操作参数格式

$lookup操作符和$unwind操作符的格式

# $lookup的格式
# MongoDB语句
{$lookup: {from: "右表名称",localField: "左表的关联字段",foreignField: "右表关联字段",as: "匹配到的多条右表JSON对象的数组名称,这里自己自定义取名"}
}对应的SQL语句
from 左表名称 left join 右表名称 on 左表的关联字段=右表关联字段# $unwind的格式
# 基础格式
{ $unwind: "需要展开的数组名称" }# 带有选项的格式
{ $unwind: {path: "需要展开的数组名称", 选项键值对}}# 常用的选项键值对
preserveNullAndEmptyArrays: true     # 控制是否保留空数组或 null 值的文档(默认 false,即过滤掉)
includeArrayIndex: "添加存储原数组元素的索引位置,自定义字段名称"
等等

案例解析

# 案例: 左表匹配多条右表的例子# 场景说明
# 左表(orders):一条订单可能关联多个客户(例如“团购订单”或“家庭订单”)
# 右表(customers):多个客户可能属于同一个订单(通过 order_id 关联)# 原始数据
# orders 集合(左表)# { "_id": 1, "order_id": 1001, "amount": 500 }  // 订单 1001# { "_id": 2, "order_id": 1002, "amount": 300 }  // 订单 1002(无关联客户)
# customers 集合(右表)# { "_id": 200, "name": "Alice", "email": "alice@example.com", "order_id": 1001 }  // 属于订单 1001# { "_id": 201, "name": "Bob", "email": "bob@example.com", "order_id": 1001 }    // 属于订单 1001# { "_id": 202, "name": "Charlie", "email": "charlie@example.com", "order_id": 1001 } // 属于订单 1001# { "_id": 203, "name": "Dave", "email": "dave@example.com", "order_id": 999 }   // 无关联订单# 执行 $lookup(左表匹配右表的多条记录)db.orders.aggregate([{$lookup: {from: "customers",localField: "order_id",  // 左表的 order_idforeignField: "order_id", // 右表的 order_idas: "customer_info"      // 匹配的客户信息存入数组,自定义的名称}}])# 匹配结果(customer_info 是数组,可能包含多条客户数据)#订单 1001 匹配到 3 个客户(Alice、Bob、Charlie){"_id": 1,"order_id": 1001,"amount": 500,"customer_info": [{ "_id": 200, "name": "Alice", "email": "alice@example.com", "order_id":1001 },{ "_id": 201, "name": "Bob", "email": "bob@example.com", "order_id":1001 },{ "_id": 202, "name": "Charlie", "email": "charlie@example.com", "order_id":1001}]}#订单 1002 无匹配客户{"_id": 2,"order_id": 1002,"amount": 300,"customer_info": []  // 空数组}# 执行 $unwind(展开多条客户记录){$unwind: {path: "$customer_info",preserveNullAndEmptyArrays: true  // 保留无匹配的订单}}
# 匹配结果(订单 1001 被展开为 3 条文档,每条对应一个客户)# 订单 1001 + Alice{"_id": 1,"order_id": 1001,"amount": 500,"customer_info": { "_id": 200, "name": "Alice", "email": "alice@example.com", "order_id": 1001 }}# 订单 1001 + Bob{"_id": 1,"order_id": 1001,"amount": 500,"customer_info": { "_id": 201, "name": "Bob", "email": "bob@example.com", "order_id": 1001 }}# 订单 1001 + Charlie{"_id": 1,"order_id": 1001,"amount": 500,"customer_info": { "_id": 202, "name": "Charlie", "email": "charlie@example.com", "order_id": 1001 }}#订单 1002 无匹配(保留原文档,customer_info 为 null 或不存在){"_id": 2,"order_id": 1002,"amount": 300,"customer_info": null}
# 最终 $project(提取关键字段){$project: {order_id: 1,amount: 1,customer_name: "$customer_info.name",customer_email: "$customer_info.email"}}
# 最终的处理结果# 订单 1001 + Alice{ "order_id": 1001, "amount": 500, "customer_name": "Alice", "customer_email": "alice@example.com" }# 订单 1001 + Bob{ "order_id": 1001, "amount": 500, "customer_name": "Bob", "customer_email": "bob@example.com" }# 订单 1001 + Charlie{ "order_id": 1001, "amount": 500, "customer_name": "Charlie", "customer_email": "charlie@example.com" }#订单 1002 无匹配{ "order_id": 1002, "amount": 300, "customer_name": null, "customer_email": null }

http://www.dtcms.com/a/282714.html

相关文章:

  • 个人笔记(linux/sort与uniq命令)
  • ETL还是ELT,大数据处理怎么选更靠谱?
  • GIT : early EOF invalid index-pack output
  • Jmeter socket接口测试
  • C#——循环(while循环和do-while循环)
  • Python 与JA3 库的应用
  • 在 Linux 系统中实现 Spring Boot 程序自动启动的最佳实践
  • STM32 GPIO
  • 【深度学习】神经网络-part2
  • 架构设计之计算高性能——单体服务器高性能
  • 算法训练营DAY34 第九章 动态规划part02
  • 告别定位盲区!低成本组合导航系统ER-GNSS/MINS-05的实战能力
  • 不安全的 SSL:主机名验证功能被禁用与修复方案
  • WP Force SSL Pro – HTTPS SSL Redirect Boost Your Website‘s Trust in Minutes!
  • 使用token调用Spring OAuth2 Resource Server接口错误 insufficient_scope
  • 服务攻防-Java组件安全数据处理FastJsonJackSonXStream自动BP插件CVE漏洞
  • 多级缓存架构与热点探测系统核心技术解析
  • 2025年C++后端开发高频面试题深度解析:线程安全LRU缓存设计与实现
  • 免费MCP服务:Excel CSV 转 JSON MCP by WTSolutions 文档
  • C++:现代 C++ 编程基石,C++11核心特性解析与实践
  • 软件开发:重塑商贸物流行业格局的核心力量
  • Zabbix监控K8S的PV信息详细教程!
  • AI产品经理面试宝典第25天:AI+机器人产品设计与技术落地面试题与答法
  • Xsens人形机器人拟人动作AI训练,提升机器人工作精度与效率
  • MySQL高并发高可用架构:分库分表实战与ShardingSphere中间件应用
  • JavaScript语言 Error对象及错误处理机制 原生错误类型
  • 芯谷科技--FET偏置控制器,为卫星通信系统提供稳定偏置D8400
  • 大模型智能体(Agent)工程化:AutoGen企业级落地方案全解析
  • exports使用 package.json字段控制如何访问你的 npm 包
  • A13 GMS应用预装