MongoDB 原生管道查询
MongoDB 中使用db.COLLECTION_NAME.aggregate([{},...]) 方法来构建和使用聚合管道。先看下官网给的实例,感受一下聚合管道的用法。
MongoDB Aggregation 管道操作符与表达式
| 管道操作符 | 描述 | 
|---|---|
| $project | 增加、删除、重命名字段 | 
| $match | 条件匹配 | 
| $limit | 限制结果的数量 | 
| $skip | 跳过文档的数量 | 
| $sort | 条件排序 | 
| $group | 条件组合结果 统计 | 
| $lookup | 引入其它集合的数据(表关联查询) | 
SQL 和 NoSQL 对比
| SQL | NoSQL | 
|---|---|
| WHERE | $match | 
| GROUP BY | $group | 
| HAVING | $match | 
| SELECT | $project | 
| ORDER BY | $sort | 
| LIMIT | $limit | 
| SUM() | $sum | 
| COUNT() | $sum | 
| join | $lookup | 
管道表达式
管道操作符作为“键”,所对应的“值”叫做管道表达式。
例如{$match:{status:"A"}},$match称为管道操作符,而status:"A"称为管道表达式,是管道操作符的操作数(Operand)。
每个管道表达式是一个文档结构,它是由字段名、字段值、和一些表达式操作符组成的。
| 常用表达式操作符 | 描述 | 
|---|---|
| $addToSet | 将文档指定字段的值去重 | 
| $max | 文档指定字段的最大值 | 
| $min | 文档指定字段的最小值 | 
| $sum | 文档指定字段求和 | 
| $avg | 文档指定字段求平均 | 
| $gt | 大于给定值 | 
| $lt | 小于给定值 | 
| $eq | 等于给定值 | 
$project
修改文档的结构,可以用来重命名、增加或删除文档中的字段。
// 要求查找order 只返回文档中trade_no 和all_price 字段db.order.aggregate([{$project:{trade_no:1,all_price:1}}])
$match
作用: 用于过滤文档。用法类似于find() 方法中的参数。
// 要求查找order 只返回文档中trade_no 和all_price 字段, 同时只返回价格大于90的db.order.aggregate([{$project:{ trade_no:1, all_price:1 }},{$match:{"all_price":{$gte:90}}}])
$group
将集合中的文档进行分组,可用于统计结果。
// 统计每个订单的数量,按照订单号分组db.order_item.aggregate([{$group: {_id: "$order_id", total: {$sum: "$num"}}}])
$sort
将集合中的文档进行排序
db.order.aggregate([{$project:{ trade_no:1, all_price:1 }},{$match:{"all_price":{$gte:90}}},{$sort:{"all_price":-1}}])
$limit
限制数量。
db.order.aggregate([{$project:{ trade_no:1, all_price:1 }},{$match:{"all_price":{$gte:90}}},{$sort:{"all_price":-1}},{$limit:1}])
$skip
db.order.aggregate([{$project:{ trade_no:1, all_price:1 }},{$match:{"all_price":{$gte:90}}},{$sort:{"all_price":-1}},{$skip:1}])
$lookup 表关联
db.order.aggregate([{$lookup:{from: "order_item",localField: "order_id",foreignField: "order_id",as: "items"}}])
Mongoose 管道查询
模拟数据
设置一个orders 订单集合,一个orderItems 集合,1个订单记录对应多个订单项,构成一对多的关系。
order 的 model
const mongoose = require('./db')// 利用自动生成的 _id 作为唯一标识const orderSchema = mongoose.Schema({allPrice: {type: Number,required: true},content: String})const orderModel = mongoose.model('Order', orderSchema, 'orders')module.exports = orderModel
orderItem 的 model
const mongoose = require('./db')const orderItemSchema = mongoose.Schema({orderID: {type: mongoose.Schema.Types.ObjectId,},title: {type: String},num: Number})const orderItemModel = mongoose.model('OrderItem', orderItemSchema, 'orderItem')module.exports = orderItemModel
创建模拟数据
const OrderModel = require('../model/order')const OrderItemModel = require('../model/orderItem')// 模拟数据const orders = [{ allPrice: 23, content: '订单1'},{ allPrice: 33, content: '订单2'},{ allPrice: 43, content: '订单3'},{ allPrice: 53, content: '订单4'},{ allPrice: 63, content: '订单5'},{ allPrice: 73, content: '订单6'},{ allPrice: 83, content: '订单7'},]const orderItems = [{title: 'item1', num: 3},{title: 'item2', num: 3},{title: 'item3', num: 3},{title: 'item4', num: 3},{title: 'item5', num: 3},{title: 'item6', num: 3},{title: 'item7', num: 3},{title: 'item8', num: 3},]// 创建订单,每个订单包含3个随机订单项const createOrder = (orders, orderItems) => {orders.forEach((order) => {let tempOrder = new OrderModel(order)tempOrder.save((err) => {if (err) {console.log('订单创建失败')return}// 创建订单项let rnd = Math.floor(Math.random()*(orderItems.length-3))let tempOrderItems = []tempOrderItems.push(orderItems[rnd])tempOrderItems.push(orderItems[rnd+1])tempOrderItems.push(orderItems[rnd+2])createOrderItems(tempOrderItems, tempOrder._id)})})}const createOrderItems = (orderItems, orderID) => {orderItems.forEach(({title, num}) => {let tempItem = new OrderItemModel({orderID,title,num})tempItem.save((err) => {if (err) {console.log('订单项保存错误')return}console.log('订单项保存成功')})})}createOrder(orders, orderItems)
使用管道查询
查询每个订单对应的所有订单项
const OrderModel = require('../model/order')const OrderItemModel = require('../model/orderItem')// 查询每个订单的订单项,并保存到 items 的字段中OrderModel.aggregate([{$lookup: {from: 'orderItem', // 表名localField: '_id',foreignField: 'orderID',as: 'items'}}], (err, docs) => {if(err) {console.log('查询失败')return}docs.forEach((doc) => {console.log(doc.content)doc.items.forEach((item) => {console.log(doc.content, item.title)})})})
查询每个订单对应的每个订单
const OrderModel = require('../model/order')const OrderItemModel = require('../model/orderItem')// 查询系念订单项对应的订单内容OrderItemModel.aggregate([{$lookup: {from: 'orders',localField: 'orderID',foreignField: '_id',as: 'order'}}], (err, docs) => {if(err) {console.log('查询失败')}docs.forEach((doc) => {console.log(`订单项:${doc.title}, 属于订单 ${doc.order[0].content}`)})})
