在MongoDB中使用Spring Data MongoDB对存储为BigDecimal类型的数字值进行排序

Sorting number values stored as a BigDecimal type with Spring Data MongoDB in MongoDB

我正在通过教程(https://spring.io/guides/tutorials/data/2/)研究Spring Data MongoDB。
在本教程中,成本类型为BigDecimal,如下所示,并将其作为"字符串"存储在MongoDB中。
因此,当我尝试通过cost字段获得升序排序的结果时,我得到了错误的结果。

我发现使用BigDecimal类是进行计算的最佳方法。
但是,如果我在MongoDB中使用Spring Data MongoDB将数字保存为BigDecimal类型,
它将被保存为字符串类型
并且我会得到一个错误的排序结果,如本教程。

为了精确起见,我该怎么做才能得到正确的数字排序结果?
您能帮我解决这个问题吗?
提前非常感谢您。

例如,

1> MenuItem类

1
2
3
4
5
6
7
8
9
@Document(collection ="menu")
public class MenuItem {
  @Id
  private String id;

  @Field("itemName")
  @Indexed
  private String name;
  private BigDecimal cost;

2>创建MenuItem

的实例

1
2
3
4
5
6
7
8
9
10
MenuItem item = new MenuItem();
item.setDescription("Peanutty Noodles, perfect for every occasion.");
item.setName("Yummy Noodles");
item.setCost(new BigDecimal("52.99"));


MenuItem item = new MenuItem();
item.setDescription("Rice, Egg Fried");
item.setName("Yummy Rice");
item.setCost(new BigDecimal("211.99"));

3>排序结果

db.menu.find().sort({cost:1})

{" _id":ObjectId(" 53e982f0300475a4fbab8c32")," _class":" com.yummynoodlebar.persistence.domain.MenuItem"," itemName":" Yummy Rice"," description":" Rice,Egg Fried" ,"成分":[{"名称":"鸡蛋","描述":"鸡肉鸡蛋"},{"名称":"米饭","描述":"纯白米饭"}},"成本":" 211.99"," minutesToPrepare":0}

{" _id":ObjectId(" 53e982f0300475a4fbab8c33")," _class":" com.yummynoodlebar.persistence.domain.MenuItem"," itemName":" Yummy Rice"," description":" Rice,Egg Fried" ,"成分":[{"名称":"鸡蛋","描述":"鸡肉鸡蛋"},{"名称":"米饭","描述":"纯白米饭"}},"成本":" 211.99"," minutesToPrepare":0}

{" _id":ObjectId(" 53e982f0300475a4fbab8c2f")," _class":" com.yummynoodlebar.persistence.domain.MenuItem"," itemName":" Yummy Noodles"," description":"花生米粉,非常适合","成分":[{"名称":"花生","描述":"坚??果"},{"名称":"鸡蛋","描述":"在面条中使用"}, {" name":" Noodles"," description":" Crisp,可爱的面条"}]," cost":" 52.99"," minutesToPrepare":0}

{" _id":ObjectId(" 53e982f0300475a4fbab8c30")," _class":" com.yummynoodlebar.persistence.domain.MenuItem"," itemName":" Yummy Noodles"," description":"花生米粉,非常适合","成分":[{"名称":"花生","描述":"坚??果"},{"名称":"鸡蛋","描述":"在面条中使用"}, {" name":" Noodles"," description":" Crisp,可爱的面条"}]," cost":" 52.99"," minutesToPrepare":0}

{" _id":ObjectId(" 53e982f0300475a4fbab8c31")," _class":" com.yummynoodlebar.persistence.domain.MenuItem"," itemName":" Yummy Noodles"," description":"花生米粉,非常适合","成分":[{"名称":"鸡蛋","描述":"在面条中使用"},{"名称":"花生","描述":"坚??果"}, {" name":" Noodles"," description":" Crisp,可爱的面条"}]," cost":" 52.99"," minutesToPrepare":0}


从3.4版开始,MongoDB通过新的十进制数据类型(decimal128)添加了对" BigDecimal"的支持。但是,默认情况下,spring数据仍将Java BigDecimal映射到MongoDB String(我想这是为了向后兼容)。好消息是,可以通过在spring boot应用程序中注入CustomConversions来覆盖默认映射,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Bean
CustomConversions customConverions() {
  Converter<Decimal128, BigDecimal> decimal128ToBigDecimal = new Converter<Decimal128, BigDecimal>() {
    @Override
    public BigDecimal convert(Decimal128 s) {
      return s==null ? null : s.bigDecimalValue();
    }
  };

  Converter<BigDecimal, Decimal128> bigDecimalToDecimal128 = new Converter<BigDecimal, Decimal128>() {
    @Override
    public Decimal128 convert(BigDecimal s) {
      return s==null ? null : new Decimal128(s);
    }
  };

  return new CustomConversions(Arrays.asList(decimal128ToBigDecimal, bigDecimalToDecimal128));
}

请查看此帖子以获取更多详细信息,甚至是完整的示例。


如果您正在处理Money,并且"成本"看起来像这样,请在代码中使用Money类。钱将携带金额和货币。金钱具有将金额提取为BigDecimal等的方法。在MongoDB中将金钱坚持为丰富的形状,如下所示:

{费用:{amt:21199,ccode:" USD"}}

所有相同币种的数字运算(eq,gt,lt等)都可以正常工作。
而且,由于汇率是动态的,因此您无法在数据库中进行跨货币比较。

http://www.moschetti.org/rants/mongomoney.html


不幸的是,MongoDB本机不支持BigDecimal。这就是为什么我们默认将其转换为String的原因。

对于更多的数值处理,尤其是在处理价格或类似价格时,??我们通常建议改为存储双精度数据,然后将其转换为BigDecimal,以便直接在域对象中进行算术运算。