Я ищу элегантное решение для сохранения локализованных данных в MongoDB в моем приложении Spring.
В приведенном ниже примере есть базовое c решение для сохранения локализованных данных для поля description
.
Как уже предлагалось в how-to-do-i18n-with-mongodb схема для локализованных данных:
"description": [{
"locale": "es",
"value": "someESvalue"
}, {
"locale": "en",
"value": "someENvalue"
}]
Учитывая что сущность выглядит так:
@Document(collection = "foo")
public class Foo implements Serializable {
@Id
private String id;
@Field("description")
private List<LocalizedValue> description;
// Getters and Setters are omitted in all classes
LocalizedValue содержит и локаль, и значение
public class LocalizedValue {
private Locale locale;
private String value;
И DTO выглядит так:
public class FooDTO {
private String id;
private String description;
В служба save
и обновление (или customUpdate
) обрабатываются по-разному
@Service
public class FooServiceImpl implements FooService {
public FooDTO save(FooDTO dto) {
// String is converted to List<LocalizedValue> in this step
Foo foo = entityMapper.toEntity(dto);
if (StringUtils.isBlank(foo.getId())) {
// save and update must be treated differently
foo = fooRepository.save(foo);
} else {
foo = fooRepository.customUpdate(foo);
}
Обратите внимание, что преобразователь обрабатывает преобразование из поля String description
в DTO в List<LocalizedValue> description
в entity.
На этом этапе обновление или вставка LocalizedValue
в массив description
становится проблемой. Для этого есть собственный метод репозитория.
public class FooRepositoryImpl implements FooRepositoryCustom {
private final MongoTemplate mongoTemplate;
public FooRepositoryImpl(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
public Foo customUpdate(Foo foo) {
Query query = new Query();
query.addCriteria(Criteria.where("id").is(foo.getId()).andOperator(
Criteria.where("description.locale").is(foo.getDescription().get(0).getLocale().toString())));
List<Foo> foos = mongoTemplate.find(query, Foo.class);
Update update = new Update();
UpdateResult writeResult;
// insert a new LocalizedValue or update an existing one
if (CollectionUtils.isEmpty(foos)) {
query = new Query();
query.addCriteria(Criteria.where("id").is(foo.getId()));
update.addToSet("description", foo.getDescription().get(0));
writeResult = mongoTemplate.upsert(query, update, Foo.class);
} else {
update.set("description.$.value", foo.getDescription().get(0).getValue());
writeResult = mongoTemplate.updateFirst(query, update, Foo.class);
}
// Here you may need to update manually any other fields Foo may have !
Этот пример работает, но, на мой взгляд, это довольно уродливое решение. Вам по-прежнему необходимо создать собственный репозиторий для каждой из ваших сущностей и написать (и выполнить) несколько запросов. Даже если вам удастся дополнительно оптимизировать запросы (возможно, с помощью Aggregation Framework), для каждого поля каждой сущности, которую вы можете захотеть локализовать, необходимо выполнить множество ненужных шагов.
Основная проблема заключается в управление этим массивом:
"description": [{
"locale": "es",
"value": "someESvalue"
}, {
"locale": "en",
"value": "someENvalue"
}]
Если locale
не существует, вы должны вставить новый элемент, если locale
существует, вы должны обновить его value
.
Я думаю о расширении MongoRepository
и рассмотрении этих операций в общем виде c.
Что вы думаете о i18n с MongoDB? Знаете ли вы лучшее решение?
Заранее спасибо.