Сортировка числовых значений, хранящихся как тип BigDecimal, с помощью Spring Data MongoDB в MongoDB

Я изучаю Spring Data MongoDB с помощью учебника (https://spring.io/guides/tutorials/data/2/). В этом руководстве тип стоимости - BigDecimal, как показано ниже, и он хранится как «строка» в MongoDB. Итак, когда я попытался получить результат сортировки с сортировкой по возрастанию по полю стоимости, я получил неверный результат.

Я обнаружил, что использование класса BigDecimal - лучший способ для расчетов. Но если я сохраню числа как тип BigDecimal с помощью Spring Data MongoDB в MongoDB, он будет сохранен как строковый тип, и я получу неправильный результат сортировки, как в этом руководстве.

Что я могу сделать, чтобы получить правильный результат сортировки в числах ради точности? Не могли бы вы помочь мне решить эту проблему? Заранее большое спасибо.

Например,

1> MenuItem класс

@Document(collection = "menu")
public class MenuItem {
  @Id
  private String id;

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

2> создать экземпляр MenuItem

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 ({стоимость: 1})

{"_id": ObjectId ("53e982f0300475a4fbab8c32"), "_class": "com.yummynoodlebar.persistence.domain.MenuItem", "itemName": "Вкусный рис", "description": "Рис, жареные яйца", "ингредиенты ": [{" name ":" Яйцо "," description ":" Куриные яйца "}, {" name ":" Рис "," description ":" Прямой белый рис "}]," cost ":" 211,99 " , "minutesToPrepare": 0}

{"_id": ObjectId ("53e982f0300475a4fbab8c33"), "_class": "com.yummynoodlebar.persistence.domain.MenuItem", "itemName": "Вкусный рис", "description": "Рис, жареные яйца", "ингредиенты ": [{" name ":" Яйцо "," description ":" Куриные яйца "}, {" name ":" Рис "," description ":" Пряный белый рис "}]," cost ":" 211,99 " , "minutesToPrepare": 0}

{"_id": ObjectId ("53e982f0300475a4fbab8c2f"), "_class": "com.yummynoodlebar.persistence.domain.MenuItem", "itemName": "Вкусная лапша", "description": "Лапша с арахисом, идеально подходит для любого случая. "," ингредиенты ": [{" name ":" Арахис "," description ":" Орех "}, {" name ":" Яйцо "," description ":" Используется в лапше "}, {" name ":" Лапша "," description ":" Прекрасная хрустящая лапша "}]," cost ":" 52,99 "," minutesToPrepare ": 0}

{"_id": ObjectId ("53e982f0300475a4fbab8c30"), "_class": "com.yummynoodlebar.persistence.domain.MenuItem", "itemName": "Вкусная лапша", "description": "Лапша с арахисом, идеально подходит для любого случая. "," ингредиенты ": [{" name ":" Арахис "," description ":" Орех "}, {" name ":" Яйцо "," description ":" Используется в лапше "}, {" name ":" Лапша "," description ":" Прекрасная хрустящая лапша "}]," cost ":" 52,99 "," minutesToPrepare ": 0}

{"_id": ObjectId ("53e982f0300475a4fbab8c31"), "_class": "com.yummynoodlebar.persistence.domain.MenuItem", "itemName": "Вкусная лапша", "description": "Лапша с арахисом, идеально подходит для любого случая. "," ингредиенты ": [{" name ":" Яйцо "," description ":" Используется в лапше "}, {" name ":" Арахис "," description ":" Орех "}, {" name ":" Лапша "," description ":" Прекрасная хрустящая лапша "}]," cost ":" 52,99 "," minutesToPrepare ": 0}


person Claire    schedule 12.08.2014    source источник


Ответы (3)


К сожалению, BigDecimal изначально не поддерживается MongoDB. Поэтому по умолчанию мы конвертируем его в Strings.

Для более числовой обработки, особенно если вы имеете дело с ценами и т.п., мы обычно рекомендуем вместо этого хранить двойные значения и преобразовывать их в BigDecimals для арифметических операций непосредственно в объекте домена.

person Oliver Drotbohm    schedule 12.08.2014
comment
Применяются стандартные правила точности с плавающей запятой и обратного преобразования. Вы можете это сделать, но есть некоторые предостережения. - person Buzz Moschetti; 18.05.2015

Начиная с версии 3.4, MongoDB добавила поддержку BigDecimal с помощью нового типа данных decimal (decimal128). Однако данные Spring по-прежнему сопоставляют Java BigDecimal с MongoDB string по умолчанию (я думаю, это для обратной совместимости). Хорошая новость заключается в том, что сопоставление по умолчанию можно просто переопределить, вставив CustomConversions в приложение весенней загрузки следующим образом:

@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));
}

Пожалуйста, проверьте этот пост для получения более подробной информации и даже полного пример.

person Robin    schedule 18.01.2018

Если вы имеете дело с деньгами - а «стоимость» выглядит так, - используйте в своем коде класс Money. Деньги будут содержать сумму и валюту. У Money есть методы для получения суммы в виде BigDecimal и т. Д. Сохраняйте деньги в MongoDB в виде богатой формы, например:

{cost: {amt: 21199, ccode: "USD"}}

Все числовые операции с одинаковой валютой (eq, gt, lt и т. Д.) Работают точно. И вы в любом случае не можете проводить кросс-валютное сравнение в базе данных, потому что обменный курс динамический.

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

person Buzz Moschetti    schedule 15.05.2015
comment
Мне пришлось пойти с аналогичным решением: использовать специализированный класс и хранить значения в центах. - person Cotta; 06.06.2016