SpringBoot+MongoDB查询操作(MongoTemplate)总结

没有学过非关系型数据库,特别是嵌套实体类查询,寸步难行

实体类的结构是这样的

实体类T1

@Data
@Document("[mongoDB对应的索引]")
public class T1 {
    private String id;
    private Integer count = 0;
    private String startTime;
    private String message;
    private List<T2> details;
}

实体类T2

@Data
public class T2 implements Serializable {
    private String url;
    private String status;
    private Integer pageCount;
    private String createTime;
    private List<T3> details;
}

实体类T3

@Data
public class T3 implements Serializable {
    private String status;
    private String message;
    private String createTime;
}

注入模板类

@Autowired
private MongoTemplate mongoTemplate;

1.简单构造查询

构造Query对象

想实现的效果:如果传入id就根据ID查询对象,如果没有传入ID根据创建时间查询最新的对象

Query query = new Query();
if (StrUtil.isNotEmpty(id)) {
    query.addCriteria(Criteria.where("_id").is(id));//is:等于
} else {
    query.with(Sort.by(Sort.Order.desc("startTime")));//降序
}
query.fields().exclude("details.details");//exclude:不需要查询这个字段
T1 t1 = mongoTemplate.findOne(query, T1.class);//findOne:查询集合第一个对象

如果想获取整个list,使用find方法

Query query = new Query();
query.fields().include("_id");//include:只需要查询这个字段
query.fields().include("count");
query.fields().include("startTime");
query.with(Sort.by(Sort.Order.desc("startTime")));
query.limit(10);//分页
List<T1> list = mongoTemplate.find(query, T1.class);

2.聚合管道查询

构造AggregationResults对象

想实现的效果:根据T3时间排序取出前十条,因为文档是嵌套的,需要使用unwind方法展开才能获取对应的值

 List<T3> list = new ArrayList<>();
Aggregation aggregation = Aggregation.newAggregation(
    Aggregation.unwind("details"),//unwind:展开文档
    Aggregation.unwind("details.details"),
    Aggregation.sort(Sort.Direction.DESC, "details.details.startTime"),//T3的时间排序,就是一层点一层
    Aggregation.limit(10)//分页
).withOptions(Aggregation.newAggregationOptions().allowDiskUse(true).build());
AggregationResults<JSONObject> results = mongoTemplate.aggregate(aggregation, "[集合名称]", JSONObject.class);
//直接映射T3,发现details里的details取不到值,转为JSON格式再映射就拿到了。JSONObject使用到fastjson包
for (JSONObject jsonObject : results) {
    T3 t3 = new T3();
    JSONObject jsonObject1 = jsonObject.getJSONObject("details").getJSONObject("details");
    String status = jsonObject1.getString("status");
    String startTime = jsonObject1.getString("createTime");
    String message = jsonObject1.getString("message");
    t3.setStatus(status);
    t3.setCreateTime(startTime);
    t3.setMessage(message);
    list.add(t3);
}
return list;

动态条件构造查询

想实现的效果:根据T2字段,如果传入时间或者链接,就通过传参查询,如果没有传入则查询全部。并且传入的参数还有页数和页码。mangoDB没有工具类进行分页,所以需要手动分页。并且不像关系型数据库可以直接传参进行分页,是通过skip()跳过进行分页(可能会有深度分页问题)

public CommonPage<T2> queryDetails(DataVo dataVo) {
        Integer pageNum = dataVo.getPageNum();//页码
        Integer pageSize = dataVo.getPageSize();//页数
        if (pageNum <= 0) {
            pageNum = 1;
        }
        if (pageSize <= 0) {
            pageSize = 10;
        }
        Integer skipNum = (pageNum - 1) * pageSize;
        String id = "";
        if (StrUtil.isEmpty(dataVo.getId())) {
            Query query = new Query();
            query.with(Sort.by(Sort.Order.desc("startTime")));
            query.fields().include("_id");
            T1 ti = mongoTemplate.findOne(query, T1.class);
            id = ti.getId();
        } else {
            id = dataVo.getId();
        }
        List<T2> resultList = new ArrayList<>();
        List<AggregationOperation> commonOperations = new ArrayList<>();
        Criteria criteria = Criteria.where("_id").is(id);
        UnwindOperation unwind = Aggregation.unwind("details");
        SkipOperation skip = Aggregation.skip(skipNum);//跳过文档
        LimitOperation limit = Aggregation.limit(pageSize);
        SortOperation sort = Aggregation.sort(Sort.Direction.DESC, "details.createTime");
        if (StrUtil.isNotEmpty(dataVo.getUrl())) {
            String exprSpecialWord = escapeExprSpecialWord(dataVo.getUrl());
            Pattern pattern = Pattern.compile("^.*" + exprSpecialWord + ".*$", Pattern.CASE_INSENSITIVE);
            criteria.andOperator(Criteria.where("details.url").regex(pattern));
        }
        if (StrUtil.isNotEmpty(dataVo.getStartTime()) && StrUtil.isNotEmpty(dataVo.getEndTime())) {
       criteria.andOperator(Criteria.where("details.createTime").lte(dataVo.getEndTime()).gte(dataVo.getStartTime()));
        }
        if (dataVo.getStatus() != null && dataVo.getStatus() != 0) {
            if (dataVo.getStatus() == 1) {
                criteria.andOperator(Criteria.where("details.failPageCount").is(0).and("details.pageCount").gt(0));
            } else {
                Criteria criteriaFail = new Criteria();
                criteriaFail.orOperator(Criteria.where("details.failPageCount").is(0).and("details.pageCount").is(0),//或条件构造器
                        Criteria.where("details.failPageCount").gt(0).and("details.pageCount").gt(0));
                criteria.andOperator(criteriaFail);
            }
        }
        MatchOperation match = Aggregation.match(criteria);
        commonOperations.add(unwind);//match条件不能在unwind之前
        commonOperations.add(match);
        commonOperations.add(skip);
        commonOperations.add(limit);
        commonOperations.add(sort);
        Aggregation aggregation = Aggregation.newAggregation(commonOperations).withOptions(Aggregation.newAggregationOptions().allowDiskUse(true).build());
        AggregationResults<JSONObject> results = mongoTemplate.aggregate(aggregation, "[集合名称]", JSONObject.class);
        for (JSONObject r : results) {
            T2 param = new T2();
            JSONObject jsonObject = r.getJSONObject("details");
            Integer pageCount = jsonObject.getInteger("pageCount");
            Integer failPageCount = jsonObject.getInteger("failPageCount");
            param.setUrl(jsonObject.getString("url"));
            param.setPageCount(pageCount);
            param.setFailPageCount(failPageCount);
            param.setCreateTime(jsonObject.getString("createTime"));
            if (failPageCount == 0 && pageCount > 0) {
                param.setStatus(1);
            } else {
                param.setStatus(2);
            }
            param.setDetails(JSONUtil.toList(jsonObject.getString("details"), T3.class));
            resultList.add(param);
        }
        //手动分页计数  移除跳过和分页条件
        commonOperations.remove(skip);
        commonOperations.remove(limit);
        CountOperation count = Aggregation.count().as("count");
        commonOperations.add(count);
        Aggregation aggregation2 = Aggregation.newAggregation(commonOperations).withOptions(Aggregation.newAggregationOptions().allowDiskUse(true).build());
        AggregationResults<JSONObject> results2 = mongoTemplate.aggregate(aggregation2, "[集合名称]", JSONObject.class);
        long total = 0;
        for (JSONObject jsonObject : results2) {
            total = jsonObject.getLong("count");
        }
        // 设置分页参数
        Integer totalPage = Math.toIntExact(total / pageSize + (total % pageSize > 0 ? 1 : 0));
        CommonPage<T2> commonPage = CommonPage.restPage(resultList);
        commonPage.setPageNum(pageNum);
        commonPage.setPageSize(pageSize);
        commonPage.setTotalPage(totalPage);
        commonPage.setTotal(total);
        return commonPage;
    }

以上代码使用过程中出现以下问题:
1.sort时间排序有误,需要在分页前就进行排序,故commonOperations.add(sort);放在limit之前;
2.if动态查询条件不支持组合的条件查询,因为只创建了一个对象,多条件查询(两个或三个条件)会有误,故使用

List<Criteria> criteriaList = new ArrayList<>();//创建列表
criteriaList.add(Criteria.where("_id").is(id));//后面所有的动态条件都是这样添加
//...
Criteria criteria = new Criteria();
criteria.andOperator(criteriaList.toArray(new Criteria[0]));//转换成一个criteria
MatchOperation match = Aggregation.match(criteria);

分页数据封装

@NoArgsConstructor
public class CommonPage<T> {
    private Integer pageNum;
    private Integer pageSize;
    private Integer totalPage;
    private Long total;
    private List<T> list;
    /**
     * 将PageHelper分页后的list转为分页信息
     */
    public static <T> CommonPage<T> restPage(List<T> list) {
        CommonPage<T> result = new CommonPage<T>();
        PageInfo<T> pageInfo = new PageInfo<T>(list);
        result.setTotalPage(pageInfo.getPages());
        result.setPageNum(pageInfo.getPageNum());
        result.setPageSize(pageInfo.getPageSize());
        result.setTotal(pageInfo.getTotal());
        result.setList(pageInfo.getList());
        return result;
    }
    /**
     * 将SpringData分页后的list转为分页信息
     */
    public static <T> CommonPage<T> restPage(Page<T> pageInfo) {
        CommonPage<T> result = new CommonPage<T>();
        result.setTotalPage(pageInfo.getTotalPages());
        result.setPageNum(pageInfo.getNumber());
        result.setPageSize(pageInfo.getSize());
        result.setTotal(pageInfo.getTotalElements());
        result.setList(pageInfo.getContent());
        return result;
    }
  //get set方法 有参构造略
}

防止链接传入搜索被正则过滤

public static String escapeExprSpecialWord(String keyword) {
    String[] fbsArr = {"\\", "$", "(", ")", "*", "+", ".", "[", "]", "?", "^", "{", "}", "|"};
    for (String key : fbsArr) {
        if (keyword.contains(key)) {
            keyword = keyword.replace(key, "\\" + key);
        }
    }
    return keyword;
}

补充:

1.根据query条件更新或者修改
		Query query = new Query();
        Long collectTotal = mongoTemplate.count(query, "my_collect");
        Update update = new Update();
        update.set("name", "collectTotal");
        update.set("total", collectTotal);
        Query updateQuery = new Query();
        updateQuery.addCriteria(Criteria.where("name").is("collectTotal"));
        mongoTemplate.upsert(updateQuery, update, "collect");
2.不包含某字符查询
Query query = new Query();
query.addCriteria(Criteria.where("name").not().regex("周"));
List<YourCollection> result = mongoTemplate.find(query, YourCollection.class);
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐