目前为止,我们已经介绍了一部分聚合管道中的管道参数:
$match:文档过滤
$group:文档分组,并介绍了分组中的常用操作:$addToSet,$avg,$sum,$min,$max等。
$addFields:添加字段,等效于$set
$unset:移除字段
$project:字段投影
如果需要进一步了解可以参考:
MongoDB 聚合管道的文档筛选及分组统计https://blog.csdn.net/m1729339749/article/details/130034658MongoDB 聚合管道的字段投影https://blog.csdn.net/m1729339749/article/details/130055110这篇我们主要介绍使用聚合管道实现文档操作(排序、跳过、获取、随机抽取、分解):
一、准备工作
初始化零食数据
db.goods.insertMany([
{ "_id": 1, name: "薯片", size: "S", quantity: 10, price: 8, expirationTime: ISODate( "2023-08-08T00:00:00Z" ) },
{ "_id": 2, name: "薯片", size: "L", quantity: 8, price: 12, expirationTime: ISODate( "2023-08-08T00:00:00Z" ) },
{ "_id": 3, name: "牛肉干", size: "L", quantity: 5, price: 30, expirationTime: ISODate( "2023-10-10T00:00:00Z" ) },
{ "_id": 4, name: "可口可乐", size: "S", quantity: 10, price: 3, expirationTime: ISODate( "2025-01-06T00:00:00Z" ) },
{ "_id": 5, name: "可口可乐", size: "L", quantity: 6, price: 10, expirationTime: ISODate( "2025-01-06T00:00:00Z" ) },
{ "_id": 6, name: "旺仔牛奶", size: "L", quantity: 10, price: 5, expirationTime: ISODate( "2023-08-10T00:00:00Z" )}
])
初始化测试数据
db.test.insertMany(
[
{ "_id" : "1001", "name" : "张三", "fruits" : [ "apple", "orange" ] },
{ "_id" : "1002", "name" : "李四", "fruits" : [ "banana", "apple" ] },
{ "_id" : "1003", "name" : "王五", "fruits" : [ "banana", "apple", "orange" ] }
]
);
二、文档排序($sort)
语法:{ $sort: { <field1>: <sort order>, <field2>: <sort order> ... } }
将文档按照指定字段排序
其中,<field>指的是排序字段
<sort order>指的是排序方式, 1代表正序,-1代表倒序,{ $meta: "textScore" }代表文本分数进行排序,在使用文本搜索的情况下适合使用,此处不再讲解,后面我们用专门的示例对其进行解释。
语法中使用多个排序字段的原因是比较排序时当第一个字段对应的值相等时则使用第二个字段对应的值进行比较排序,依次类推。
例子1:按照零食的价格进行正序排序
db.goods.aggregate([
{
$sort: { "price": 1 }
}
])
聚合查询的结果:
{ "_id" : 4, "name" : "可口可乐", "size" : "S", "quantity" : 10, "price" : 3, "expirationTime" : ISODate("2025-01-06T00:00:00Z") }
{ "_id" : 6, "name" : "旺仔牛奶", "size" : "L", "quantity" : 10, "price" : 5, "expirationTime" : ISODate("2023-08-10T00:00:00Z") }
{ "_id" : 1, "name" : "薯片", "size" : "S", "quantity" : 10, "price" : 8, "expirationTime" : ISODate("2023-08-08T00:00:00Z") }
{ "_id" : 5, "name" : "可口可乐", "size" : "L", "quantity" : 6, "price" : 10, "expirationTime" : ISODate("2025-01-06T00:00:00Z") }
{ "_id" : 2, "name" : "薯片", "size" : "L", "quantity" : 8, "price" : 12, "expirationTime" : ISODate("2023-08-08T00:00:00Z") }
{ "_id" : 3, "name" : "牛肉干", "size" : "L", "quantity" : 5, "price" : 30, "expirationTime" : ISODate("2023-10-10T00:00:00Z") }
三、跳过文档($skip)
语法:{ $skip: <positive 64-bit integer> }
跳过N条文档,<positive 64-bit integer>指的是正整数
例子:按照零食的价格进行正序排序,跳过两条零食
db.goods.aggregate([
{
$sort: { "price": 1 },
},
{
$skip: 2
}
])
聚合查询的结果:
{ "_id" : 1, "name" : "薯片", "size" : "S", "quantity" : 10, "price" : 8, "expirationTime" : ISODate("2023-08-08T00:00:00Z") }
{ "_id" : 5, "name" : "可口可乐", "size" : "L", "quantity" : 6, "price" : 10, "expirationTime" : ISODate("2025-01-06T00:00:00Z") }
{ "_id" : 2, "name" : "薯片", "size" : "L", "quantity" : 8, "price" : 12, "expirationTime" : ISODate("2023-08-08T00:00:00Z") }
{ "_id" : 3, "name" : "牛肉干", "size" : "L", "quantity" : 5, "price" : 30, "expirationTime" : ISODate("2023-10-10T00:00:00Z") }
四、获取文档($limit)
语法:{ $limit: <positive 64-bit integer> }
获取N条文档,<positive 64-bit integer>指的是正整数
例子:按照零食的价格进行正序排序,跳过两条零食后,获取两条零食
db.goods.aggregate([
{
$sort: { "price": 1 },
},
{
$skip: 2
},
{
$limit: 2
}
])
聚合查询的结果:
{ "_id" : 1, "name" : "薯片", "size" : "S", "quantity" : 10, "price" : 8, "expirationTime" : ISODate("2023-08-08T00:00:00Z") }
{ "_id" : 5, "name" : "可口可乐", "size" : "L", "quantity" : 6, "price" : 10, "expirationTime" : ISODate("2025-01-06T00:00:00Z") }
五、随机抽取文档($sample)
语法:{ $sample: { size: <positive integer N> } }
从所有文档中随机查询N条文档,N是正整数;
例子:从所有零食中随机抽取2种零食
db.goods.aggregate([
{
"$project": {
"_id": 0,
"name": 1,
"size": {
$cond: {
if: { $eq: [ "S", "$size" ] },
then: "小包装",
else: "大包装"
}
}
}
},
{
"$sample": { size: 2 }
}
])
解释一下给出的聚合查询语句:
(1) 使用$project对所有的零食进行字段映射,只保留名称和型号两个字段
(2) 随机查询两条文档
执行聚合查询后会随机查询两条文档数据:
{ "name" : "可口可乐", "size" : "小包装" }
{ "name" : "牛肉干", "size" : "大包装" }
【注意】
(1) 随机查询的文档数据不会重复
(2) 如果随机查询的文档数(N)大于文档的总数(M),则只能返回M条文档
六、分解文档($unwind)
语法:{ $unwind: <field path> }
解开数组字段的值,把数组中的每个元素构造成一个文档
<field path>指的是字段路径,使用$ + 字段
例子:获取test集合中的fruits字段分解后的文档
db.test.aggregate([
{
$unwind: "$fruits"
}
])
聚合查询的结果:
{ "_id" : "1001", "name" : "张三", "fruits" : "apple" }
{ "_id" : "1001", "name" : "张三", "fruits" : "orange" }
{ "_id" : "1002", "name" : "李四", "fruits" : "banana" }
{ "_id" : "1002", "name" : "李四", "fruits" : "apple" }
{ "_id" : "1003", "name" : "王五", "fruits" : "banana" }
{ "_id" : "1003", "name" : "王五", "fruits" : "apple" }
{ "_id" : "1003", "name" : "王五", "fruits" : "orange" }