diff --git a/core/src/main/java/org/opensearch/sql/expression/ReferenceExpression.java b/core/src/main/java/org/opensearch/sql/expression/ReferenceExpression.java index 94bb4e067d..8c4f3b5681 100644 --- a/core/src/main/java/org/opensearch/sql/expression/ReferenceExpression.java +++ b/core/src/main/java/org/opensearch/sql/expression/ReferenceExpression.java @@ -10,11 +10,15 @@ import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; + import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.RequiredArgsConstructor; +import org.opensearch.sql.data.model.ExprCollectionValue; import org.opensearch.sql.data.model.ExprTupleValue; import org.opensearch.sql.data.model.ExprValue; +import org.opensearch.sql.data.type.ExprCoreType; import org.opensearch.sql.data.type.ExprType; import org.opensearch.sql.expression.env.Environment; @@ -100,6 +104,12 @@ public ExprValue resolve(ExprTupleValue value) { } private ExprValue resolve(ExprValue value, List paths) { + // This case is to allow returning all values in an array to be in one row + if (value.type().equals(ExprCoreType.ARRAY)){ + return new ExprCollectionValue(value.collectionValue().stream() + .map(val -> resolve(val, paths)).collect(Collectors.toList())); + } + final ExprValue wholePathValue = value.keyValue(String.join(PATH_SEP, paths)); if (!wholePathValue.isMissing() || paths.size() == 1) { return wholePathValue; diff --git a/integ-test/src/test/java/org/opensearch/sql/legacy/ObjectFieldSelectIT.java b/integ-test/src/test/java/org/opensearch/sql/legacy/ObjectFieldSelectIT.java index bddaa22772..dc1c8f21c4 100644 --- a/integ-test/src/test/java/org/opensearch/sql/legacy/ObjectFieldSelectIT.java +++ b/integ-test/src/test/java/org/opensearch/sql/legacy/ObjectFieldSelectIT.java @@ -85,22 +85,6 @@ public void testSelectNestedFieldItself() { ); } - @Test - public void testSelectObjectFieldOfArrayValuesItself() { - JSONObject response = new JSONObject(query("SELECT accounts FROM %s")); - - // Only the first element of the list of is returned. - verifyDataRows(response, rows(new JSONObject("{\"id\": 1}"))); - } - - @Test - public void testSelectObjectFieldOfArrayValuesInnerFields() { - JSONObject response = new JSONObject(query("SELECT accounts.id FROM %s")); - - // Only the first element of the list of is returned. - verifyDataRows(response, rows(1)); - } - private String query(String sql) { return executeQuery( StringUtils.format(sql, TEST_INDEX_DEEP_NESTED), diff --git a/integ-test/src/test/java/org/opensearch/sql/legacy/SQLIntegTestCase.java b/integ-test/src/test/java/org/opensearch/sql/legacy/SQLIntegTestCase.java index 53d14f67ab..1f48184bb1 100644 --- a/integ-test/src/test/java/org/opensearch/sql/legacy/SQLIntegTestCase.java +++ b/integ-test/src/test/java/org/opensearch/sql/legacy/SQLIntegTestCase.java @@ -587,7 +587,15 @@ public enum Index { "src/test/resources/calcs.json"), MULTI_NESTED(TestsConstants.TEST_INDEX_MULTI_NESTED, "multi_nested", - getMappingFile("indexDefinitions/multi_nested.json"), + getMappingFile("multi_nested.json"), + "src/test/resources/multi_nested_objects.json"), + NESTED_OBJECT(TestsConstants.TEST_INDEX_NESTED_OBJECT, + "nested_object", + null, + "src/test/resources/nested_objects.json"), + MULTI_NESTED_OBJECT(TestsConstants.TEST_INDEX_MULTI_NESTED_OBJECT, + "multi_nested_object", + null, "src/test/resources/multi_nested_objects.json"); private final String name; diff --git a/integ-test/src/test/java/org/opensearch/sql/legacy/TestsConstants.java b/integ-test/src/test/java/org/opensearch/sql/legacy/TestsConstants.java index 11c4306be3..beaefd5469 100644 --- a/integ-test/src/test/java/org/opensearch/sql/legacy/TestsConstants.java +++ b/integ-test/src/test/java/org/opensearch/sql/legacy/TestsConstants.java @@ -53,7 +53,9 @@ public class TestsConstants { public final static String TEST_INDEX_BEER = TEST_INDEX + "_beer"; public final static String TEST_INDEX_NULL_MISSING = TEST_INDEX + "_null_missing"; public final static String TEST_INDEX_CALCS = TEST_INDEX + "_calcs"; - public final static String TEST_INDEX_MULTI_NESTED= TEST_INDEX + "_multi_nested"; + public final static String TEST_INDEX_MULTI_NESTED = TEST_INDEX + "_multi_nested"; + public final static String TEST_INDEX_NESTED_OBJECT = TEST_INDEX + "_nested_object"; + public final static String TEST_INDEX_MULTI_NESTED_OBJECT = TEST_INDEX + "_multi_nested_object"; public final static String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; public final static String TS_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS"; diff --git a/integ-test/src/test/java/org/opensearch/sql/sql/NestedIT.java b/integ-test/src/test/java/org/opensearch/sql/sql/NestedIT.java index e8d6dcc8aa..cb5c24fcc5 100644 --- a/integ-test/src/test/java/org/opensearch/sql/sql/NestedIT.java +++ b/integ-test/src/test/java/org/opensearch/sql/sql/NestedIT.java @@ -5,11 +5,17 @@ package org.opensearch.sql.sql; +import static org.opensearch.sql.legacy.TestsConstants.TEST_INDEX_MULTI_NESTED; +import static org.opensearch.sql.util.MatcherUtils.rows; +import static org.opensearch.sql.util.MatcherUtils.verifyDataRows; + +import org.json.JSONArray; import org.json.JSONObject; import org.junit.Test; import org.opensearch.sql.legacy.SQLIntegTestCase; import java.io.IOException; +import java.util.List; import static org.opensearch.sql.legacy.TestsConstants.TEST_INDEX_NESTED_TYPE; @@ -17,12 +23,34 @@ public class NestedIT extends SQLIntegTestCase { @Override public void init() throws IOException { loadIndex(Index.NESTED); + loadIndex(Index.MULTI_NESTED); + } + + @Test + public void nested_function_with_array_of_nested_field_test() { + String query = "SELECT nested(message.info), nested(comment.data) FROM " + TEST_INDEX_NESTED_TYPE; + JSONObject result = executeJdbcRequest(query); + + assertEquals(5, result.getInt("total")); + verifyDataRows(result, + rows("a", "ab"), + rows("b", "aa"), + rows("c", "aa"), + rows(new JSONArray(List.of("c","a")), "ab"), + rows(new JSONArray(List.of("zz")), new JSONArray(List.of("aa", "bb")))); } @Test - public void nested_string_subfield_test() { - String query = "SELECT nested(message.dayOfWeek) FROM " + TEST_INDEX_NESTED_TYPE; + public void nested_function_with_array_of_multi_nested_field_test() { + String query = "SELECT nested(message.author.name) FROM " + TEST_INDEX_MULTI_NESTED; JSONObject result = executeJdbcRequest(query); + assertEquals(5, result.getInt("total")); + verifyDataRows(result, + rows("e"), + rows("f"), + rows("g"), + rows(new JSONArray(List.of("h", "p"))), + rows(new JSONArray(List.of("yy")))); } } diff --git a/integ-test/src/test/java/org/opensearch/sql/sql/NestedPartiQLIT.java b/integ-test/src/test/java/org/opensearch/sql/sql/NestedPartiQLIT.java new file mode 100644 index 0000000000..d8c9e0ec65 --- /dev/null +++ b/integ-test/src/test/java/org/opensearch/sql/sql/NestedPartiQLIT.java @@ -0,0 +1,50 @@ +package org.opensearch.sql.sql; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.junit.Test; +import org.opensearch.sql.legacy.SQLIntegTestCase; + +import java.io.IOException; +import java.util.List; + +import static org.opensearch.sql.legacy.TestsConstants.TEST_INDEX_MULTI_NESTED; +import static org.opensearch.sql.legacy.TestsConstants.TEST_INDEX_NESTED_TYPE; +import static org.opensearch.sql.util.MatcherUtils.rows; +import static org.opensearch.sql.util.MatcherUtils.verifyDataRows; + +public class NestedPartiQLIT extends SQLIntegTestCase { + @Override + public void init() throws IOException { + loadIndex(Index.NESTED); + loadIndex(Index.MULTI_NESTED); + } + + @Test + public void partiQL_with_array_of_nested_field_test() { + String query = "SELECT message.info, comment.data FROM " + TEST_INDEX_NESTED_TYPE; + JSONObject result = executeJdbcRequest(query); + + assertEquals(5, result.getInt("total")); + verifyDataRows(result, + rows("a", "ab"), + rows("b", "aa"), + rows("c", "aa"), + rows(new JSONArray(List.of("c","a")), "ab"), + rows(new JSONArray(List.of("zz")), new JSONArray(List.of("aa", "bb")))); + } + + @Test + public void partiQL_with_array_of_multi_nested_field_test() { + String query = "SELECT message.author.name, message.info FROM " + TEST_INDEX_MULTI_NESTED; + JSONObject result = executeJdbcRequest(query); + + assertEquals(5, result.getInt("total")); + verifyDataRows(result, + rows("e", "a"), + rows("f", "b"), + rows("g", "c"), + rows(new JSONArray(List.of("h", "p")), new JSONArray(List.of("d","i"))), + rows(new JSONArray(List.of("yy")), new JSONArray(List.of("zz")))); + } +} diff --git a/integ-test/src/test/java/org/opensearch/sql/sql/ObjectIT.java b/integ-test/src/test/java/org/opensearch/sql/sql/ObjectIT.java new file mode 100644 index 0000000000..105bd0dab0 --- /dev/null +++ b/integ-test/src/test/java/org/opensearch/sql/sql/ObjectIT.java @@ -0,0 +1,50 @@ +package org.opensearch.sql.sql; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.junit.Test; +import org.opensearch.sql.legacy.SQLIntegTestCase; + +import java.io.IOException; +import java.util.List; + +import static org.opensearch.sql.legacy.TestsConstants.TEST_INDEX_MULTI_NESTED_OBJECT; +import static org.opensearch.sql.legacy.TestsConstants.TEST_INDEX_NESTED_OBJECT; +import static org.opensearch.sql.util.MatcherUtils.rows; +import static org.opensearch.sql.util.MatcherUtils.verifyDataRows; + +public class ObjectIT extends SQLIntegTestCase { + @Override + public void init() throws IOException { + loadIndex(Index.NESTED_OBJECT); + loadIndex(Index.MULTI_NESTED_OBJECT); + } + + @Test + public void object_array_of_objects_test() { + String query = "SELECT message.info, comment.data FROM " + TEST_INDEX_NESTED_OBJECT; + JSONObject result = executeJdbcRequest(query); + + assertEquals(5, result.getInt("total")); + verifyDataRows(result, + rows("a", "ab"), + rows("b", "aa"), + rows("c", "aa"), + rows(new JSONArray(List.of("c","a")), "ab"), + rows(new JSONArray(List.of("zz")), new JSONArray(List.of("aa", "bb")))); + } + + @Test + public void object_with_array_of_multi_nested_objects_test() { + String query = "SELECT message.author.name, message.info FROM " + TEST_INDEX_MULTI_NESTED_OBJECT; + JSONObject result = executeJdbcRequest(query); + + assertEquals(5, result.getInt("total")); + verifyDataRows(result, + rows("e", "a"), + rows("f", "b"), + rows("g", "c"), + rows(new JSONArray(List.of("h", "p")), new JSONArray(List.of("d","i"))), + rows(new JSONArray(List.of("yy")), new JSONArray(List.of("zz")))); + } +} diff --git a/integ-test/src/test/resources/multi_nested_objects.json b/integ-test/src/test/resources/multi_nested_objects.json index bdee031357..f5838fe90b 100644 --- a/integ-test/src/test/resources/multi_nested_objects.json +++ b/integ-test/src/test/resources/multi_nested_objects.json @@ -1,9 +1,9 @@ {"index":{"_id":"1"}} -{"message":[{"info":"a","author":{"name": "e", "address": {"street": "bc", "number": 1}},"dayOfWeek":1}]} +{"message":{"info":"a","author":{"name": "e", "address": {"street": "bc", "number": 1}},"dayOfWeek":1}} {"index":{"_id":"2"}} -{"message":[{"info":"b","author":{"name": "f", "address": {"street": "ab", "number": 2}},"dayOfWeek":2}]} +{"message":{"info":"b","author":{"name": "f", "address": {"street": "ab", "number": 2}},"dayOfWeek":2}} {"index":{"_id":"3"}} -{"message":[{"info":"c","author":{"name": "g", "address": {"street": "sk", "number": 3}},"dayOfWeek":1}]} +{"message":{"info":"c","author":{"name": "g", "address": {"street": "sk", "number": 3}},"dayOfWeek":1}} {"index":{"_id":"4"}} {"message":[{"info":"d","author":{"name": "h", "address": {"street": "mb", "number": 4}},"dayOfWeek":4},{"info":"i","author":{"name": "p", "address": {"street": "on", "number": 5}},"dayOfWeek":5}]} {"index":{"_id":"5"}}