MongoDB复杂关联查询 spring实现

mongodb聚合查询

1.解决场景

实际工作场景如下:
1需要关联查询,具体模型如下
话题下会有动态TopicRecord,动态可以进行评论TopicComment,评论可以进行回复TopicComment,需要根据热度/时间进行查询动态,其中动态下的评论最多显示5条,评论下的回复字段需要去掉

2 当前项目中的依赖为spring-data-mongodb-2.1.19.RELEASE-sources.jar而数据库版本为4.4 导致很多操作符数据库能用但是项目中没法通过api使用比如$set $unset $slice
设计图如下:
成果截图

2.首先编写 shell执行命令

参考链接:

1.$project命令 老版本设置输出字段 包括exclude排除字段(后来未实现)
官方project命令文档

2.$set命令 添加临时字段

官方set命令文档

3.$unset命令 删除字段

官方unset命令文档

4.$splice命令 截取数组

官方splice命令文档

5.$addFileds 添加字段命令

官方addFileds命令文档

初步版本 没加分页 没进行截取
db.topic_record.aggregate([
	{
	  "$match":{
	  //对应为话题的id
		"taskId" : "5f4638cb2dca5a575181342b"
	  }
	},
	//通过$set 命令 增加新字段将ObjectId转换为string类型用来进行关联查询   topicRecord._id --> article_id (临时)--> topic_comment.recordId 被关联的评论表
	{"$set":{article_id:{"$toString":"$_id"}}},
	  { 
	  "$lookup" : 
	  { 
	  "from" : "topic_comment", 
	  "localField" : "article_id", 
	  "foreignField" : "recordId","as" : "comments"
	  }
	  },
	{ "$unset" : "comments.replies"}
]).pretty();

通过本地进行查询测试发现可以执行这样就可以上demo开搞了

3.编写demo进行调试

方式1.首先尝试用自带api进行编写

这里的坑有好几个:

  1. article_id需要将ObjectId装换为String
  2. lookup语句需要用 as(“comments”)进行赋值
  3. sort skip limit 的执行顺序需要确定好 如果顺序写反很容易出错 这里不会自动修正执行顺序
  4. 通过Aggregation.project 的 exclude 和 addExpression 方法都没有实现去除comments.replies 字段的功能,最后发现unset方法可以
  5. 这里返回数据类型TopicRecord中映射的comments列表 不能使用@Transient注解,如果添加了会映射不到

问题: 在这里使用project命令的时候发现一个问题,如果我只想排除某个字段其他字段默认全部显示,语法使用很麻烦
补充: 这里使用project也是出于老版本包中不支持addFileds这种操作 然后通过project.addExpression 方法可以进行转换 就这样保留了

    @Test
    public void testPay4() { 
        String topicId="5f4638cb2dca5a575181342b";
        PageRequest pageRequest = PageRequest.of(0, 5, Sort.by(Sort.Direction.ASC, "id"));
        //关联信息
        LookupOperation lookupOperation = LookupOperation.newLookup().from(mongoTemplate.getCollectionName(TopicComment.class))
                .localField("article_id").foreignField("recordId").as("comments");

        //计时学习推荐查询
        Aggregation aggregation1 = Aggregation.newAggregation(
                Aggregation.match(Criteria.where("taskId").is(topicId)),
                Aggregation.project("id","content","taskId","studyId","username","nickname","avatar","favour","isMaster","commentCount","createTime","imageUrls","comments").andExpression("{$toString:'$_id'}").as("article_id"),
                lookupOperation,
                Aggregation.sort(pageRequest.getSort()),
                Aggregation.skip(pageRequest.getOffset()),
                Aggregation.limit(pageRequest.getPageSize()),
                UnsetOperation.unset("comments.replies")
//                Aggregation.project("id","content","taskId","studyId","username","nickname","avatar","favour","isMaster","commentCount","createTime","imageUrls","comments").andExpression("$unset")
//                Aggregation.project("id","content","taskId","studyId","username","nickname","avatar","favour","isMaster","commentCount","createTime","imageUrls","comments").andExpression("{ comments: { replies: 0 } }")
                );
        AggregationResults<TopicRecord> aggregate = mongoTemplate.aggregate(aggregation1, TopicRecord.class, TopicRecord.class);

        System.out.println(aggregate);
    }

方式1在项目中由于不能使用UnsetOperation等方法 寻找其他方式进行编写最后优化结果如下
最终方案
方式2.

@Test
    public void testPay5() {
        //通过bson语句  使用mongo包里Aggregation原生的写法
        String topicId="5f4638cb2dca5a575181342b";
        PageRequest pageRequest = PageRequest.of(0, 5, Sort.by(Sort.Direction.ASC, "id"));
        //关联信息
        LookupOperation lookupOperation = LookupOperation.newLookup().from(mongoTemplate.getCollectionName(TopicComment.class))
                .localField("article_id").foreignField("recordId").as("comments");
        AggregationOperation unset = context -> new Document("$unset", "comments.replies");
        AggregationOperation set = context -> new Document("$set", new BasicDBObject("article_id", new BasicDBObject("$toString", "$_id")));
        BasicDBList objects = new BasicDBList();
        objects.add("$comments");
        objects.add(0);
        objects.add(1);
        AggregationOperation set2 = context -> new Document("$set", new BasicDBObject("comments", new BasicDBObject("$slice", objects)));
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.match(Criteria.where("taskId").is(topicId)),
                set,
                lookupOperation,
                Aggregation.sort(pageRequest.getSort()),
                Aggregation.skip(pageRequest.getOffset()),
                Aggregation.limit(pageRequest.getPageSize()),
                set2,
                unset
        );
        AggregationResults<TopicRecord> aggregate1 = mongoTemplate.aggregate(aggregation, TopicRecord.class, TopicRecord.class);

        System.out.println(aggregate1);

    }

其余参考链接/获得灵感:

https://www.it1352.com/1551972.html

https://www.it1352.com/987780.html

https://blog.csdn.net/qq_18769269/article/details/81011193

https://blog.csdn.net/haiyoung/article/details/80865043

https://blog.csdn.net/quietlystaywithyou/article/details/85297104

https://docs.mongodb.com/manual/reference/operator/aggregation/project/#remove-var

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值