MongoDB日期存储与查询、@Query、嵌套字段查询实战总结

本文详细描述了在MongoDB中使用Java和MongoRepository进行复杂查询,尤其是嵌套字段和日期范围查询的问题,以及解决方法,包括日期类型转换和单元测试验证。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

缘由

MongoDB数据库如下:
在这里插入图片描述
如上截图,使用MongoDB客户端工具DataGrip,在filter过滤框输入{ 'profiles.alias': '逆天子', 'profiles.channel': '' },即可实现昵称和渠道多个嵌套字段过滤查询。

现有业务需求:用Java代码来查询指定渠道和创建日期在指定时间区间范围内的数据。

注意到creationDate是一个一级字段(方便理解),profiles字段和creationDate属于同一级,是一个数组,而profiles.channel是一个嵌套字段。

Java应用程序查询指定渠道(通过@Query注解profiles.channel)和指定日期的数据,Dao层(或叫Repository层)接口Interface代码如下:

import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;

@Repository
public interface AccountRepository extends MongoRepository<Account, String> {
   
	@Query("{ 'profiles.channel': ?0 }")
	List<Account> findByProfileChannelAndCreationDateBetween(String channel, Date start, Date end);
}

单元测试代码如下:

@Test
public void testFindByProfileChannelAndCreationDateBetween() {
   
    String time = "2024-01-21";
    String startTime = time + DateUtils.DAY_START;
    String endTime = time + DateUtils.DAY_END;
    Date start = new Date();
    Date end = new Date();
    try {
   
        start = DateUtils.parseThenUtc(startTime);
        end = DateUtils.parseThenUtc(endTime);
    } catch (ParseException e) {
   
        log.error("test failed: {}", e.getMessage());
    }
    List<Account> accountList = accountRepository.findByProfileChannelAndCreationDateBetween(ChannelEnum.DATONG_APP.getChannelCode(), start, end);
    log.info("size:{}", accountList.size());
}

输出如下:size:70829

没有报错,但是并不能说明没有问题。根据自己对于业务的理解,数据量显然不对劲,此渠道的全量数据是这么多才差不多。

也就是说,上面的Interface接口查询方法,只有渠道条件生效,日期没有生效??

至于为什么没有生效,请继续往下看。想看结论的直接翻到文末。

排查

不生效

MongoRepository是Spring Data MongoDB提供的,继承MongoRepository之后,就可以使用IDEA的智能提示快速编写查询方法。如下图所示:
在这里插入图片描述
但是:上面的这种方式只能对一级字段生效。如果想要过滤查询嵌套字段,则派不上用场。

此时,需要使用一个更强大的@Query注解。

但是,@Query和JPA方式不能一起使用。也就是上面的方法findByProfileChannelAndCreationDateBetween查询方法,经过简化后只保留一级字段,然后嵌套字段使用@Query方式:

@Query("{ 'profiles.channel': ?0 }")
List<Account> findByCreationDateBetween(String channel, Date s1, Date s2);

依旧是不生效的。

版本1

基于上面的结论,有一版新的写法:

@Query("{ 'profiles.channel': ?0, 'creationDate': {$gte: ?1, $lte: ?2} }")
List<Account> findByChannelAndCreationDate(String channel, Date start, Date end);

此时输出:size:28。这个数据看起来才比较正常(虽然后面的结论证明不是正确的)。

WARN告警

如果不过滤渠道呢?查询某个日期时间段内所有渠道的全量用户数据?

两种写法都可以:

long countByCreationDateBetween(Date start, Date end);

@Query("{ 'creationDate': {$gte: ?0, $lte: ?1} }")
long countByCreationDate(Date start, Date end);

等等。怎么第一种写法,IDEA给出一个WARN??
在这里插入图片描述

MongoDB日期

上面IDEA给出的Warning显而易见。因为MongoDB数据库字段定义是Instant类型ÿ

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

johnny233

晚饭能不能加鸡腿就靠你了

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值