Preview:
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);
        }
    }
}


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