From b803c74ac767890c616f1ccf13d4fe314e4ce923 Mon Sep 17 00:00:00 2001
From: Rustam_Lotsmanenko <Rustam_Lotsmanenko@epam.com>
Date: Tue, 27 Apr 2021 16:37:46 +0400
Subject: [PATCH] review fix

---
 .../osdu/indexer/schema/converter/readme.md   |  66 ++++++++++-
 .../service/StorageIndexerPayloadMapper.java  |   3 -
 .../converter/PropertiesProcessorTest.java    | 107 ++++++++++++++++++
 .../opengroup/osdu/common/RecordSteps.java    |  21 +++-
 .../osdu/models/record/RecordData.java        |  18 +++
 .../org/opengroup/osdu/util/ElasticUtils.java |  19 ++++
 .../indexRecord-schema-service.feature        |   5 +-
 .../step_definitions/index/record/Steps.java  |  13 ++-
 8 files changed, 238 insertions(+), 14 deletions(-)
 create mode 100644 testing/indexer-test-core/src/main/java/org/opengroup/osdu/models/record/RecordData.java

diff --git a/indexer-core/src/main/java/org/opengroup/osdu/indexer/schema/converter/readme.md b/indexer-core/src/main/java/org/opengroup/osdu/indexer/schema/converter/readme.md
index 4c39a2764..17a4ca99a 100644
--- a/indexer-core/src/main/java/org/opengroup/osdu/indexer/schema/converter/readme.md
+++ b/indexer-core/src/main/java/org/opengroup/osdu/indexer/schema/converter/readme.md
@@ -203,8 +203,68 @@ Is converted to
 
 2.  Arrays
 
-Arrays of complex types are ignored, only following arrays of primitive
-types are supported
+Arrays of complex types by default will be consumed as object type
+```json
+"Markers": {
+"type": "array",
+    "items": {
+        "type": "object", 
+        "properties":  {
+            "NegativeVerticalDelta"{
+                "description": "The distance vertically below the Marker position that marks the limit of the high confidence range for the Marker pick.",
+                "x-osdu-frame-of-reference": "UOM:length",
+                "type": "number"
+            },
+          .....
+        }
+    }
+}        
+
+```
+Without inner objects processing
+```json
+{
+        path = Markers,
+        kind = []object
+}
+```
+
+Processing can be specified with optional "x-osdu-indexing" property 
+```json
+"properties": {
+    "Markers": {
+        "x-osdu-indexing": {
+        "type": "nested"
+         },
+        "type": "array",
+        "items": {
+            "type": "object",
+        "properties":  {
+            "NegativeVerticalDelta"{
+                "description": "The distance vertically below the Marker position that marks the limit of the high confidence range for the Marker pick.",
+                "x-osdu-frame-of-reference": "UOM:length",
+                "type": "number"
+            },
+          .....
+```
+"x-osdu-indexing" property values
+```json
+"nested" , "flattened"
+```
+By default, only "nested" type will lead to inner objects processing
+```json
+{ 
+path = Markers,
+        kind = nested,
+        properties = [{
+                path = NegativeVerticalDelta,
+                kind = double
+            },
+        .....
+}
+```
+
+Arrays of primitive types are supported
 
 ```json
 "number", "string", "integer", "boolean"
@@ -370,4 +430,4 @@ is converted to
     }
     ]
 }
-```
+```
\ No newline at end of file
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 1af15db21..f8d197dd9 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
@@ -128,9 +128,6 @@ public class StorageIndexerPayloadMapper {
 				case GEO_SHAPE:
 					this.attributeParsingService.tryParseGeojson(recordId, name, value, dataMap);
 					break;
-				case NESTED:
-					// don't do anything , each nested property will be parsed separately
-					break;
 				case FLATTENED:
 					// flattened type inner properties will be added "as is" without parsing as they types not present in schema
 					this.attributeParsingService.tryParseFlattened(recordId, name, value, dataMap);
diff --git a/indexer-core/src/test/java/org/opengroup/osdu/indexer/schema/converter/PropertiesProcessorTest.java b/indexer-core/src/test/java/org/opengroup/osdu/indexer/schema/converter/PropertiesProcessorTest.java
index 182bc58d3..837e203f4 100644
--- a/indexer-core/src/test/java/org/opengroup/osdu/indexer/schema/converter/PropertiesProcessorTest.java
+++ b/indexer-core/src/test/java/org/opengroup/osdu/indexer/schema/converter/PropertiesProcessorTest.java
@@ -14,6 +14,7 @@
 
 package org.opengroup.osdu.indexer.schema.converter;
 
+import com.google.common.collect.ImmutableMap;
 import org.junit.Test;
 import org.mockito.Mockito;
 import org.opengroup.osdu.core.common.logging.JaxRsDpsLog;
@@ -22,6 +23,7 @@ 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.LinkedHashMap;
@@ -101,4 +103,109 @@ public class PropertiesProcessorTest {
                 .processItem(allOfItem).map(Object::toString).reduce("", String::concat);
         assertEquals("{path=" + PATH + ", kind=int}", res);
     }
+
+    @Test
+    public void should_return_processed_nested_array_items(){
+        JaxRsDpsLog log = Mockito.mock(JaxRsDpsLog.class);
+
+        Map<String, TypeProperty> itemsProperties = new LinkedHashMap<>();
+        TypeProperty intProperty = new TypeProperty();
+        intProperty.setType("integer");
+        itemsProperties.put(PATH, intProperty);
+
+        Items items = new Items();
+        items.setProperties(itemsProperties);
+
+        TypeProperty arrayProperty = new TypeProperty();
+        arrayProperty.setIndexingType(ImmutableMap.of("type","nested"));
+        arrayProperty.setType("array");
+        arrayProperty.setItems(items);
+
+        Map<String, TypeProperty> allOfItemProperties = new LinkedHashMap<>();
+        allOfItemProperties.put(PATH,arrayProperty);
+
+        AllOfItem allOfItem = new AllOfItem();
+        allOfItem.setProperties(allOfItemProperties);
+
+        String res = new PropertiesProcessor(Mockito.mock(Definitions.class), log, new SchemaConverterPropertiesConfig())
+            .processItem(allOfItem).map(Object::toString).reduce("", String::concat);
+        assertEquals("{path="+ PATH + ", kind=nested, properties=[{path="+ PATH + ", kind=int}]}",res);
+    }
+
+    @Test
+    public void should_return_flattened_array_without_processing(){
+        JaxRsDpsLog log = Mockito.mock(JaxRsDpsLog.class);
+
+        Map<String, TypeProperty> itemsProperties = new LinkedHashMap<>();
+        TypeProperty intProperty = new TypeProperty();
+        intProperty.setType("integer");
+        itemsProperties.put(PATH, intProperty);
+
+        Items items = new Items();
+        items.setProperties(itemsProperties);
+
+        TypeProperty arrayProperty = new TypeProperty();
+        arrayProperty.setIndexingType(ImmutableMap.of("type","flattened"));
+        arrayProperty.setType("array");
+        arrayProperty.setItems(items);
+
+        Map<String, TypeProperty> allOfItemProperties = new LinkedHashMap<>();
+        allOfItemProperties.put(PATH,arrayProperty);
+
+        AllOfItem allOfItem = new AllOfItem();
+        allOfItem.setProperties(allOfItemProperties);
+
+        String res = new PropertiesProcessor(Mockito.mock(Definitions.class), log, new SchemaConverterPropertiesConfig())
+            .processItem(allOfItem).map(Object::toString).reduce("", String::concat);
+        assertEquals("{path="+ PATH + ", kind=flattened}",res);
+    }
+
+    @Test
+    public void should_return_object_array_without_hints_in_schema_without_processing(){
+        JaxRsDpsLog log = Mockito.mock(JaxRsDpsLog.class);
+
+        Map<String, TypeProperty> itemsProperties = new LinkedHashMap<>();
+        TypeProperty intProperty = new TypeProperty();
+        intProperty.setType("integer");
+        itemsProperties.put(PATH, intProperty);
+
+        Items items = new Items();
+        items.setProperties(itemsProperties);
+
+        TypeProperty arrayProperty = new TypeProperty();
+        arrayProperty.setType("array");
+        arrayProperty.setItems(items);
+
+        Map<String, TypeProperty> allOfItemProperties = new LinkedHashMap<>();
+        allOfItemProperties.put(PATH,arrayProperty);
+
+        AllOfItem allOfItem = new AllOfItem();
+        allOfItem.setProperties(allOfItemProperties);
+
+        String res = new PropertiesProcessor(Mockito.mock(Definitions.class), log, new SchemaConverterPropertiesConfig())
+            .processItem(allOfItem).map(Object::toString).reduce("", String::concat);
+        assertEquals("{path="+ PATH + ", kind=[]object}",res);
+    }
+
+    @Test
+    public void should_process_not_object_array_type(){
+        JaxRsDpsLog log = Mockito.mock(JaxRsDpsLog.class);
+
+        Items items = new Items();
+        items.setType("integer");
+
+        TypeProperty arrayProperty = new TypeProperty();
+        arrayProperty.setType("array");
+        arrayProperty.setItems(items);
+
+        Map<String, TypeProperty> allOfItemProperties = new LinkedHashMap<>();
+        allOfItemProperties.put(PATH,arrayProperty);
+
+        AllOfItem allOfItem = new AllOfItem();
+        allOfItem.setProperties(allOfItemProperties);
+
+        String res = new PropertiesProcessor(Mockito.mock(Definitions.class), log, new SchemaConverterPropertiesConfig())
+            .processItem(allOfItem).map(Object::toString).reduce("", String::concat);
+        assertEquals("{path="+ PATH + ", kind=[]int}",res);
+    }
 }
\ No newline at end of file
diff --git a/testing/indexer-test-core/src/main/java/org/opengroup/osdu/common/RecordSteps.java b/testing/indexer-test-core/src/main/java/org/opengroup/osdu/common/RecordSteps.java
index fbf12a6f9..4da17d875 100644
--- a/testing/indexer-test-core/src/main/java/org/opengroup/osdu/common/RecordSteps.java
+++ b/testing/indexer-test-core/src/main/java/org/opengroup/osdu/common/RecordSteps.java
@@ -1,5 +1,6 @@
 package org.opengroup.osdu.common;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.common.collect.MapDifference;
 import com.google.common.collect.Maps;
 import com.google.gson.Gson;
@@ -11,6 +12,7 @@ import org.elasticsearch.cluster.metadata.MappingMetadata;
 import org.opengroup.osdu.core.common.model.entitlements.Acl;
 import org.opengroup.osdu.models.Setup;
 import org.opengroup.osdu.models.TestIndex;
+import org.opengroup.osdu.models.record.RecordData;
 import org.opengroup.osdu.util.ElasticUtils;
 import org.opengroup.osdu.util.FileHandler;
 import org.opengroup.osdu.util.HTTPClient;
@@ -32,6 +34,7 @@ import static org.opengroup.osdu.util.Config.getStorageBaseURL;
 @Log
 public class RecordSteps extends TestsBase {
     private Map<String, TestIndex> inputIndexMap = new HashMap<>();
+    private ObjectMapper mapper = new ObjectMapper();
     private boolean shutDownHookAdded = false;
 
     private String timeStamp = String.valueOf(System.currentTimeMillis());
@@ -158,19 +161,33 @@ public class RecordSteps extends TestsBase {
 
     public void i_should_get_the_documents_for_the_in_the_Elastic_Search_by_nestedQuery(
         int expectedNumber, String index, String path, String firstNestedField, String firstNestedValue, String secondNestedField, String secondNestedValue)
-        throws Exception {
+        throws Throwable {
         long numOfIndexedDocuments = createIndex(index);
         long actualNumberOfRecords = elasticUtils.fetchRecordsByNestedQuery(index, path, firstNestedField, firstNestedValue, secondNestedField, secondNestedValue);
         assertEquals(expectedNumber, actualNumberOfRecords);
     }
 
     public void i_should_be_able_search_documents_for_the_by_flattened_inner_properties(int expectedCount, String index, String flattenedField,
-        String flattenedFieldValue) throws IOException, InterruptedException {
+        String flattenedFieldValue) throws Throwable {
         long numOfIndexedDocuments = createIndex(index);
         long actualNumberOfRecords = elasticUtils.fetchRecordsWithFlattenedFieldsQuery(index, flattenedField, flattenedFieldValue);
         assertEquals(expectedCount, actualNumberOfRecords);
     }
 
+    public void i_should_get_object_in_search_response_without_hints_in_schema(String objectField, String index, String recordFile, String acl, String kind)
+        throws Throwable {
+        long numOfIndexedDocuments = createIndex(index);
+        String expectedRecord = FileHandler.readFile(String.format("%s.%s", recordFile, "json"));
+
+        RecordData[] fileRecordData = mapper.readValue(expectedRecord, RecordData[].class);
+        RecordData expectedRecordData = fileRecordData[0];
+
+        String elasticRecordData = elasticUtils.fetchDataFromObjectsArrayRecords(index);
+        RecordData actualRecordData = mapper.readValue(elasticRecordData, RecordData.class);
+
+        assertEquals(expectedRecordData.getData().get(objectField),actualRecordData.getData().get(objectField));
+    }
+
     private long createIndex(String index) throws InterruptedException, IOException {
         long numOfIndexedDocuments = 0;
         int iterator;
diff --git a/testing/indexer-test-core/src/main/java/org/opengroup/osdu/models/record/RecordData.java b/testing/indexer-test-core/src/main/java/org/opengroup/osdu/models/record/RecordData.java
new file mode 100644
index 000000000..ec0a6a221
--- /dev/null
+++ b/testing/indexer-test-core/src/main/java/org/opengroup/osdu/models/record/RecordData.java
@@ -0,0 +1,18 @@
+package org.opengroup.osdu.models.record;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.List;
+import java.util.Map;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class RecordData {
+
+    @JsonProperty("data")
+    private Map<String, List<Map<String,Object>>> data;
+
+    public Map<String, List<Map<String, Object>>> getData() {
+        return data;
+    }
+
+}
diff --git a/testing/indexer-test-core/src/main/java/org/opengroup/osdu/util/ElasticUtils.java b/testing/indexer-test-core/src/main/java/org/opengroup/osdu/util/ElasticUtils.java
index 668d1888c..3d029aa6e 100644
--- a/testing/indexer-test-core/src/main/java/org/opengroup/osdu/util/ElasticUtils.java
+++ b/testing/indexer-test-core/src/main/java/org/opengroup/osdu/util/ElasticUtils.java
@@ -52,6 +52,7 @@ import org.elasticsearch.client.indices.GetIndexRequest;
 import org.elasticsearch.client.indices.GetMappingsRequest;
 import org.elasticsearch.client.indices.GetMappingsResponse;
 import org.elasticsearch.cluster.metadata.MappingMetadata;
+import org.elasticsearch.search.SearchHits;
 import org.locationtech.jts.geom.Coordinate;
 import org.elasticsearch.common.geo.builders.EnvelopeBuilder;
 import org.elasticsearch.common.settings.Settings;
@@ -309,6 +310,24 @@ public class ElasticUtils {
         }
     }
 
+    public String fetchDataFromObjectsArrayRecords(String index) throws IOException {
+        try {
+            try (RestHighLevelClient client = this.createClient(username, password, host)) {
+                SearchRequest request = new SearchRequest(index);
+                SearchResponse searchResponse = client.search(request, RequestOptions.DEFAULT);
+
+                SearchHits searchHits = searchResponse.getHits();
+                if (searchHits.getHits().length != 0) {
+                    return searchHits.getHits()[0].getSourceAsString();
+                }
+                return null;
+            }
+        } catch (ElasticsearchStatusException e) {
+            log.log(Level.INFO, String.format("Elastic search threw exception: %s", e.getMessage()));
+            return null;
+        }
+    }
+
     public Map<String, MappingMetadata> getMapping(String index) throws IOException {
         try (RestHighLevelClient client = this.createClient(username, password, host)) {
             GetMappingsRequest request = new GetMappingsRequest();
diff --git a/testing/indexer-test-core/src/main/resources/features/indexrecord/indexRecord-schema-service.feature b/testing/indexer-test-core/src/main/resources/features/indexrecord/indexRecord-schema-service.feature
index 2991c9691..50051edcf 100644
--- a/testing/indexer-test-core/src/main/resources/features/indexrecord/indexRecord-schema-service.feature
+++ b/testing/indexer-test-core/src/main/resources/features/indexrecord/indexRecord-schema-service.feature
@@ -48,7 +48,8 @@ Feature: Indexing of the documents
     When I ingest records with the <recordFile> with <acl> for a given <kind>
     Then I should be able search <number> documents for the <index> by nested <path> and properties (<first_nested_field>, <first_nested_value>) and  (<second_nested_field>, <second_nested_value>)
     Then I should be able search <number> documents for the <index> by flattened inner properties (<flattened_inner_field>, <flattened_inner_value>)
+    Then I should get <object_inner_field> in response, without hints in schema for the <index> that present in the <recordFile> with <acl> for a given <kind>
 
     Examples:
-      | kind                                              | recordFile                       | number | index                                             | acl                            | path              | first_nested_field           | first_nested_value | second_nested_field          | second_nested_value | flattened_inner_field           | flattened_inner_value |
-      | "tenant1:wks:ArraysOfObjectsTestCollection:4.0.0" | "r3-index_record_arrayofobjects" | 1      | "tenant1-wks-arraysofobjectstestcollection-4.0.0" | "data.default.viewers@tenant1" | "data.NestedTest" | "data.NestedTest.NumberTest" | 12345              | "data.NestedTest.StringTest" | "test string"       | "data.FlattenedTest.StringTest" | "test string"         |
\ No newline at end of file
+      | kind                                              | recordFile                       | number | index                                             | acl                            | path              | first_nested_field           | first_nested_value | second_nested_field          | second_nested_value | flattened_inner_field           | flattened_inner_value | object_inner_field |
+      | "tenant1:wks:ArraysOfObjectsTestCollection:4.0.0" | "r3-index_record_arrayofobjects" | 1      | "tenant1-wks-arraysofobjectstestcollection-4.0.0" | "data.default.viewers@tenant1" | "data.NestedTest" | "data.NestedTest.NumberTest" | 12345              | "data.NestedTest.StringTest" | "test string"       | "data.FlattenedTest.StringTest" | "test string"         | "ObjectTest"       |
\ No newline at end of file
diff --git a/testing/indexer-test-gcp/src/test/java/org/opengroup/osdu/step_definitions/index/record/Steps.java b/testing/indexer-test-gcp/src/test/java/org/opengroup/osdu/step_definitions/index/record/Steps.java
index bc1ee68b4..26060454c 100644
--- a/testing/indexer-test-gcp/src/test/java/org/opengroup/osdu/step_definitions/index/record/Steps.java
+++ b/testing/indexer-test-gcp/src/test/java/org/opengroup/osdu/step_definitions/index/record/Steps.java
@@ -6,7 +6,6 @@ import cucumber.api.java.Before;
 import cucumber.api.java.en.Given;
 import cucumber.api.java.en.Then;
 import cucumber.api.java.en.When;
-import java.io.IOException;
 import lombok.extern.java.Log;
 import org.opengroup.osdu.common.SchemaServiceRecordSteps;
 import org.opengroup.osdu.util.ElasticUtils;
@@ -66,7 +65,7 @@ public class Steps extends SchemaServiceRecordSteps {
     @Then("^I should be able search (\\d+) documents for the \"([^\"]*)\" by nested \"([^\"]*)\" and properties \\(\"([^\"]*)\", (\\d+)\\) and  \\(\"([^\"]*)\", \"([^\"]*)\"\\)$")
     public void i_should_get_the_documents_for_the_in_the_Elastic_Search_by_nestedQuery(
         int expectedCount, String index, String path, String firstNestedProperty, String firstNestedValue, String secondNestedProperty,
-        String secondNestedValue) throws Exception {
+        String secondNestedValue) throws Throwable {
         String actualName = generateActualName(index, null);
         super.i_should_get_the_documents_for_the_in_the_Elastic_Search_by_nestedQuery(expectedCount, actualName, path, firstNestedProperty, firstNestedValue,
             secondNestedProperty, secondNestedValue);
@@ -74,10 +73,16 @@ public class Steps extends SchemaServiceRecordSteps {
 
     @Then("^I should be able search (\\d+) documents for the \"([^\"]*)\" by flattened inner properties \\(\"([^\"]*)\", \"([^\"]*)\"\\)$")
     public void i_should_be_able_search_documents_for_the_by_flattened_inner_properties(int expectedCount, String index, String flattenedField,
-        String flattenedFieldValue)
-        throws IOException, InterruptedException {
+        String flattenedFieldValue) throws Throwable {
         String actualName = generateActualName(index, null);
         super.i_should_be_able_search_documents_for_the_by_flattened_inner_properties(expectedCount, actualName, flattenedField, flattenedFieldValue);
 
     }
+
+    @Then("^I should get \"([^\"]*)\" in response, without hints in schema for the \"([^\"]*)\" that present in the \"([^\"]*)\" with \"([^\"]*)\" for a given \"([^\"]*)\"$")
+    public void i_should_get_object_in_search_response_without_hints_in_schema(String objectInnerField, String index, String recordFile, String acl, String kind)
+        throws Throwable {
+        String actualName = generateActualName(index, null);
+        super.i_should_get_object_in_search_response_without_hints_in_schema(objectInnerField ,actualName, recordFile, acl, kind);
+    }
 }
\ No newline at end of file
-- 
GitLab