问题概述
Java实体类 是BigDecimal类型的数据在保存到MongoDB库中之后会变为string类型,当涉及到该字段的值进行比较大小的时候就会发生问题,例如:字段price在Java实体类中是BigDecimal值为6,那么存到MongoDB后就会变为string类型的6。
Criteria.where("price").gte(new BigDecimal("7"))
上面这个结果就为true,但是下面的这样比较结果就为false,明明10>6,但是代码却认为是false,就很神奇。
Criteria.where("price").gte(new BigDecimal("10"))
为什么会发生这样的情况呢,很简单,是因为MongoDB的string 类型的比较大小的时候不是对比的值,MongoDB将字符串按UTF-8进行字典排序比较,所以结果不对。那么要解决这样的问题,有两种办法适应不同的场景。
解决办法
适用于原生的Java操作mongotemple,不适用于管道聚合查询。
Query.query(Criteria.where("$where").is(String.format("this.price <= %s", new BigDecimal("7"))));
适用于管道查询的方式:把要做判断的字段转换为int等数值类型的,然后再去match
Aggregation.project("price").andExpression("toInt(price)").as("price")
.andExclude("_id");// 去除id字段
Aggregation.match(Criteria.where("price").lte(2));
// 先把price字段转为int类型的 然后再去进行match匹配,如果有多个match的话并且数据量很大的话,可以先做一次match匹配,然后project,然后在做数值的比较match,
Aggregation aggregation = Aggregation.newAggregation(Aggregation.match(普通的criteria), group, project, Aggregation.match(数值的priceCriteria), Aggregation.sort(sort),
Aggregation.skip((long) (pageIndex() - 1) * pageSize), Aggregation.limit(pageSize));
- 最好的办法就是把string类型的数值转变为int或者double类型的,这样减少了聚合操作,因为聚合操作加上数据量特别大的话 就会非常慢,如果可以把数据刷成数值类型的是最有效的办法了。我们项目最后也是采用了刷数据类型的办法,这样解决了很大的问题。