package com.constellio.app.services.schemas.bulkImport; import static com.constellio.model.entities.schemas.Schemas.LEGACY_ID; import static com.constellio.model.services.search.query.logical.LogicalSearchQueryOperators.from; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import com.constellio.app.services.schemas.bulkImport.data.ImportDataProvider; import com.constellio.data.utils.KeyListMap; import com.constellio.data.utils.KeySetMap; import com.constellio.model.entities.records.Record; import com.constellio.model.entities.schemas.Metadata; import com.constellio.model.entities.schemas.MetadataSchemaType; import com.constellio.model.entities.schemas.MetadataSchemaTypes; import com.constellio.model.services.records.RecordServices; import com.constellio.model.services.search.SearchServices; import com.constellio.model.services.search.query.ReturnedMetadatasFilter; import com.constellio.model.services.search.query.logical.LogicalSearchQuery; public class ResolverCache { private static final int MAX_NUMBER_OF_RECORDS_BEFORE_LOADING_ALL_LEGACY_IDS = 1000; Map<String, Long> typesRecordsCount = new HashMap<>(); Map<String, Map<String, SchemaTypeUniqueMetadataMappingCache>> cache = new HashMap<>(); MetadataSchemaTypes types; RecordServices recordServices; SearchServices searchServices; ImportDataProvider importDataProvider; public ResolverCache(RecordServices recordServices, SearchServices searchServices, MetadataSchemaTypes types, ImportDataProvider importDataProvider) { this.recordServices = recordServices; this.types = types; this.searchServices = searchServices; this.importDataProvider = importDataProvider; } public int getCacheTotalSize() { int nbElements = 0; for (Map.Entry<String, Map<String, SchemaTypeUniqueMetadataMappingCache>> element : cache.entrySet()) { for (SchemaTypeUniqueMetadataMappingCache cache : element.getValue().values()) { nbElements += cache.getItemsCount(); } } return nbElements; } synchronized SchemaTypeUniqueMetadataMappingCache getSchemaTypeCache(String schemaType, String metadata) { if (!cache.containsKey(schemaType)) { cache.put(schemaType, new HashMap<String, SchemaTypeUniqueMetadataMappingCache>()); } Map<String, SchemaTypeUniqueMetadataMappingCache> mapping = cache.get(schemaType); if (!mapping.containsKey(metadata)) { mapping.put(metadata, new SchemaTypeUniqueMetadataMappingCache(schemaType, metadata)); } return mapping.get(metadata); } public List<MetadataSchemaType> getCachedSchemaTypes() { List<MetadataSchemaType> returnedTypes = new ArrayList<>(); for (String schemaType : cache.keySet()) { MetadataSchemaType type = types.getSchemaType(schemaType); returnedTypes.add(type); } return returnedTypes; } public void mapIds(String schemaType, String metadata, String legacyId, String id) { getSchemaTypeCache(schemaType, metadata).mapIds(legacyId, id); } public void markAsRecordInFile(String schemaType, String metadata, String legacyId) { getSchemaTypeCache(schemaType, metadata).markAsRecordInFile(legacyId); } public boolean isAvailable(String schemaType, String metadata, String legacyId) { return !getSchemaTypeCache(schemaType, metadata).recordsInFile.contains(legacyId); } public synchronized boolean isRecordUpdate(String schemaType, String legacyId) { if (!typesRecordsCount.containsKey(schemaType)) { MetadataSchemaType type = types.getSchemaType(schemaType); typesRecordsCount.put(schemaType, searchServices.getResultsCount(from(type).where(LEGACY_ID).isNotNull())); } return typesRecordsCount.get(schemaType) > 0 && getSchemaTypeCache(schemaType, LEGACY_ID.getLocalCode()).isRecordUpdate(legacyId); } public String resolve(String schemaType, String resolver) { if (resolver == null) { return null; } else { int colonIndex = resolver.indexOf(":"); if (colonIndex != -1) { String resolverMetadata = resolver.substring(0, colonIndex); String resolverValue = resolver.substring(colonIndex + 1); String id = getSchemaTypeCache(schemaType, resolverMetadata).searchMapping.get(resolverValue); if (id == null) { MetadataSchemaType type = types.getSchemaType(schemaType); Metadata metadata = type.getAllMetadatas().getMetadataWithLocalCode(resolverMetadata); Record result = recordServices.getRecordByMetadata(metadata, resolverValue); id = result == null ? null : result.getId(); getSchemaTypeCache(schemaType, resolverMetadata).mapSearch(resolver, id); } return id; } else { return getSchemaTypeCache(schemaType, LEGACY_ID.getLocalCode()).idsMapping.get(resolver); } } } public void markUniqueValueAsRequired(String schemaType, String metadata, String uniqueValue, String usedByMetadata, String usedByLegacyId) { getSchemaTypeCache(schemaType, metadata).markLegacyIdAsRequiredBy(uniqueValue, usedByMetadata, usedByLegacyId); } public KeySetMap<String, String> getUnresolvableUniqueValues(String schemaType, String metadata) { return getSchemaTypeCache(schemaType, metadata).getUnresolvableLegacyIds(); } public Set<String> getNotYetImportedLegacyIds(String schemaType) { return getSchemaTypeCache(schemaType, LEGACY_ID.getLocalCode()).recordsInFile; } public boolean isNewUniqueValue(String schemaType, String metadata, String legacyId) { return getSchemaTypeCache(schemaType, metadata).isNewLegacyId(legacyId); } class SchemaTypeUniqueMetadataMappingCache { int importDataSize = -1; String metadata; String schemaType; Map<String, String> idsMapping = new HashMap<>(); Map<String, String> searchMapping = new HashMap<>(); Set<String> recordsInFile = new HashSet<>(); KeySetMap<String, String> unresolvedLegacyIds = new KeySetMap<>(); Set<String> legacyIds = null; private SchemaTypeUniqueMetadataMappingCache(String schemaType, String metadata) { this.schemaType = schemaType; this.metadata = metadata; } public synchronized void mapIds(String legacyId, String id) { idsMapping.put(legacyId, id); recordsInFile.remove(legacyId); unresolvedLegacyIds.remove(legacyId); } public synchronized void mapSearch(String search, String id) { searchMapping.put(search, id); } public synchronized void markLegacyIdAsRequiredBy(String legacyId, String usedByMetadata, String usedByLegacyId) { if (!idsMapping.containsKey(legacyId) && !recordsInFile.contains(legacyId)) { unresolvedLegacyIds.add(legacyId, usedByMetadata + ":" + usedByLegacyId); } } public synchronized KeySetMap<String, String> getUnresolvableLegacyIds() { if (unresolvedLegacyIds.getNestedMap().size() > MAX_NUMBER_OF_RECORDS_BEFORE_LOADING_ALL_LEGACY_IDS) { LogicalSearchQuery query = new LogicalSearchQuery(); MetadataSchemaType type = types.getSchemaType(schemaType); Metadata resolverMetadata = type.getDefaultSchema().get(this.metadata); query.setCondition(from(type).where(resolverMetadata).isNotNull()); query.setReturnedMetadatas(ReturnedMetadatasFilter.onlyMetadatas(resolverMetadata)); Iterator<Record> recordsIterator = searchServices.recordsIterator(query, 10000); SchemaTypeUniqueMetadataMappingCache cache = getSchemaTypeCache(schemaType, metadata); while (recordsIterator.hasNext()) { Record record = recordsIterator.next(); cache.mapSearch(record.<String>get(resolverMetadata), record.getId()); } } for (String requiredLegacyId : new HashSet<>(unresolvedLegacyIds.getNestedMap().keySet())) { String id = resolve(schemaType, metadata + ":" + requiredLegacyId); if (id != null) { mapIds(requiredLegacyId, id); } } return new KeySetMap<>(unresolvedLegacyIds); } public synchronized void markAsRecordInFile(String legacyId) { recordsInFile.add(legacyId); unresolvedLegacyIds.remove(legacyId); } public synchronized boolean isNewLegacyId(String legacyId) { return !idsMapping.containsKey(legacyId) && !recordsInFile.contains(legacyId); } public synchronized boolean isRecordUpdate(String legacyId) { if (importDataSize == -1) { importDataSize = importDataProvider.size(schemaType); } if (importDataSize <= MAX_NUMBER_OF_RECORDS_BEFORE_LOADING_ALL_LEGACY_IDS) { MetadataSchemaType type = types.getSchemaType(schemaType); return searchServices.hasResults(from(type).where(LEGACY_ID).isEqualTo(legacyId)); } else { if (legacyIds == null) { MetadataSchemaType type = types.getSchemaType(schemaType); //List<String> ids = searchServices.searchRecordIds(from(type).where(Schemas.LEGACY_ID).isNotNull()); legacyIds = new HashSet<>(); Iterator<Record> iterators = searchServices.recordsIterator(new LogicalSearchQuery() .setCondition(from(type).where(LEGACY_ID).isNotNull()) .setReturnedMetadatas(ReturnedMetadatasFilter.onlyMetadatas(LEGACY_ID)), 5000); while (iterators.hasNext()) { String aLegacyId = iterators.next().get(LEGACY_ID); legacyIds.add(aLegacyId); } } return legacyIds.contains(legacyId); } } public int getItemsCount() { int size = 0; if (idsMapping != null) { size += idsMapping.size(); } if (searchMapping != null) { size += searchMapping.size(); } if (recordsInFile != null) { size += recordsInFile.size(); } if (unresolvedLegacyIds != null) { size += unresolvedLegacyIds.getNestedMap().size(); } if (legacyIds != null) { size += legacyIds.size(); } return size; } } }