package com.til.tcc.manager.mongo.service; import com.til.tcc.manager.constants.Constants; import com.til.tcc.manager.mongo.entity.AufsConfigTranslationEntity; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Sort; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Update; import org.springframework.stereotype.Service; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; @Service @Slf4j public class AufsConfigTranslationService { @Autowired private BaseMongoService baseMongoService; @Autowired private MongoTemplate mongoTemplate; public Map<String, Map<Integer, List<AufsConfigTranslationEntity>>> getTranslationConfigs(String pc) { if (pc == null) { throw new IllegalArgumentException("Parameter 'pc' cannot be null"); } try { if(StringUtils.equalsIgnoreCase(pc, "eis")){ pc = Constants.pc.es.name(); }else if (StringUtils.equalsIgnoreCase(pc, "tml")){ pc = Constants.pc.tamilsamayam.name(); }else if (StringUtils.equalsIgnoreCase(pc, "mly")){ pc = Constants.pc.malayalamsamayam.name(); }else if (StringUtils.equalsIgnoreCase(pc, "tlg")){ pc = Constants.pc.telugusamayam.name(); } Query query = new Query(); query.addCriteria(Criteria.where(AufsConfigTranslationEntity.PC).is(pc)); query.addCriteria(Criteria.where(AufsConfigTranslationEntity.ENABLE).is(true)); List<AufsConfigTranslationEntity> list = baseMongoService.find(query, AufsConfigTranslationEntity.class); list.forEach(x -> x.setRootTrans(null)); return list.stream() .collect(Collectors.groupingBy(AufsConfigTranslationEntity::getPfm, Collectors.groupingBy(AufsConfigTranslationEntity::getFv, Collectors.toList()))); } catch (Exception e) { log.error("Exception occurred for pc: {} ", pc, e); throw new RuntimeException("Failed to retrieve translation configs", e); } } public AufsConfigTranslationEntity getById(String id) { try { Query query = new Query(); query.addCriteria(Criteria.where(AufsConfigTranslationEntity.ID).is(id)); return baseMongoService.findOne(query, AufsConfigTranslationEntity.class); } catch (Exception e) { log.error("Exception occurred for {} ", id, e); throw new RuntimeException("Failed to retrieve translation config", e); } } public int updateById(String id, AufsConfigTranslationEntity updatedEntity) { try { Query query = new Query(Criteria.where("_id").is(id)); Update update = new Update(); if(StringUtils.isNotBlank(updatedEntity.getPfm())){ update.set("pfm", updatedEntity.getPfm()); } if(StringUtils.isNotBlank(updatedEntity.getPc())){ update.set("pc", updatedEntity.getPc()); } if(Objects.nonNull(updatedEntity.getFv())){ update.set("fv", updatedEntity.getFv()); } if(Objects.nonNull(updatedEntity.getEnable())){ update.set("enable", updatedEntity.getEnable()); } if(updatedEntity.getRootTrans() != null){ update.set("rootTrans", updatedEntity.getRootTrans()); } return baseMongoService.updateOne(query, update, AufsConfigTranslationEntity.class); } catch (Exception e) { log.error("Exception occurred for {} ", id, e); throw new RuntimeException("Failed to retrieve translation config", e); } } public void updateTranslations(String client, String pfm, Integer fv, Map<String, Object> updates, Map<String, Object> removals) { // 1. Get target feed version (use specified or latest) Integer targetFv = (fv != null) ? fv : getLatestFv(client, pfm); if (targetFv == null) { throw new RuntimeException("No translations found for client: " + client + ", platform: " + pfm); } // 2. Update the specified document updateSpecificDocument(client, pfm, targetFv, updates, removals); // 3. Update other language documents (pc ≠ "toi") updateOtherLanguageDocuments(client, pfm, updates, removals); } private void updateSpecificDocument(String client, String pfm, Integer fv, Map<String, Object> updates, Map<String, Object> removals) { Query query = Query.query(Criteria.where("client").is(client) .and("pfm").is(pfm) .and("fv").is(fv)); AufsConfigTranslationEntity doc = mongoTemplate.findOne(query, AufsConfigTranslationEntity.class); if (doc == null) { doc = new AufsConfigTranslationEntity(); doc.setClient(client); doc.setPfm(pfm); doc.setFv(fv); doc.setRootTrans(new HashMap<>()); } Map<String, Object> rootTrans = doc.getRootTrans() != null ? new HashMap<>(doc.getRootTrans()) : new HashMap<>(); // Apply updates and removals with deep merge applyUpdatesAndRemovals(rootTrans, updates, removals); doc.setRootTrans(rootTrans); mongoTemplate.save(doc); } private void updateOtherLanguageDocuments(String client, String pfm, Map<String, Object> updates, Map<String, Object> removals) { Query query = new Query(Criteria.where("client").is(client) .and("pfm").is(pfm) .and("pc").ne("toi")); query.with(new Sort(Sort.Direction.DESC, "fv")); List<AufsConfigTranslationEntity> allLangDocs = mongoTemplate.find(query, AufsConfigTranslationEntity.class); // Keep only latest fv per unique pc Map<String, AufsConfigTranslationEntity> latestPerPc = allLangDocs.stream() .collect(Collectors.toMap( AufsConfigTranslationEntity::getPc, Function.identity(), (doc1, doc2) -> doc1.getFv() >= doc2.getFv() ? doc1 : doc2 )); int updateCount = 0; for (AufsConfigTranslationEntity doc : latestPerPc.values()) { Map<String, Object> rootTrans = doc.getRootTrans() != null ? new HashMap<>(doc.getRootTrans()) : new HashMap<>(); // Apply updates and removals with deep merge applyUpdatesAndRemovals(rootTrans, updates, removals); doc.setRootTrans(rootTrans); mongoTemplate.save(doc); updateCount++; } System.out.println("Updated " + updateCount + " other language documents."); } private Integer getLatestFv(String client, String pfm) { Query query = new Query(Criteria.where("client").is(client) .and("pfm").is(pfm)) .with(new Sort(Sort.Direction.DESC, "fv")) .limit(1); AufsConfigTranslationEntity doc = mongoTemplate.findOne(query, AufsConfigTranslationEntity.class); return doc != null ? doc.getFv() : null; } //Apply updates and then apply removals to the root translation map with deep merge support @SuppressWarnings("unchecked") private void applyUpdatesAndRemovals(Map<String, Object> rootTrans, Map<String, Object> updates, Map<String, Object> removals) { // Apply updates with deep merge if (updates != null && !updates.isEmpty()) { // First unflatten dotted keys into a nested structure Map<String, Object> structuredUpdates = unflattenUpdates(updates); // Then merge with existing data deepMerge(rootTrans, structuredUpdates); } // Apply removals if (removals != null && !removals.isEmpty()) { for (Map.Entry<String, Object> entry : removals.entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); // Handle removal based on key structure if (key.contains(".")) { // For dot notation keys, handle nested removal removeNestedMapValue(rootTrans, key, value); } else { // For top-level keys if (value == null) { // Remove entire key if value is null rootTrans.remove(key); } else if (value instanceof Map) { // Handle nested map removal handleNestedMapRemoval(rootTrans, key, (Map<String, Object>) value); } else { // Remove key only if value matches exactly Object currentValue = rootTrans.get(key); if (currentValue != null && currentValue.equals(value)) { rootTrans.remove(key); } } } } } } //Convert dotted notation keys to nested structure @SuppressWarnings("unchecked") private Map<String, Object> unflattenUpdates(Map<String, Object> updates) { Map<String, Object> result = new HashMap<>(); for (Map.Entry<String, Object> entry : updates.entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); if (key.contains(".")) { String[] parts = key.split("\\."); Map<String, Object> current = result; // Navigate to the correct level for (int i = 0; i < parts.length - 1; i++) { if (!current.containsKey(parts[i])) { current.put(parts[i], new HashMap<>()); } Object next = current.get(parts[i]); if (!(next instanceof Map)) { // Override if not a map Map<String, Object> newMap = new HashMap<>(); current.put(parts[i], newMap); current = newMap; } else { current = (Map<String, Object>) next; } } // Set the value at the deepest level current.put(parts[parts.length - 1], value); } else { // Handle top-level keys normally result.put(key, value); } } return result; } //Deep merge two maps - preserves existing values when not explicitly overwritten @SuppressWarnings("unchecked") private void deepMerge(Map<String, Object> target, Map<String, Object> source) { if (source == null) { return; } for (Map.Entry<String, Object> entry : source.entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); // If both have a map for the same key, merge them if (value instanceof Map && target.containsKey(key) && target.get(key) instanceof Map) { deepMerge((Map<String, Object>) target.get(key), (Map<String, Object>) value); } else { // Otherwise just overwrite/add the value target.put(key, value); } } } //Handle removal of nested values in a map structure @SuppressWarnings("unchecked") private void handleNestedMapRemoval(Map<String, Object> rootMap, String key, Map<String, Object> valueToRemove) { // If the key doesn't exist in root map, nothing to remove if (!rootMap.containsKey(key)) { return; } Object currentValue = rootMap.get(key); // If current value is not a map, but removal specifies a map, something is wrong if (!(currentValue instanceof Map)) { return; } Map<String, Object> currentMap = (Map<String, Object>) currentValue; // If removal map is empty, remove the entire key if (valueToRemove.isEmpty()) { rootMap.remove(key); return; } // For each entry in the removal map, apply removal logic for (Map.Entry<String, Object> entry : valueToRemove.entrySet()) { String nestedKey = entry.getKey(); Object nestedValue = entry.getValue(); if (!currentMap.containsKey(nestedKey)) { continue; } Object existingNestedValue = currentMap.get(nestedKey); if (nestedValue == null) { // Remove entire nested key if value is null currentMap.remove(nestedKey); } else if (nestedValue instanceof Map && existingNestedValue instanceof Map) { // Recursively handle nested map removal handleNestedMapRemoval(currentMap, nestedKey, (Map<String, Object>) nestedValue); // If the nested map is now empty, remove it if (((Map<?, ?>) existingNestedValue).isEmpty()) { currentMap.remove(nestedKey); } } else if (existingNestedValue != null && existingNestedValue.equals(nestedValue)) { // Remove if values match exactly currentMap.remove(nestedKey); } } // If the map is now empty after removals, remove the parent key if (currentMap.isEmpty()) { rootMap.remove(key); } } //Remove a value from a nested map structure using dot notation @SuppressWarnings("unchecked") private void removeNestedMapValue(Map<String, Object> rootMap, String dottedKey, Object valueToRemove) { String[] keyParts = dottedKey.split("\\."); // Navigate to the parent map that contains the final key Map<String, Object> currentMap = rootMap; for (int i = 0; i < keyParts.length - 1; i++) { Object value = currentMap.get(keyParts[i]); if (!(value instanceof Map)) { // Path doesn't exist, nothing to remove return; } currentMap = (Map<String, Object>) value; } String finalKey = keyParts[keyParts.length - 1]; // Handle removal based on value type if (!currentMap.containsKey(finalKey)) { return; } Object currentValue = currentMap.get(finalKey); if (valueToRemove == null) { // Remove entire key if value to remove is null currentMap.remove(finalKey); } else if (valueToRemove instanceof Map && currentValue instanceof Map) { // Handle nested map removal handleNestedMapRemoval(currentMap, finalKey, (Map<String, Object>) valueToRemove); } else if (currentValue != null && currentValue.equals(valueToRemove)) { // Remove if values match exactly currentMap.remove(finalKey); } // Check if parent maps should be removed (if they're empty) if (keyParts.length > 1 && currentMap.isEmpty()) { // Remove empty parent maps recursively removeEmptyParentMaps(rootMap, keyParts, 0, keyParts.length - 1); } } //Recursively remove empty parent maps @SuppressWarnings("unchecked") private void removeEmptyParentMaps(Map<String, Object> rootMap, String[] keyParts, int startIdx, int endIdx) { if (startIdx >= endIdx) { return; } // Build the path to the current map Map<String, Object> currentMap = rootMap; for (int i = 0; i < endIdx - 1; i++) { Object value = currentMap.get(keyParts[i]); if (!(value instanceof Map)) { return; } currentMap = (Map<String, Object>) value; } // Check if the map at the path is empty Object value = currentMap.get(keyParts[endIdx - 1]); if (value instanceof Map && ((Map<?, ?>) value).isEmpty()) { currentMap.remove(keyParts[endIdx - 1]); //Recursively check parent removeEmptyParentMaps(rootMap, keyParts, startIdx, endIdx - 1); } } }
Preview:
downloadDownload PNG
downloadDownload JPEG
downloadDownload SVG
Tip: You can change the style, width & colours of the snippet with the inspect tool before clicking Download!
Click to optimize width for Twitter