文章目录
- 数据准备
- 条件查询
- 聚合查询
- 模糊查询
- 拆分数据
- 关联查询
- 根据用户组查询用户
- 根据用户查询用户组
- 控制显示
- 字段置顶
- 合并字段
- 新增或修改字段
- 计算总数
- 分页查询
- 分页查询加总数
- 排序
- 分组
数据准备
UserPO
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "user")
public class UserPO extends BasePO {private String name;private String description;}
UserGroupPO
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "user_group")
public class UserGroupPO extends BasePO {private String name;private String description;private List<Long> userIds;}
用户数据
用户组数据
条件查询
match:and 匹配
查询用户组 id 等于 1,且删除标识等于 N 的数据
db.getCollection("user_group").aggregate([{"$match": {"$and": [{"_id": NumberLong("1")}, {"deleteFlag": "N"}]}
}]);
match:or 匹配
查询用户 name 等于 nn 或者 description 等于 dd 的数据
db.getCollection("user").aggregate([{"$match": {"$or": [{"name": "nn"}, {"description": "dd"}]}
}]);
聚合查询
查询用户组里的用户详情
模糊查询
match:按规则匹配
regex:按正则匹配
实现:按 xxx 模糊匹配字段 userName 或者 按 xxx 模糊匹配字段 userDes
db.getCollection("user").aggregate([{"$match": {"$or": [{"userName": {"$regex": ".*xxx.*"}}, {"userDes": {"$regex": ".*xxx.*"}}]}
}]);
拆分数据
unwind:将文档中的某一个数组类型字段拆分成多条
实现:根据 userIds 把用户组拆分了多行
db.getCollection("user_group").aggregate([{"$match": {"$and": [{"_id": NumberLong("2809813774582045833")}, {"deleteFlag": "N"}]}
}, {"$unwind": "$userIds"
}]);
关联查询
根据用户组查询用户
lookup官方文档
lookup:关联查询,类似于 MySQL 的 left jion 效果
实现:根据用户组的 userIds 查询用户详情,注意:查出来的 user 是个数组
- from:同一个数据库下等待被Join的集合。
- localField:源集合中的match值,如果输入的集合中,某文档没有 localField
这个Key(Field),在处理的过程中,会默认为此文档含
有 localField:null的键值对。 - foreignField:待Join的集合的match值,如果待Join的集合中,文档没有foreignField
值,在处理的过程中,会默认为此文档含有 foreignField:null的键值对。 - as:为输出文档的新增值命名。如果输入的集合中已存在该值,则会覆盖掉
db.getCollection("user_group").aggregate([{"$match": {"$and": [{"_id": NumberLong("4523281346310580824")}, {"deleteFlag": "N"}]}
}, {"$unwind": "$userIds"
}, {"$lookup": {"from": "user","localField": "userIds","foreignField": "_id","as": "user"}
}]);
根据用户查询用户组
lookup:关联查询,类似于 MySQL 的 left jion 效果
实现:查询用户列表且包含对应的用户组信息,注意:查出来的 userGroups 是个数组
- from:要关联的从表名
- let:自定义变量,可以为多个
- pipeline:自定义的操作从表的聚合,但不允许使用out和merge操作,使用 let 定义的变量需要用两个$
- as:为输出文档的新增值命名。如果输入的集合中已存在该值,则会覆盖掉
db.getCollection("user").aggregate([{$lookup: {from: "user_group",let: {user_id: "$_id"},pipeline: [{$match: {$expr: {$in: ["$$user_id", "$userIds"]}}}],as: "userGroups"}
}]);
控制显示
project 控制字段显示与否
实现: 删除无用字段,
- 普通列({成员:1 | true}):表示要显示的内容
- “_id” 列({“_id”:0 | false}):表示 “_id” 列是否显示
db.getCollection("user_group").aggregate([{"$match": {"$and": [{"_id": NumberLong("4523281346310580824")}, {"deleteFlag": "N"}]}
}, {"$unwind": "$userIds"
}, {"$lookup": {"from": "user","localField": "userIds","foreignField": "_id","as": "user"}
}, {"$project": {"user": 1,"_id": 0,}
}]);
字段置顶
arrayElemAt:将指定文档提升到顶级并替换所有其它字段。该操作会替换输入文档中的所有现在字段,包括_id字段
replaceRoot:返回存在于给定数组的指定索引上的元素
实现:把 user 的第一个元素提至顶级
db.getCollection("user_group").aggregate([{"$match": {"$and": [{"_id": NumberLong("4523281346310580824")}, {"deleteFlag": "N"}]}
}, {"$unwind": "$userIds"
}, {"$lookup": {"from": "user","localField": "userIds","foreignField": "_id","as": "user"}
}, {"$project": {"user": 1,"_id": 0,}
}, {"$replaceRoot": {"newRoot": {"$arrayElemAt": ["$user", 0]}}
}]);
此数据格式已经非常漂亮,符合日常返回格式,完成了需求
合并字段
mergeObjects:将多个文档合并为一个文档,如果要合并的文档包含相同的字段名,则结果文档中的字段具有该字段最后一个合并文档中的值
实现:保留 userGroup 的信息
db.getCollection("user_group").aggregate([{"$match": {"$and": [{"_id": NumberLong("4523281346310580824")}, {"deleteFlag": "N"}]}
}, {"$unwind": "$userIds"
}, {"$lookup": {"from": "user","localField": "userIds","foreignField": "_id","as": "user"}
}, {"$replaceRoot": {"newRoot": {"$mergeObjects": [{"$arrayElemAt": ["$user", 0]}, "$$ROOT"]}}
},{"$project": {"user": 0}
}]);
新增或修改字段
set:字段存在则修改,无则新增
实现:
db.getCollection("user_group").aggregate([{"$match": {"$and": [{"_id": NumberLong("4523281346310580824")}, {"deleteFlag": "N"}]}
}, {"$unwind": "$userIds"
}, {"$lookup": {"from": "user","localField": "userIds","foreignField": "_id","as": "user"}
}, {"$replaceRoot": {"newRoot": {"$mergeObjects": [{"$arrayElemAt": ["$user", 0]}, "$$ROOT"]}}
}, {"$project": {"user": 0}
}, {"$set": {"accounts": "accounts","deleteFlag": "deleteFlag"}
}]);
计算总数
count:计算总数
实现:查询 user 表的总条数
db.getCollection("user").aggregate([{"$count": "total"}
]);
分页查询
分页:对数据做分页处理
跳过 3 条数据,从第 4 条起,显示 2 条
- skip:跳过多少条
- limit:显示多少条
db.getCollection("user").aggregate([{"$skip": 3},{"$limit": 2}
]);
分页查询加总数
分页查询需要同时返回总数和数据
facet:在同一组输入文档的单一阶段中处理多个聚合管道;每个子管道在输出文档中都有自己的字段,其结果存储在文档数组中
db.getCollection("user").aggregate([{"$facet": {"metadata": [{"$count": "total"}],"records": [{"$skip": 0}, {"$limit": 10}]}}
]);
metadata 不太符合常规数据结构,把它去掉,把 total 提上顶层
db.getCollection("user").aggregate([{"$facet": {"metadata": [{"$count": "total"}],"records": [{"$skip": 0}, {"$limit": 2}]}},{"$replaceRoot": {"newRoot": {"$mergeObjects": [{"$arrayElemAt": ["$metadata", 0]}, "$$ROOT"]}}},{"$project": {"metadata": 0}}
]);
排序
sort:根据某字段对数据进行排序
其中 key 用来定义要根据那个字段进行排序,后面的值 1 则表示以升序进行排序,若要以降序进行排序则需要将其设置为 -1
实现:对用户表按创建时间倒序来排序
db.getCollection("user").aggregate([{$sort: {creationDate: - 1}}
]);
分组
$group :根据某字段对数据进行分组,类似 MySQL的 group by
- _id :强制必须存在。可以为 null
- $sum:计算总数
- $avg:计算平均值
- $max:最大值
- $min:最小值
- $first:输出第一个内容
- $last:输出最后一个内容
- $push:将数据拼接到一个数组中
- $addToSet:将数据拼接到一个数组中,过滤重复数据
数据准备
$sum:根据用户的 userName 字段,对数据进行分组,并统计重复的总数
db.getCollection("user").aggregate([{$group: {_id: "$userName",count: {$sum: 1}}
}]);
$avg:根据用户的 userName 字段,对数据进行分组,并算出分组后 userDes 的平均值
db.getCollection("user").aggregate([{$group: {_id: "$userName",desavg: {$avg: "$userDes"}}
}]);
$push:根据用户的 userName 字段,对数据进行分组,并把 userDes 组合到 num
db.getCollection("user").aggregate([{$group: {_id: "$userName",num: {$push: "$userDes"}}
}]);
$addToSet:和push类似,但会过滤重复的数据,name2 的 40 只有一条了
db.getCollection("user").aggregate([{$group: {_id: "$userName",num: {$addToSet: "$userDes"}}
}]);
$$ROOT:分组后生成list
db.getCollection("user").aggregate([{$group: {_id: "$userName",num: {$push: "$$ROOT"}}
}]);