package builder
import "go.mongodb.org/mongo-driver/bson"
type AggregateM []bson.M
type AggregatePipe struct {
agg AggregateM
}
// 用于调试时直接数据构建原始语句
func (pipe *AggregatePipe) String() string {
bt, err := json.Marshal(pipe.agg)
if err != nil {
return ""
}
return string(bt)
}
func NewAggregatePipe() *AggregatePipe {
return &AggregatePipe{
agg: make(AggregateM, 0),
}
}
// 根据 $or 组数量动态生成 $match 中的语句
// matchOr = []$or
func buildMatchOr(match bson.M, matchOr []interface{}) {
if match != nil && len(matchOr) > 0 {
if len(matchOr) > 1 {
and := make([]interface{}, 0)
for _, or := range matchOr {
if or != nil {
and = append(and, bson.M{"$or": or})
}
}
if len(and) > 0 {
match["$and"] = and
}
} else {
if matchOr[0] != nil {
match["$or"] = matchOr[0]
}
}
}
}
func (pipe *AggregatePipe) Match(m bson.M, or ...interface{}) *AggregatePipe {
if len(or) > 0 {
if len(m) == 0 {
m = make(bson.M)
}
buildMatchOr(m, or)
}
if len(m) > 0 {
pipe.agg = append(pipe.agg, bson.M{
"$match": m,
})
}
return pipe
}
// 使用 $group 要注意 sort 位置(要在 $group 之后)及字段名的变化(_id.date)
func (pipe *AggregatePipe) Sort(s bson.M) *AggregatePipe {
if len(s) > 0 {
pipe.agg = append(pipe.agg, bson.M{
"$sort": s,
})
}
return pipe
}
// 使用 $group 要注意 sort 位置(要在 $group 之后)及字段名的变化(_id.date)
func (pipe *AggregatePipe) SortOne(field string, isDesc bool) *AggregatePipe {
if len(field) > 0 {
sort := 1 // ASC 升序
if isDesc {
sort = -1 // DESC 降序
}
pipe.agg = append(pipe.agg, bson.M{
"$sort": bson.M{
field: sort,
},
})
}
return pipe
}
// 联表
func (pipe *AggregatePipe) Lookup(lookups ...bson.M) *AggregatePipe {
if len(lookups) > 0 {
for _, lp := range lookups {
if len(lp) > 0 {
pipe.agg = append(pipe.agg, bson.M{
"$lookup": lp,
})
}
}
}
return pipe
}
func (pipe *AggregatePipe) LookupOne(from, alias, localField, foreignField string) *AggregatePipe {
if from == "" || alias == "" || localField == "" || foreignField == "" {
return pipe
}
lookup := bson.M{
"from": from,
"as": alias,
"localField": localField,
"foreignField": foreignField,
}
pipe.agg = append(pipe.agg, bson.M{
"$lookup": lookup,
})
return pipe
}
func (pipe *AggregatePipe) Unwind(unwinds ...string) *AggregatePipe {
if len(unwinds) > 0 {
for _, uw := range unwinds {
if len(uw) > 0 {
pipe.agg = append(pipe.agg, bson.M{
"$unwind": uw,
})
}
}
}
return pipe
}
func (pipe *AggregatePipe) Group(groups ...bson.M) *AggregatePipe {
if len(groups) > 0 {
for _, gp := range groups {
if len(gp) > 0 {
if _, exist := gp["_id"]; exist {
pipe.agg = append(pipe.agg, bson.M{
"$group": gp,
})
}
}
}
}
return pipe
}
// 控制输出字段
func (pipe *AggregatePipe) Project(p bson.M) *AggregatePipe {
if len(p) > 0 {
pipe.agg = append(pipe.agg, bson.M{
"$project": p,
})
}
return pipe
}
func (pipe *AggregatePipe) Skip(skip int64) *AggregatePipe {
if skip > 0 {
pipe.agg = append(pipe.agg, bson.M{
"$skip": skip,
})
}
return pipe
}
func (pipe *AggregatePipe) Limit(limit int64) *AggregatePipe {
if limit > 0 {
pipe.agg = append(pipe.agg, bson.M{
"$limit": limit,
})
}
return pipe
}
func (pipe *AggregatePipe) Custom(ms ...bson.M) *AggregatePipe {
for _, m := range ms {
if len(m) > 0 {
pipe.agg = append(pipe.agg, m)
}
}
return pipe
}
// 获取用于统计数据量的 AggregateM
// reservePaginate[0] == true 保留语句中所有的 $skip、$limit
func (pipe *AggregatePipe) CountM(reservePaginate ...bool) AggregateM {
var agg AggregateM
// 默认去除语句中所有的 $skip、$limit
if len(reservePaginate) == 0 || reservePaginate[0] == false {
for _, a := range pipe.agg {
if _, exist := a["$skip"]; exist {
continue
}
if _, exist := a["$limit"]; exist {
continue
}
agg = append(agg, a)
}
}
agg = append(agg, bson.M{
"$count": "total",
})
return agg
}
// 获取用于查询数据的 AggregateM
func (pipe *AggregatePipe) QueryM() AggregateM {
return pipe.agg
} 本文标题:链式构建 mongodb 聚合查询语句
版权声明:本文使用「署名-非商业性使用-相同方式共享」创作共享协议,转载或使用请遵守署名协议。
相关文章
上一篇:激活 Windows 10