问题描述
MongoDB
中有一个集合t_test_sort
结构如下,其中数值字段value
为字符串类型,现想按照value
的数值大小进行降序排列。
{
"_id" : ObjectId("656c87b36ca8100cd4a60348"),
"name" : "麻了",
"date" : "2033-04-05",
"value" : "-1.744353",
"_class" : "com.rkyao.spring.boot.mongo.entity.MongoSortEntity"
}
使用如下查询语句进行查询,得到的结果显然不对,这样是按照字符串进行排的,而不是数值大小
db.getCollection("t_test_sort")
.find({})
.sort({ "value": -1 })
尝试解决
网上搜索了一波解决方案都是要添加numericOrdering:true
配置,这样可以将字符串按照数值排序,但是试验后依然不行。
db.getCollection("t_test_sort")
.find({})
.sort({ "value": -1 })
.collation({"locale": "zh",numericOrdering:true})
百思不得其姐,查询MongoDB
官方文档后找到了原因,原来是numericOrdering:true
只支持非负整数,负数和小数都不支持
网上的方案行不通,只能继续研究了。
解决方案
方案一
把value
字段的类型修改为数值型,但是修改字段的影响比较大,需要重新刷数据且有可能会影响其他业务,所以除非走投无路暂不考虑。
方案二
再次查询官方文档后找到了一个方法,可以在聚合管道中使用$toDecimal
将value
字段的值转换为数值,赋给一个临时字段sortField
,然后在根据sortField
字段进行排序。
db.getCollection("t_test_sort").aggregate([
{
$addFields: {
sortField: { $toDecimal: "$value" }
}
},
{
$sort: { sortField: -1 }
}
])
可以看到查询结果完全按照sortField
数值大小降序排列,问题解决。
Java
代码也一并给出来
public List<MongoSortEntity> sort() {
List<AggregationOperation> operationList = new ArrayList<>();
operationList.add(
Aggregation.project("_id", "name", "date", "value")
.and(ConvertOperators.ToDecimal.toDecimal("$value")).as("sortField")
);
operationList.add(Aggregation.sort(Sort.by(Sort.Direction.DESC, "sortField")));
Aggregation aggregation = Aggregation.newAggregation(operationList);
AggregationResults<MongoSortEntity> results = mongoTemplate.aggregate(aggregation, 't_test_sort', MongoSortEntity.class);
return results.getMappedResults();
}
参考文档
numericOrdering
用法
https://www.mongodb.com/docs/manual/reference/collation/#std-label-numeric-order-restrictions
$toDecimal
用法
https://www.mongodb.com/docs/manual/reference/operator/aggregation/toDecimal/