MongoTemplate中聚合查询
图
MongoDB并非关系型数据库,但是却又能实现关系型数据库的功能,比如关联查询、聚合查询等操作,以及关联后数据的过滤、处理等情况,它都是支持的,更多业务场景它都能完成

关联操作

Criteria criteria = new Criteria();
criteria.and("status").ne(GlobalStatus.STATUS_DELETE.value());
if (StrUtil.isNotBlank(keyword)) {
    criteria.and("customer").regex(keyword);
}
Aggregation aggregation = Aggregation.newAggregation(
        // 查询条件
        match(criteria),
        // 排序(可多个)
        Aggregation.sort(Sort.Direction.DESC, "_id"),
        // 跳过多少条
        Aggregation.skip((pageIndex - 1) * pageSize),
        // 查询多少条数据
        limit(pageSize),
        // 关联其他表(本表字段关联其他表的主键)
        lookup(UserShowInfoView.collectionName, "uid", "_id", "user0"),
        // 关联其他表(本表主键关联其他表的字段,最好给关联表的关联字段加索引,加快查询速度)
        lookup(ShopServer.COLLECTION_NAME, "_id", "shopId", "server0"),
        // 子表再次关联其他表(注意尽量设计表中带字段,该操作性能不高)
        lookup(User.COLLECTION_NAME, "$server0.uid", "_id", "user1"),
        // 数据处理,字段过多使用Class工具类
        project("applyStatus", "violationTag", "status", "authV", "avatar","name")
                // 更改字段名进行映射
                .and("schoolName").as("name")
                // 取关联数据的第一条数据
                .andExpression("arrayElemAt('$user0',0)").as("user")
                // 取关联数据的第一条的某个字段
                .andExpression("arrayElemAt('$authV0.authV',0)").as("authV")
                // 取关联数据的条数(数量)
                .andExpression("{$size:'$server0'}").as("serverNum")
                // 加减(减-1也可)
                .and("evaluationCount").plus(1).as("evaluationCount")
                // 除法计算
                .andExpression("{$divide:{'$goodEvaluationCount','$evaluationCount'}}").as("favourable")
                // 给null的数据赋默认值
                .and("isTopping").applyCondition(ConditionalOperators.IfNull.ifNull("isTopping").then("0"))
                // 是否存在(用于是否收藏、点赞)
                .andExpression(String.format("in(new org.bson.types.ObjectId('%s'),msgLike.uid)", uid)).as("like")
);
List<ShopAdminListVo> list = mongoTemplate.aggregate(aggregation, Shop.COLLECTION_NAME, ShopAdminListVo.class).getMappedResults();

注意:关联后的变量名称不要使用status,会报错

关联分组

Aggregation.group("uid").count().as("count")
    .first("uid").as("uid")
    .first("lawDeptId").as("lawDeptId")
Aggregation.group().sum("time").as("count")

注意:group后就剩下_id字段,且为group中的字段值,如果后面需要其他字段使用first

关联排序

// 单条件单字段
Aggregation.sort(Sort.Direction.DESC, "createDate")

// 单条件多字段
Aggregation.sort(Sort.Direction.DESC, "status", "createDate")

// 多条件
Aggregation.sort(Sort.Direction.ASC, "status").and(Sort.Direction.DESC, "createDate")

sort(new Sort(Sort.Order.asc("topType"), Sort.Order.desc("sort"), Sort.Order.desc("topDate"), Sort.Order.desc("_id"))),

// 多条件格式
Sort sort;
if (dateSort == 0) {
    sort = new Sort(Sort.Direction.DESC, "createDate");
} else {
    sort = new Sort(Sort.Direction.ASC, "createDate");
}
Aggregation aggregation = Aggregation.newAggregation(
    Aggregation.match(criteria),
    Aggregation.sort(sort)
);

关联过滤

//过滤多条件:filter
BasicDBObject filter4 = new BasicDBObject("$filter", new BasicDBObject(
        "input","$pushMessage0"
        ).append("as", "item")
        .append("cond", new BasicDBObject("$and", 
                new Object[] {
                        new BasicDBObject("$eq", new Object[] {"$$item.classify", Constant.INTERACTION_NOTIFY_CLASSIFY}),
                        new BasicDBObject("$eq", new Object[] {"$$item.status", Constant.COMM_STATUS_NORMAL})
                        }))
        );
BasicDBObject reserveFilter = BasicDBObject.parse("{'$filter':{'input':'$pushMessage0','as':'item','cond':{'$and':[{'$eq':['$$item.classify',4]},{'$eq':['$$item.status',0]}]}}}");		
//过滤单条件:filter
BasicDBObject filter = new BasicDBObject("$filter", new BasicDBObject(
                    "input","$lives0"
                    ).append("as", "item")
                    .append("cond", new BasicDBObject("$eq", 
                            new Object[] {"$$item.status", Constant.COMM_STATUS_NORMAL})));
BasicDBObject reserveFilter = BasicDBObject.parse("{'$filter':{'input':'$reserve0','as':'item','cond':{'$eq':['$$item.status',0]}}}");
            
            Aggregation agg = newAggregation(
                    geoNear(NearQuery.near(new Point(lon, lat))
                            .query(query(where("_id").is(id)))
                            .distanceMultiplier(Constant.EarthRadius)
                            .spherical(true), "dist.dis"),//距离geoNear查询
                    lookup(Camera.collectionName, "live", "_id", "lives0"), //关联查询
                    lookup(UserShowInfoView.collectionName, "uid", "_id", "user0"),
                    lookup(LikeRecodeView.collectionName, "_id", "oid", "like0"),
                    lookup(BusinessWantGo.collectionName, "_id", "bid", "wantGo0"),
                    lookup(BusinessCustomerRecord.collectionName, "_id", "bid", "customerRecode0"),
                    Aggregation.graphLookup(LegalVideoType.COLLECTION_NAME)
                            .startWith("$_id")
                            .connectFrom("_id")
                            .connectTo("parentId").as("son"),
                    project("status", "hot", "uid", "name", "keyWordType", "intro", "intro2"
                            , "cover", "loc", "address", "doBusinessWeek", "startDoBusinessTime"
                            , "endDoBusinessTime", "telphone", "createDate")
                    .andExpression("dist.dis").as("dis")
                    .andExpression("substr('$cityCode', 4, 2)").as("sotCityCode1")//截取字符串,从某(4)位置开始截取几(2)位;
                    .and(new BaseAggregateExpression(filter)).as("live") //过滤器过来数组
                    .andExpression("arrayElemAt(\"$user0\",0)").as("user") //选取数组第0个
                    
                    .andExpression(String.format("in(new org.bson.types.ObjectId(\"%s\"),like0.uid)", uid)).as("like") //判断是否包含
                    .andExpression("{$size:'$like0'}").as("likeNum")  //统计数组长度
                    
                    .andExpression("{$size:'$wantGo0'}").as("wantGoNum")
                    
                    .andExpression("{$size:'$customerRecode0'}").as("customerRecodeNum")
                    .andExpression("{$reverseArray:'$customerRecode0'}").as("customerRecode1"),//数组翻转
                    
                    project("status", "hot", "uid", "name", "keyWordType", "intro", "intro2"
                            , "cover", "loc", "address", "doBusinessWeek", "startDoBusinessTime"
                            , "endDoBusinessTime", "telphone", "createDate"
                            , "live", "user", "like", "likeNum", "wantGoNum"
                            , "customerRecodeNum", "dis")
                    .andExpression(String.format("{slice(customerRecode1,%s)}", Constant.LIST_LIMIT_5)) //截取数组指定长度
                    .as("customerRecode2"),
                    lookup(UserShowInfoView.collectionName, "customerRecode2.uid", "_id", "callUsers"),
                    
                    project("status", "hot", "uid", "name", "keyWordType", "intro", "intro2"
                            , "cover", "loc", "address", "doBusinessWeek", "startDoBusinessTime"
                            , "endDoBusinessTime", "telphone", "createDate"
                            , "live", "user", "like", "likeNum", "wantGoNum"
                            , "customerRecodeNum", "callUsers", "dis")
                    );
            BasicDBObject obj = mongoTemplate.aggregate(agg, Business.class, BasicDBObject.class).getUniqueMappedResult();	

字段计算

Aggregation aggregation = Aggregation.newAggregation(
        Aggregation.match(Criteria.where("type").in(1, 3).and("status").is(0)),
        Aggregation.project("startTime", "endTime").andExpression("endTime - startTime").as("totalTime"),
        Aggregation.group().sum("totalTime").as("totalTime")
);

层级遍历

例如多级分类等场景

Aggregation aggregation = Aggregation.newAggregation(
    Aggregation.match(criteria1),
    Aggregation.graphLookup(LegalVideoType.COLLECTION_NAME)
        .startWith("$_id")
        .connectFrom("_id")
        .connectTo("parentId").as("son"),
    Aggregation.project().andExpression("son._id").as("sonIdList")
);
    
List<Document> dbObjectList = mongoTemplate.aggregate(aggregation, LegalVideoType.COLLECTION_NAME, Document.class).getMappedResults();

注意

集合查询中如果出现关联查询,最好为关联表的外键建立索引,否则数据量大了就会比较慢