Mongo Pipeline的一个使用案例
一、背景
最近在做的一个项目是工单模板,用户可以自定义工单的任意字段,自定义字段可用于渲染表单、详情、列表搜索条件、列表展示字段。这里最难的是列表搜索条件,因为用户可以自定义字段,不可能对每个字段建立索引,所以这里应用了一个类似倒排索引的方法。具体方法是建一个搜索集合(暂且命名为C),C有四个字段,分别为:_id是C的主键,field_id是自定义字段的主键id,field_value是自定义字段的值,target_id是目标文档的主键。
举例如下:比如工单有两个字段,分别是“日期”和“等级”,那么工单在保存后的数据如下:
- 工单:
{
"_id" : ObjectId("6265843314fefc03222bf1e5"),
"values" : [
{
"id" : NumberLong(3),
"fields" : "2021-03-01"
},
{
"id" : NumberLong(6),
"fields" : "严重"
}
]
}
- 搜索集合
{
"_id" : ObjectId("e708864855f3bb69c4d9a212"),
"field_id" : NumberLong(3),
"field_value" : "2021-03-01",
"target_id" : "6265843314fefc03222bf1e5",
}
{
"_id" : ObjectId("e708864855f3bb69c4d9a213"),
"field_id" : NumberLong(6),
"field_value" : "严重",
"target_id" : "6265843314fefc03222bf1e5",
}
二、问题描述
在查询工单的时候,如何做查询呢?比如需要查询同时满足日期为“2021-03-01”并且等级为“严重”的工单列表
三、技术分析
方案一、先搜索出满足第一个条件的工单集合,然后搜索出满足第二个条件的工单集合,两者取交集,伪代码如下
res1 := mongoDb.Collection.Find(bson.M{"field_id":3, "field_value":"2021-03-01"})
res2 := mongoDb.Collection.Find(bson.M{"field_id":6, "field_value":"严重"})
res := intersect_func(res1, res2)
return res
方案二、使用mongo的Aggregation Pipeline,先query,然后group,伪代码如下:
type AggregateResult struct {
ID string `bson:"_id" json:"_id"`
Count int `bson:"count" json:"count"`
}
pipeline := [
{
$match:{
$or:[{
"field_id":3,
"field_value":"2021-03-01"
},{
"field_id":6,
"field_value":"严重"
}]
}
},
{
$group:{
_id: "$target_id",
count: {$sum : 1}
}
}
]
res := make([]string, 0)
cursor, _ := mongoDb.Collection.Aggregate(ctx, pipeline)
for cursor.Next(ctx) {
e := new(AggregateResult)
_ = cursor.Decode(e)
if e.Count < 2 {
continue
}
res = append(res, e.ID)
}
return res
四、参考
- https://docs.mongodb.com/v4.0/core/aggregation-pipeline/
- https://docs.mongodb.com/v4.0/reference/operator/aggregation/group/#pipe._S_group