缘由
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类型ÿ