From deea6ca4df0018d31cacc2dcbfb635a596337d29 Mon Sep 17 00:00:00 2001 From: Rustam_Lotsmanenko <Rustam_Lotsmanenko@epam.com> Date: Fri, 26 Mar 2021 14:55:16 +0400 Subject: [PATCH] GONRG-2028 Objects array Changes: Added inner properties processing for Arrays of objects --- indexer-core/pom.xml | 2 +- .../schema/converter/PropertiesProcessor.java | 71 ++++- .../config/SchemaConverterConfig.java | 2 + .../SchemaConverterPropertiesConfig.java | 23 +- .../indexer/schema/converter/tags/Items.java | 2 + .../schema/converter/tags/TypeProperty.java | 5 +- .../service/AttributeParsingServiceImpl.java | 22 +- .../service/IAttributeParsingService.java | 2 + .../service/IndexSchemaServiceImpl.java | 43 +-- .../service/IndexerMappingServiceImpl.java | 16 +- .../service/StorageIndexerPayloadMapper.java | 258 +++++++++++------- .../osdu/indexer/util/TypeMapper.java | 44 ++- .../StorageIndexerPayloadMapperTest.java | 140 ++++++++++ pom.xml | 2 +- .../service/IndexerMappingServiceTest.java | 2 +- .../service/IndexerMappingServiceTest.java | 2 +- .../service/IndexerMappingServiceTest.java | 2 +- 17 files changed, 478 insertions(+), 160 deletions(-) create mode 100644 indexer-core/src/test/java/org/opengroup/osdu/indexer/service/StorageIndexerPayloadMapperTest.java diff --git a/indexer-core/pom.xml b/indexer-core/pom.xml index 49ce44c6a..587bd5619 100644 --- a/indexer-core/pom.xml +++ b/indexer-core/pom.xml @@ -16,7 +16,7 @@ <properties> <commons-beanutils.version>1.9.4</commons-beanutils.version> - <osdu.oscorecommon.version>0.6.9</osdu.oscorecommon.version> + <osdu.oscorecommon.version>0.8.1-SNAPSHOT</osdu.oscorecommon.version> </properties> <dependencies> diff --git a/indexer-core/src/main/java/org/opengroup/osdu/indexer/schema/converter/PropertiesProcessor.java b/indexer-core/src/main/java/org/opengroup/osdu/indexer/schema/converter/PropertiesProcessor.java index 57c2cae2c..52e171dfa 100644 --- a/indexer-core/src/main/java/org/opengroup/osdu/indexer/schema/converter/PropertiesProcessor.java +++ b/indexer-core/src/main/java/org/opengroup/osdu/indexer/schema/converter/PropertiesProcessor.java @@ -14,7 +14,16 @@ package org.opengroup.osdu.indexer.schema.converter; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.http.HttpStatus; +import org.opengroup.osdu.core.common.Constants; import org.opengroup.osdu.core.common.logging.JaxRsDpsLog; import org.opengroup.osdu.core.common.model.http.AppException; import org.opengroup.osdu.core.common.search.Preconditions; @@ -23,12 +32,9 @@ import org.opengroup.osdu.indexer.schema.converter.config.SchemaConverterPropert import org.opengroup.osdu.indexer.schema.converter.tags.AllOfItem; import org.opengroup.osdu.indexer.schema.converter.tags.Definition; import org.opengroup.osdu.indexer.schema.converter.tags.Definitions; +import org.opengroup.osdu.indexer.schema.converter.tags.Items; import org.opengroup.osdu.indexer.schema.converter.tags.TypeProperty; -import java.util.*; -import java.util.function.Supplier; -import java.util.stream.Stream; - public class PropertiesProcessor { private JaxRsDpsLog log; @@ -151,6 +157,7 @@ public class PropertiesProcessor { private Stream<Map<String, Object>> processPropertyEntry(Map.Entry<String, TypeProperty> entry) { Preconditions.checkNotNull(entry, "entry cannot be null"); + if ("object".equals(entry.getValue().getType()) && Objects.isNull(entry.getValue().getItems()) && Objects.isNull(entry.getValue().getRef()) @@ -159,7 +166,46 @@ public class PropertiesProcessor { } if ("array".equals(entry.getValue().getType())) { - if (schemaConverterConfig.getSupportedArrayTypes().contains(entry.getValue().getItems().getType())) { + + Items items = entry.getValue().getItems(); + + if(Objects.nonNull(items.getProperties()) && !items.getProperties().isEmpty()){ + String indexingType = getFromIndexingType(entry.getValue().getIndexingType()); + /*Schema item inner properties will be processed if they are present & indexingType in schema configured for processing + result ex: + { + path = ArrayItem, + kind = nested, + properties = [{ + path = InnerProperty, + kind = double + }, { + path = OtherInnerProperty, + kind = string + } + ] + } + */ + if(schemaConverterConfig.getProcessedArraysTypes().contains(indexingType)){ + PropertiesProcessor propertiesProcessor = new PropertiesProcessor(definitions, log, new SchemaConverterPropertiesConfig()); + return storageSchemaObjectArrayEntry( + indexingType, + entry.getKey(), + items.getProperties().entrySet().stream().flatMap(propertiesProcessor::processPropertyEntry)); + + /*Otherwise inner properties won't be processed + result ex: + { + path = ArrayItem, + kind = []object + } + */ + }else { + return storageSchemaEntry(indexingType, pathPrefixWithDot + entry.getKey()); + } + } + + if (schemaConverterConfig.getSupportedArrayTypes().contains(items.getType()) && Objects.isNull(items.getProperties())) { return storageSchemaEntry("[]" + getTypeByDefinitionProperty(entry.getValue()), pathPrefixWithDot + entry.getKey()); } @@ -225,6 +271,17 @@ public class PropertiesProcessor { return Stream.of(map); } + private Stream<Map<String, Object>> storageSchemaObjectArrayEntry(String kind, String path,Stream<Map<String, Object>> mapStream) { + Preconditions.checkNotNullOrEmpty(kind, "kind cannot be null or empty"); + Preconditions.checkNotNullOrEmpty(path, "path cannot be null or empty"); + + Map<String, Object> map = new HashMap<>(); + map.put("kind", kind); + map.put("path", path); + map.put(Constants.PROPERTIES,mapStream.collect(Collectors.toList())); + return Stream.of(map); + } + private String getTypeByDefinitionProperty(TypeProperty definitionProperty) { Preconditions.checkNotNull(definitionProperty, "definitionProperty cannot be null"); @@ -270,4 +327,8 @@ public class PropertiesProcessor { }; } + private String getFromIndexingType(String indexingType) { + return schemaConverterConfig.getArraysTypesMap().getOrDefault(indexingType, "[]object"); + } + } diff --git a/indexer-core/src/main/java/org/opengroup/osdu/indexer/schema/converter/config/SchemaConverterConfig.java b/indexer-core/src/main/java/org/opengroup/osdu/indexer/schema/converter/config/SchemaConverterConfig.java index 539b2bdfd..5e945aa52 100644 --- a/indexer-core/src/main/java/org/opengroup/osdu/indexer/schema/converter/config/SchemaConverterConfig.java +++ b/indexer-core/src/main/java/org/opengroup/osdu/indexer/schema/converter/config/SchemaConverterConfig.java @@ -11,4 +11,6 @@ public interface SchemaConverterConfig { Set<String> getSupportedArrayTypes(); Map<String, String> getSpecialDefinitionsMap(); Map<String, String> getPrimitiveTypesMap(); + Map<String, String> getArraysTypesMap(); + Set<String> getProcessedArraysTypes(); } diff --git a/indexer-core/src/main/java/org/opengroup/osdu/indexer/schema/converter/config/SchemaConverterPropertiesConfig.java b/indexer-core/src/main/java/org/opengroup/osdu/indexer/schema/converter/config/SchemaConverterPropertiesConfig.java index 9e5eaff44..335118529 100644 --- a/indexer-core/src/main/java/org/opengroup/osdu/indexer/schema/converter/config/SchemaConverterPropertiesConfig.java +++ b/indexer-core/src/main/java/org/opengroup/osdu/indexer/schema/converter/config/SchemaConverterPropertiesConfig.java @@ -1,12 +1,15 @@ package org.opengroup.osdu.indexer.schema.converter.config; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; -import java.util.*; - @Configuration @ConfigurationProperties(prefix = "schema.converter") @Getter @@ -17,6 +20,10 @@ public class SchemaConverterPropertiesConfig implements SchemaConverterConfig { private Set<String> supportedArrayTypes = getDefaultSupportedArrayTypes(); private Map<String, String> specialDefinitionsMap = getDefaultSpecialDefinitionsMap(); private Map<String, String> primitiveTypesMap = getDefaultPrimitiveTypesMap(); + private Map<String, String> arraysTypesMap = getDefaultArraysTypesMap(); + private Set<String> processedArraysTypes = getDefaultArraysTypesForProcessing(); + + private Set<String> getDefaultSkippedDefinitions() { return new HashSet<>(Arrays.asList("AbstractAnyCrsFeatureCollection", @@ -51,4 +58,16 @@ public class SchemaConverterPropertiesConfig implements SchemaConverterConfig { return defaultPrimitiveTypesMap; } + + private Map<String, String> getDefaultArraysTypesMap() { + Map<String, String> defaultArrayTypesMap = new HashMap<>(); + defaultArrayTypesMap.put("x-type-object","[]object"); + defaultArrayTypesMap.put("x-type-flattened","flattened"); + defaultArrayTypesMap.put("x-type-nested","nested"); + return defaultArrayTypesMap; + } + + private Set<String> getDefaultArraysTypesForProcessing(){ + return new HashSet<>(Arrays.asList("nested")); + } } diff --git a/indexer-core/src/main/java/org/opengroup/osdu/indexer/schema/converter/tags/Items.java b/indexer-core/src/main/java/org/opengroup/osdu/indexer/schema/converter/tags/Items.java index dc3ef69e9..21721ccad 100644 --- a/indexer-core/src/main/java/org/opengroup/osdu/indexer/schema/converter/tags/Items.java +++ b/indexer-core/src/main/java/org/opengroup/osdu/indexer/schema/converter/tags/Items.java @@ -14,10 +14,12 @@ package org.opengroup.osdu.indexer.schema.converter.tags; +import java.util.Map; import lombok.Data; @Data public class Items { private String type; private String pattern; + private Map<String, TypeProperty> properties; } diff --git a/indexer-core/src/main/java/org/opengroup/osdu/indexer/schema/converter/tags/TypeProperty.java b/indexer-core/src/main/java/org/opengroup/osdu/indexer/schema/converter/tags/TypeProperty.java index b929bad43..af9ec6bd7 100644 --- a/indexer-core/src/main/java/org/opengroup/osdu/indexer/schema/converter/tags/TypeProperty.java +++ b/indexer-core/src/main/java/org/opengroup/osdu/indexer/schema/converter/tags/TypeProperty.java @@ -15,13 +15,14 @@ package org.opengroup.osdu.indexer.schema.converter.tags; import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.Data; - import java.util.List; import java.util.Map; +import lombok.Data; @Data public class TypeProperty { + @JsonProperty("x-osdu-indexing") + private String indexingType; private String type; private String pattern; private String format; diff --git a/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/AttributeParsingServiceImpl.java b/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/AttributeParsingServiceImpl.java index 733408ab3..66d471869 100644 --- a/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/AttributeParsingServiceImpl.java +++ b/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/AttributeParsingServiceImpl.java @@ -19,13 +19,18 @@ import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; import com.google.gson.internal.LinkedTreeMap; import com.google.gson.reflect.TypeToken; +import java.lang.reflect.Array; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.BiFunction; +import javax.inject.Inject; import org.apache.http.HttpStatus; import org.opengroup.osdu.core.common.model.indexer.ElasticType; -import org.opengroup.osdu.core.common.model.indexer.IndexSchema; import org.opengroup.osdu.core.common.model.indexer.IndexingStatus; import org.opengroup.osdu.core.common.model.indexer.JobStatus; -import org.opengroup.osdu.core.common.Constants; -import org.opengroup.osdu.indexer.model.geojson.FeatureCollection; import org.opengroup.osdu.indexer.util.parser.BooleanParser; import org.opengroup.osdu.indexer.util.parser.DateTimeParser; import org.opengroup.osdu.indexer.util.parser.GeoShapeParser; @@ -33,12 +38,6 @@ import org.opengroup.osdu.indexer.util.parser.NumberParser; import org.springframework.stereotype.Service; import org.springframework.web.context.annotation.RequestScope; -import javax.inject.Inject; -import java.lang.reflect.Array; -import java.lang.reflect.Type; -import java.util.*; -import java.util.function.BiFunction; - @Service @RequestScope public class AttributeParsingServiceImpl implements IAttributeParsingService { @@ -219,6 +218,11 @@ public class AttributeParsingServiceImpl implements IAttributeParsingService { dataMap.put(name,value); } + @Override + public void tryParseFlattened(String recordId, String name, Object value, Map<String, Object> dataMap) { + dataMap.put(name,value); + } + private List<String> isArrayType(Object attributeVal) { try { diff --git a/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/IAttributeParsingService.java b/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/IAttributeParsingService.java index c4fb8ecf2..a658cc6a1 100644 --- a/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/IAttributeParsingService.java +++ b/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/IAttributeParsingService.java @@ -28,4 +28,6 @@ public interface IAttributeParsingService { void tryParseNested(String recordId, String name, Object value, Map<String, Object> dataMap); void tryParseObject(String recordId, String name, Object value, Map<String, Object> dataMap); + + void tryParseFlattened(String recordId, String name, Object value, Map<String, Object> dataMap); } diff --git a/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/IndexSchemaServiceImpl.java b/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/IndexSchemaServiceImpl.java index a63848140..093c5d402 100644 --- a/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/IndexSchemaServiceImpl.java +++ b/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/IndexSchemaServiceImpl.java @@ -16,6 +16,12 @@ package org.opengroup.osdu.indexer.service; import com.google.common.base.Strings; import com.google.gson.Gson; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; +import javax.inject.Inject; import org.apache.http.HttpStatus; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchStatusException; @@ -23,7 +29,6 @@ import org.elasticsearch.client.RestHighLevelClient; import org.opengroup.osdu.core.common.logging.JaxRsDpsLog; import org.opengroup.osdu.core.common.model.http.AppException; import org.opengroup.osdu.core.common.model.http.RequestStatus; -import org.opengroup.osdu.core.common.model.indexer.ElasticType; import org.opengroup.osdu.core.common.model.indexer.IndexSchema; import org.opengroup.osdu.core.common.model.indexer.OperationType; import org.opengroup.osdu.core.common.model.indexer.StorageType; @@ -37,13 +42,6 @@ import org.opengroup.osdu.indexer.util.ElasticClientHandler; import org.opengroup.osdu.indexer.util.TypeMapper; import org.springframework.stereotype.Service; -import javax.inject.Inject; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URISyntaxException; -import java.util.HashMap; -import java.util.Map; - @Service public class IndexSchemaServiceImpl implements IndexSchemaService { @@ -204,16 +202,20 @@ public class IndexSchemaServiceImpl implements IndexSchemaService { if (schemaObj == null) return null; - Map<String, String> data = new HashMap<>(); + Map<String, Object> data = new HashMap<>(); Map<String, Object> meta = new HashMap<>(); if (schemaObj.getSchema() != null && schemaObj.getSchema().length > 0) { for (SchemaItem schemaItem : schemaObj.getSchema()) { String dataType = schemaItem.getKind(); - String elasticDataType = TypeMapper.getIndexerType(dataType); + Object elasticDataType = TypeMapper.getIndexerType(dataType); if (elasticDataType == null) { elasticDataType = TypeMapper.getIndexerType(StorageType.STRING.getValue()); } + if(schemaItem.getProperties() != null){ + HashMap<String, Object> propertiesMap = normalizeInnerProperties(schemaItem); + elasticDataType = TypeMapper.getObjectsArrayMapping(dataType, propertiesMap); + } data.put(schemaItem.getPath(), elasticDataType); } } @@ -234,14 +236,6 @@ public class IndexSchemaServiceImpl implements IndexSchemaService { String kind = schemaObj.getKind(); String type = kind.split(":")[2]; - //TODO temporary fix for https://community.opengroup.org/osdu/platform/system/indexer-service/-/issues/1 - if(data.get(MARKERS) != null){ - data.put(MARKERS, ElasticType.NESTED.getValue()); - } - if(data.get(CURVES) != null){ - data.put(CURVES, ElasticType.NESTED.getValue()); - } - return IndexSchema.builder().dataSchema(data).metaSchema(meta).kind(kind).type(type).build(); } catch (Exception e) { @@ -249,4 +243,17 @@ public class IndexSchemaServiceImpl implements IndexSchemaService { } } + private HashMap<String, Object> normalizeInnerProperties(SchemaItem schemaItem) { + HashMap<String, Object> propertiesMap = new HashMap<>(); + for (SchemaItem propertiesItem : schemaItem.getProperties()) { + String propertiesItemKind = propertiesItem.getKind(); + String propertiesElasticType = TypeMapper.getIndexerType(propertiesItemKind); + if (propertiesElasticType == null) { + propertiesElasticType = TypeMapper.getIndexerType(StorageType.STRING.getValue()); + } + propertiesMap.put(propertiesItem.getPath(),propertiesElasticType); + } + return propertiesMap; + } + } \ No newline at end of file diff --git a/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/IndexerMappingServiceImpl.java b/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/IndexerMappingServiceImpl.java index acd87b30e..b70bb211f 100644 --- a/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/IndexerMappingServiceImpl.java +++ b/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/IndexerMappingServiceImpl.java @@ -15,6 +15,12 @@ package org.opengroup.osdu.indexer.service; import com.google.gson.Gson; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import javax.inject.Inject; import org.apache.http.HttpStatus; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.action.support.master.AcknowledgedResponse; @@ -38,13 +44,6 @@ import org.opengroup.osdu.indexer.util.ElasticClientHandler; import org.opengroup.osdu.indexer.util.TypeMapper; import org.springframework.stereotype.Service; -import javax.inject.Inject; -import java.io.IOException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - @Service public class IndexerMappingServiceImpl extends MappingServiceImpl implements IndexerMappingService { @@ -112,7 +111,7 @@ public class IndexerMappingServiceImpl extends MappingServiceImpl implements Ind // data-source attributes Map<String, Object> dataMapping = new HashMap<>(); if (schema.getDataSchema() != null) { - for (Map.Entry<String, String> entry : schema.getDataSchema().entrySet()) { + for (Map.Entry<String, Object> entry : schema.getDataSchema().entrySet()) { dataMapping.put(entry.getKey(), TypeMapper.getDataAttributeIndexerMapping(entry.getValue())); } @@ -131,7 +130,6 @@ public class IndexerMappingServiceImpl extends MappingServiceImpl implements Ind // don't add dynamic mapping documentMapping.put("dynamic", false); - return documentMapping; } diff --git a/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/StorageIndexerPayloadMapper.java b/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/StorageIndexerPayloadMapper.java index 6d9d8b8cd..59fb3ecdf 100644 --- a/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/StorageIndexerPayloadMapper.java +++ b/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/StorageIndexerPayloadMapper.java @@ -1,115 +1,173 @@ package org.opengroup.osdu.indexer.service; +import static org.opengroup.osdu.indexer.service.IAttributeParsingService.DATA_GEOJSON_TAG; +import static org.opengroup.osdu.indexer.service.IAttributeParsingService.RECORD_GEOJSON_TAG; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import javax.inject.Inject; import org.apache.commons.beanutils.NestedNullException; import org.apache.commons.beanutils.PropertyUtils; +import org.apache.http.HttpStatus; +import org.opengroup.osdu.core.common.Constants; import org.opengroup.osdu.core.common.logging.JaxRsDpsLog; import org.opengroup.osdu.core.common.model.indexer.ElasticType; import org.opengroup.osdu.core.common.model.indexer.IndexSchema; +import org.opengroup.osdu.core.common.model.indexer.IndexingStatus; +import org.opengroup.osdu.core.common.model.indexer.JobStatus; +import org.opengroup.osdu.indexer.schema.converter.config.SchemaConverterConfig; import org.springframework.stereotype.Component; -import javax.inject.Inject; -import java.lang.reflect.InvocationTargetException; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; - -import static org.opengroup.osdu.indexer.service.IAttributeParsingService.DATA_GEOJSON_TAG; -import static org.opengroup.osdu.indexer.service.IAttributeParsingService.RECORD_GEOJSON_TAG; - @Component public class StorageIndexerPayloadMapper { - @Inject - private JaxRsDpsLog log; - @Inject - private IAttributeParsingService attributeParsingService; - - public Map<String, Object> mapDataPayload(IndexSchema storageSchema, Map<String, Object> storageRecordData, String recordId) { - - Map<String, Object> dataMap = new HashMap<>(); - - if (storageSchema.isDataSchemaMissing()) return dataMap; - - // get the key and get the corresponding object from the storageRecord object - for (Map.Entry<String, String> entry : storageSchema.getDataSchema().entrySet()) { - - String name = entry.getKey(); - - Object value = getPropertyValue(recordId, storageRecordData, name); - - ElasticType elasticType = ElasticType.forValue(entry.getValue()); - - if (value == null && !nullIndexedValueSupported(elasticType)) continue; - - switch (elasticType) { - case KEYWORD: - case KEYWORD_ARRAY: - case TEXT: - case TEXT_ARRAY: - dataMap.put(name, value); - break; - case INTEGER_ARRAY: - this.attributeParsingService.tryParseValueArray(Integer.class, recordId, name, value, dataMap); - break; - case INTEGER: - this.attributeParsingService.tryParseInteger(recordId, name, value, dataMap); - break; - case LONG_ARRAY: - this.attributeParsingService.tryParseValueArray(Long.class, recordId, name, value, dataMap); - break; - case LONG: - this.attributeParsingService.tryParseLong(recordId, name, value, dataMap); - break; - case FLOAT_ARRAY: - this.attributeParsingService.tryParseValueArray(Float.class, recordId, name, value, dataMap); - break; - case FLOAT: - this.attributeParsingService.tryParseFloat(recordId, name, value, dataMap); - break; - case DOUBLE_ARRAY: - this.attributeParsingService.tryParseValueArray(Double.class, recordId, name, value, dataMap); - break; - case DOUBLE: - this.attributeParsingService.tryParseDouble(recordId, name, value, dataMap); - break; - case BOOLEAN_ARRAY: - this.attributeParsingService.tryParseValueArray(Boolean.class, recordId, name, value, dataMap); - break; - case BOOLEAN: - this.attributeParsingService.tryParseBoolean(recordId, name, value, dataMap); - break; - case DATE_ARRAY: - this.attributeParsingService.tryParseValueArray(Date.class, recordId, name, value, dataMap); - break; - case DATE: - this.attributeParsingService.tryParseDate(recordId, name, value, dataMap); - break; - case GEO_POINT: - this.attributeParsingService.tryParseGeopoint(recordId, name, storageRecordData, dataMap); - break; - case GEO_SHAPE: - this.attributeParsingService.tryParseGeojson(recordId, name, value, dataMap); - break; - case NESTED: - this.attributeParsingService.tryParseNested(recordId, name, value, dataMap); - break; - case OBJECT: - this.attributeParsingService.tryParseObject(recordId, name, value, dataMap); - break; - case UNDEFINED: - // don't do anything for now - break; - } - } - - // add these once iterated over the list - storageSchema.getDataSchema().put(DATA_GEOJSON_TAG, ElasticType.GEO_SHAPE.getValue()); - storageSchema.getDataSchema().remove(RECORD_GEOJSON_TAG); - - return dataMap; - } - - private Object getPropertyValue(String recordId, Map<String, Object> storageRecordData, String propertyKey) { + @Inject + private JaxRsDpsLog log; + @Inject + private IAttributeParsingService attributeParsingService; + @Inject + private JobStatus jobStatus; + @Inject + private SchemaConverterConfig schemaConfig; + + public Map<String, Object> mapDataPayload(IndexSchema storageSchema, Map<String, Object> storageRecordData, + String recordId) { + + Map<String, Object> dataMap = new HashMap<>(); + + if (storageSchema.isDataSchemaMissing()) { + return dataMap; + } + + mapDataPayload(storageSchema.getDataSchema(), storageRecordData, recordId, dataMap); + + // add these once iterated over the list + storageSchema.getDataSchema().put(DATA_GEOJSON_TAG, ElasticType.GEO_SHAPE.getValue()); + storageSchema.getDataSchema().remove(RECORD_GEOJSON_TAG); + + return dataMap; + } + + private Map<String, Object> mapDataPayload(Map<String, Object> dataSchema, Map<String, Object> storageRecordData, + String recordId, Map<String, Object> dataMap) { + + // get the key and get the corresponding object from the storageRecord object + for (Map.Entry<String, Object> entry : dataSchema.entrySet()) { + String name = entry.getKey(); + Object value = getPropertyValue(recordId, storageRecordData, name); + ElasticType elasticType = defineElasticType(entry); + + if (Objects.isNull(elasticType)) { + this.jobStatus + .addOrUpdateRecordStatus(recordId, IndexingStatus.WARN, HttpStatus.SC_BAD_REQUEST, "e.getMessage()", + String.format("record-id: %s | %s", recordId, "e.getMessage()")); + continue; + } + + if (schemaConfig.getProcessedArraysTypes().contains(elasticType.getValue().toLowerCase())) { + processInnerProperties(recordId, dataMap, entry, name, (List<Map>) value); + } + + if (value == null && !nullIndexedValueSupported(elasticType)) { + continue; + } + + switch (elasticType) { + case KEYWORD: + case KEYWORD_ARRAY: + case TEXT: + case TEXT_ARRAY: + dataMap.put(name, value); + break; + case INTEGER_ARRAY: + this.attributeParsingService.tryParseValueArray(Integer.class, recordId, name, value, dataMap); + break; + case INTEGER: + this.attributeParsingService.tryParseInteger(recordId, name, value, dataMap); + break; + case LONG_ARRAY: + this.attributeParsingService.tryParseValueArray(Long.class, recordId, name, value, dataMap); + break; + case LONG: + this.attributeParsingService.tryParseLong(recordId, name, value, dataMap); + break; + case FLOAT_ARRAY: + this.attributeParsingService.tryParseValueArray(Float.class, recordId, name, value, dataMap); + break; + case FLOAT: + this.attributeParsingService.tryParseFloat(recordId, name, value, dataMap); + break; + case DOUBLE_ARRAY: + this.attributeParsingService.tryParseValueArray(Double.class, recordId, name, value, dataMap); + break; + case DOUBLE: + this.attributeParsingService.tryParseDouble(recordId, name, value, dataMap); + break; + case BOOLEAN_ARRAY: + this.attributeParsingService.tryParseValueArray(Boolean.class, recordId, name, value, dataMap); + break; + case BOOLEAN: + this.attributeParsingService.tryParseBoolean(recordId, name, value, dataMap); + break; + case DATE_ARRAY: + this.attributeParsingService.tryParseValueArray(Date.class, recordId, name, value, dataMap); + break; + case DATE: + this.attributeParsingService.tryParseDate(recordId, name, value, dataMap); + break; + case GEO_POINT: + this.attributeParsingService.tryParseGeopoint(recordId, name, storageRecordData, dataMap); + break; + case GEO_SHAPE: + this.attributeParsingService.tryParseGeojson(recordId, name, value, dataMap); + break; + case NESTED: + // don't do anything , each nested property will be mapped separately + break; + case FLATTENED: + // flattened type inner properties won't be mapped as they types not present in schema + this.attributeParsingService.tryParseFlattened(recordId, name, value, dataMap); + break; + case OBJECT: + // object type inner properties won't be mapped as they types not present in schema + this.attributeParsingService.tryParseObject(recordId, name, value, dataMap); + break; + case UNDEFINED: + // don't do anything for now + break; + } + } + + return dataMap; + } + + private void processInnerProperties(String recordId, Map<String, Object> dataMap, Entry<String, Object> entry, + String name, List<Map> value) { + Map map = (Map) entry.getValue(); + Map innerProperties = (Map) map.get(Constants.PROPERTIES); + ArrayList<Map> maps = new ArrayList<>(); + value.forEach(recordData -> maps.add(mapDataPayload(innerProperties, recordData, recordId, new HashMap<>()))); + dataMap.put(name, maps); + } + + private ElasticType defineElasticType(Map.Entry<String, Object> entry) { + ElasticType elasticType = null; + if (entry.getValue() instanceof String) { + elasticType = ElasticType.forValue(entry.getValue().toString()); + } else if (entry.getValue() instanceof Map) { + Map map = (Map) entry.getValue(); + elasticType = ElasticType.forValue(map.get(Constants.TYPE).toString()); + } + return elasticType; + } + + private Object getPropertyValue(String recordId, Map<String, Object> storageRecordData, String propertyKey) { try { // try getting first level property using optimized collection diff --git a/indexer-core/src/main/java/org/opengroup/osdu/indexer/util/TypeMapper.java b/indexer-core/src/main/java/org/opengroup/osdu/indexer/util/TypeMapper.java index 416728dc3..5887e7d98 100644 --- a/indexer-core/src/main/java/org/opengroup/osdu/indexer/util/TypeMapper.java +++ b/indexer-core/src/main/java/org/opengroup/osdu/indexer/util/TypeMapper.java @@ -14,6 +14,9 @@ package org.opengroup.osdu.indexer.util; +import java.util.HashMap; +import java.util.Map; +import org.apache.commons.lang3.StringUtils; import org.opengroup.osdu.core.common.Constants; import org.opengroup.osdu.core.common.model.entitlements.AclRole; import org.opengroup.osdu.core.common.model.indexer.ElasticType; @@ -21,11 +24,6 @@ import org.opengroup.osdu.core.common.model.indexer.Records; import org.opengroup.osdu.core.common.model.indexer.StorageType; import org.opengroup.osdu.core.common.model.search.RecordMetaAttribute; -import org.apache.commons.lang3.StringUtils; - -import java.util.HashMap; -import java.util.Map; - public class TypeMapper { private static final Map<String, String> storageToIndexerType = new HashMap<>(); @@ -34,6 +32,10 @@ public class TypeMapper { private static final String STORAGE_TYPE_OBJECTS = "[]object"; + private static final String STORAGE_TYPE_NESTED = "nested"; + + private static final String STORAGE_TYPE_FLATTENED = "flattened"; + static { metaAttributeIndexerType.put(RecordMetaAttribute.KIND.getValue(), ElasticType.KEYWORD.getValue()); @@ -69,6 +71,8 @@ public class TypeMapper { //TODO temporary fix for https://community.opengroup.org/osdu/platform/system/indexer-service/-/issues/1 storageToIndexerType.put(STORAGE_TYPE_OBJECTS, ElasticType.OBJECT.getValue()); + storageToIndexerType.put(STORAGE_TYPE_NESTED, ElasticType.NESTED.getValue()); + storageToIndexerType.put(STORAGE_TYPE_FLATTENED,ElasticType.FLATTENED.getValue()); } public static String getIndexerType(String storageType) { @@ -87,16 +91,29 @@ public class TypeMapper { return Records.Type.builder().type(metaAttributeIndexerType.get(key).toString()).build(); } - public static Object getDataAttributeIndexerMapping(String indexerType) { - if (ElasticType.TEXT.getValue().equalsIgnoreCase(indexerType)) { + public static Object getDataAttributeIndexerMapping(Object indexerType) { + if (ElasticType.TEXT.getValue().equalsIgnoreCase(indexerType.toString())) { return getTextIndexerMapping(); } - if (isArray(indexerType)) { - return Records.Type.builder().type(getArrayMemberType(indexerType)).build(); + if (isArray(indexerType.toString())) { + return Records.Type.builder().type(getArrayMemberType(indexerType.toString())).build(); + } + + if(isMap(indexerType)){ + Map<String,Object> type = (Map<String, Object>) indexerType; + HashMap<String, Object> propertiesMap = (HashMap<String, Object>) type.get(Constants.PROPERTIES); + for (Map.Entry<String,Object> entry : propertiesMap.entrySet()){ + entry.setValue(Records.Type.builder().type(entry.getValue().toString()).build()); + } + return indexerType; } - return Records.Type.builder().type(indexerType).build(); + return Records.Type.builder().type(indexerType.toString()).build(); + } + + private static boolean isMap(Object indexerType) { + return indexerType instanceof Map; } private static boolean isArray(String indexerType) { @@ -140,6 +157,13 @@ public class TypeMapper { return ancestryProperties; } + public static Object getObjectsArrayMapping(String dataType, Object properties) { + Map<String, Object> nestedMapping = new HashMap<>(); + nestedMapping.put(Constants.TYPE,storageToIndexerType.getOrDefault(dataType, dataType)); + nestedMapping.put(Constants.PROPERTIES,properties); + return nestedMapping; + } + private static Object getIndexStatusMapping() { Map<String, Object> indexStatusMapping = new HashMap<>(); indexStatusMapping.put("statusCode", Records.Type.builder().type(ElasticType.INTEGER.getValue()).build()); diff --git a/indexer-core/src/test/java/org/opengroup/osdu/indexer/service/StorageIndexerPayloadMapperTest.java b/indexer-core/src/test/java/org/opengroup/osdu/indexer/service/StorageIndexerPayloadMapperTest.java new file mode 100644 index 000000000..f3a1ac0bf --- /dev/null +++ b/indexer-core/src/test/java/org/opengroup/osdu/indexer/service/StorageIndexerPayloadMapperTest.java @@ -0,0 +1,140 @@ +package org.opengroup.osdu.indexer.service; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import com.google.common.collect.ImmutableMap; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.opengroup.osdu.core.common.Constants; +import org.opengroup.osdu.core.common.logging.JaxRsDpsLog; +import org.opengroup.osdu.core.common.model.indexer.IndexSchema; +import org.opengroup.osdu.core.common.model.indexer.JobStatus; +import org.opengroup.osdu.indexer.schema.converter.config.SchemaConverterPropertiesConfig; +import org.opengroup.osdu.indexer.util.parser.BooleanParser; +import org.opengroup.osdu.indexer.util.parser.DateTimeParser; +import org.opengroup.osdu.indexer.util.parser.GeoShapeParser; +import org.opengroup.osdu.indexer.util.parser.NumberParser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = {StorageIndexerPayloadMapper.class, AttributeParsingServiceImpl.class, NumberParser.class, + BooleanParser.class, DateTimeParser.class, + GeoShapeParser.class, GeometryConversionService.class, JobStatus.class, SchemaConverterPropertiesConfig.class,JaxRsDpsLog.class}) +public class StorageIndexerPayloadMapperTest { + + public static final String FIRST_OBJECT_INNER_PROPERTY = "FirstObjectInnerProperty"; + public static final String SECOND_OBJECT_INNER_PROPERTY = "SecondObjectInnerProperty"; + public static final String FIRST_OBJECT_TEST_VALUE = "first-object-test-value"; + public static final String SECOND_OBJECT_TEST_VALUE = "second-object-test-value"; + public static final String OBJECT_PROPERTY = "ObjectProperty"; + public static final String NESTED_PROPERTY = "NestedProperty"; + public static final String FIRST_NESTED_INNER_PROPERTY = "FirstNestedInnerProperty"; + public static final String SECOND_NESTED_INNER_PROPERTY = "SecondNestedInnerProperty"; + public static final String FIRST_NESTED_VALUE = "first-nested-value"; + public static final String SECOND_NESTED_VALUE = "second-nested-value"; + public static final String FLATTENED_PROPERTY = "FlattenedProperty"; + public static final String FIRST_FLATTENED_INNER_PROPERTY = "FirstFlattenedInnerProperty"; + public static final String SECOND_FLATTENED_INNER_PROPERTY = "SecondFlattenedInnerProperty"; + public static final String FIRST_FLATTENED_TEST_VALUE = "first-flattened-test-value"; + public static final String SECOND_FLATTENED_TEST_VALUE = "second-flattened-test-value"; + public static final String RECORD_TEST_ID = "test-id"; + + private static IndexSchema indexSchema; + private static Map<String, Object> storageRecordData; + + @Autowired + private StorageIndexerPayloadMapper payloadMapper; + + @BeforeClass + public static void setUp() { + HashMap<String, Object> dataMap = new HashMap<>(); + dataMap.put("TextProperty", "text"); + dataMap.put("TextArrayProperty", "text_array"); + dataMap.put("DoubleProperty", "double"); + dataMap.put(OBJECT_PROPERTY, "object"); + dataMap.put(FLATTENED_PROPERTY, "flattened"); + dataMap.put(NESTED_PROPERTY, ImmutableMap.of( + Constants.TYPE, "nested", + Constants.PROPERTIES, ImmutableMap.of( + FIRST_NESTED_INNER_PROPERTY, "text", + SECOND_NESTED_INNER_PROPERTY, "double") + )); + dataMap.put("DateProperty", "date"); + indexSchema = IndexSchema.builder().kind("kind").type(Constants.TYPE).dataSchema(dataMap).build(); + + storageRecordData = new HashMap<>(); + storageRecordData.put("TextProperty", "Testing"); + storageRecordData.put("TextArrayProperty", Arrays.asList("test", "test-value")); + storageRecordData.put("DoubleProperty", "0.1"); + + storageRecordData.put(OBJECT_PROPERTY, Arrays.asList( + ImmutableMap.of(FIRST_OBJECT_INNER_PROPERTY, FIRST_OBJECT_TEST_VALUE), + ImmutableMap.of(SECOND_OBJECT_INNER_PROPERTY, SECOND_OBJECT_TEST_VALUE) + )); + + storageRecordData.put(FLATTENED_PROPERTY, Arrays.asList( + ImmutableMap.of(FIRST_FLATTENED_INNER_PROPERTY, FIRST_FLATTENED_TEST_VALUE), + ImmutableMap.of(SECOND_FLATTENED_INNER_PROPERTY, SECOND_FLATTENED_TEST_VALUE) + )); + + storageRecordData.put(NESTED_PROPERTY, Arrays.asList( + ImmutableMap.of(FIRST_NESTED_INNER_PROPERTY, FIRST_NESTED_VALUE, SECOND_NESTED_INNER_PROPERTY,"0.1"), + ImmutableMap.of(FIRST_NESTED_INNER_PROPERTY, SECOND_NESTED_VALUE, SECOND_NESTED_INNER_PROPERTY,"0.2") + )); + storageRecordData.put("DateProperty", "2021-03-02T00:17:20.640Z"); + } + + @Test + public void mapDataPayloadTestNested() { + Map<String, Object> stringObjectMap = payloadMapper.mapDataPayload(indexSchema, storageRecordData, + RECORD_TEST_ID); + Object nestedProperty = stringObjectMap.get(NESTED_PROPERTY); + + assertTrue(nestedProperty instanceof List); + List<Map<String, Object>> nestedProperty1 = (List<Map<String, Object>>) nestedProperty; + Object firstNestedInnerProperty = nestedProperty1.get(0).get(FIRST_NESTED_INNER_PROPERTY); + assertEquals(FIRST_NESTED_VALUE,firstNestedInnerProperty); + Object secondNestedInnerProperty = nestedProperty1.get(0).get(SECOND_NESTED_INNER_PROPERTY); + assertEquals(0.1,secondNestedInnerProperty); + Object firstNestedInnerProperty1 = nestedProperty1.get(1).get(FIRST_NESTED_INNER_PROPERTY); + assertEquals(SECOND_NESTED_VALUE,firstNestedInnerProperty1); + Object secondNestedInnerProperty1 = nestedProperty1.get(1).get(SECOND_NESTED_INNER_PROPERTY); + assertEquals(0.2,secondNestedInnerProperty1); + } + + @Test + public void mapDataPayloadTestFlattened() { + Map<String, Object> stringObjectMap = payloadMapper.mapDataPayload(indexSchema, storageRecordData, + RECORD_TEST_ID); + Object objectProperty = stringObjectMap.get(FLATTENED_PROPERTY); + + assertTrue(objectProperty instanceof List); + List<Map<String, Object>> objectProperties = (List<Map<String, Object>>) objectProperty; + Object firstInnerProperty = objectProperties.get(0).get(FIRST_FLATTENED_INNER_PROPERTY); + assertEquals(FIRST_FLATTENED_TEST_VALUE,firstInnerProperty); + Object secondInnerProperty = objectProperties.get(1).get(SECOND_FLATTENED_INNER_PROPERTY); + assertEquals(SECOND_FLATTENED_TEST_VALUE,secondInnerProperty); + } + + @Test + public void mapDataPayloadTestObject() { + Map<String, Object> stringObjectMap = payloadMapper.mapDataPayload(indexSchema, storageRecordData, + RECORD_TEST_ID); + Object objectProperty = stringObjectMap.get(OBJECT_PROPERTY); + + assertTrue(objectProperty instanceof List); + List<Map<String, Object>> objectProperties = (List<Map<String, Object>>) objectProperty; + Object firstInnerProperty = objectProperties.get(0).get(FIRST_OBJECT_INNER_PROPERTY); + assertEquals(FIRST_OBJECT_TEST_VALUE,firstInnerProperty); + Object secondInnerProperty = objectProperties.get(1).get(SECOND_OBJECT_INNER_PROPERTY); + assertEquals(SECOND_OBJECT_TEST_VALUE,secondInnerProperty); + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 51f7584b1..5ec2aaf87 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ <java.version>1.8</java.version> <springfox-version>2.7.0</springfox-version> <spring-cloud.version>Greenwich.SR2</spring-cloud.version> - <os-core-common.version>0.6.9</os-core-common.version> + <os-core-common.version>0.8.1-SNAPSHOT</os-core-common.version> <snakeyaml.version>1.26</snakeyaml.version> <hibernate-validator.version>6.1.5.Final</hibernate-validator.version> <jackson.version>2.11.2</jackson.version> diff --git a/provider/indexer-azure/src/test/java/org/opengroup/osdu/indexer/azure/service/IndexerMappingServiceTest.java b/provider/indexer-azure/src/test/java/org/opengroup/osdu/indexer/azure/service/IndexerMappingServiceTest.java index 3fc9f04f7..01b50be0b 100644 --- a/provider/indexer-azure/src/test/java/org/opengroup/osdu/indexer/azure/service/IndexerMappingServiceTest.java +++ b/provider/indexer-azure/src/test/java/org/opengroup/osdu/indexer/azure/service/IndexerMappingServiceTest.java @@ -88,7 +88,7 @@ public class IndexerMappingServiceTest { @Before public void setup() throws IOException { - Map<String, String> dataMapping = new HashMap<>(); + Map<String, Object> dataMapping = new HashMap<>(); dataMapping.put("Location", "geo_point"); Map<String, Object> metaMapping = new HashMap<>(); metaMapping.put(RecordMetaAttribute.ID.getValue(), "keyword"); diff --git a/provider/indexer-gcp/src/test/java/org/opengroup/osdu/indexer/service/IndexerMappingServiceTest.java b/provider/indexer-gcp/src/test/java/org/opengroup/osdu/indexer/service/IndexerMappingServiceTest.java index 019bae350..f8debf207 100644 --- a/provider/indexer-gcp/src/test/java/org/opengroup/osdu/indexer/service/IndexerMappingServiceTest.java +++ b/provider/indexer-gcp/src/test/java/org/opengroup/osdu/indexer/service/IndexerMappingServiceTest.java @@ -74,7 +74,7 @@ public class IndexerMappingServiceTest { @Before public void setup() throws IOException { - Map<String, String> dataMapping = new HashMap<>(); + Map<String, Object> dataMapping = new HashMap<>(); dataMapping.put("Location", "geo_point"); Map<String, Object> metaMapping = new HashMap<>(); metaMapping.put(RecordMetaAttribute.ID.getValue(), "keyword"); diff --git a/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/IndexerMappingServiceTest.java b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/IndexerMappingServiceTest.java index d6982c113..9e22a80fc 100644 --- a/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/IndexerMappingServiceTest.java +++ b/provider/indexer-ibm/src/test/java/org/opengroup/osdu/indexer/ibm/service/IndexerMappingServiceTest.java @@ -76,7 +76,7 @@ public class IndexerMappingServiceTest { @Before public void setup() throws IOException { - Map<String, String> dataMapping = new HashMap<>(); + Map<String, Object> dataMapping = new HashMap<>(); dataMapping.put("Location", "geo_point"); Map<String, Object> metaMapping = new HashMap<>(); metaMapping.put(RecordMetaAttribute.ID.getValue(), "keyword"); -- GitLab