diff --git a/integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteFieldFormatCommandIT.java b/integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteFieldFormatCommandIT.java index 86f87c90c8..24c7e50422 100644 --- a/integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteFieldFormatCommandIT.java +++ b/integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteFieldFormatCommandIT.java @@ -12,6 +12,7 @@ import org.json.JSONObject; import org.junit.jupiter.api.Test; import org.opensearch.client.Request; +import org.opensearch.sql.legacy.TestUtils; import org.opensearch.sql.ppl.PPLIntegTestCase; public class CalciteFieldFormatCommandIT extends PPLIntegTestCase { @@ -23,26 +24,48 @@ public void init() throws Exception { loadIndex(Index.BANK); - // Create test data for string concatenation - Request request1 = new Request("PUT", "/test_eval/_doc/1?refresh=true"); - request1.setJsonEntity("{\"name\": \"Alice\", \"age\": 25, \"title\": \"Engineer\"}"); - client().performRequest(request1); + // Pre-create test_eval through the helper so the analytics-engine compatibility run + // (tests.analytics.parquet_indices=true) provisions it as a parquet-backed composite + // index. Plain auto-mapping via the doc PUTs would create a Lucene-backed index, which + // the analytics-engine planner cannot scan ("No backend can scan all requested fields"). + // Explicit mapping pins types so both v2 (verifySchema "string"/"bigint") and analytics + // paths see the same shape regardless of dynamic-mapping behavior on the parquet engine. + // Guarded by isIndexExist for idempotency — init() runs before each @Test method. + if (!TestUtils.isIndexExist(client(), "test_eval")) { + String testEvalMapping = + "{\"mappings\":{\"properties\":{" + + "\"name\":{\"type\":\"keyword\"}," + + "\"age\":{\"type\":\"long\"}," + + "\"title\":{\"type\":\"keyword\"}}}}"; + TestUtils.createIndexByRestClient(client(), "test_eval", testEvalMapping); - Request request2 = new Request("PUT", "/test_eval/_doc/2?refresh=true"); - request2.setJsonEntity("{\"name\": \"Bob\", \"age\": 30, \"title\": \"Manager\"}"); - client().performRequest(request2); + // Create test data for string concatenation + Request request1 = new Request("PUT", "/test_eval/_doc/1?refresh=true"); + request1.setJsonEntity("{\"name\": \"Alice\", \"age\": 25, \"title\": \"Engineer\"}"); + client().performRequest(request1); - Request request3 = new Request("PUT", "/test_eval/_doc/3?refresh=true"); - request3.setJsonEntity("{\"name\": \"Charlie\", \"age\": null, \"title\": \"Analyst\"}"); - client().performRequest(request3); + Request request2 = new Request("PUT", "/test_eval/_doc/2?refresh=true"); + request2.setJsonEntity("{\"name\": \"Bob\", \"age\": 30, \"title\": \"Manager\"}"); + client().performRequest(request2); + + Request request3 = new Request("PUT", "/test_eval/_doc/3?refresh=true"); + request3.setJsonEntity("{\"name\": \"Charlie\", \"age\": null, \"title\": \"Analyst\"}"); + client().performRequest(request3); + } } @Test public void testFieldFormatStringConcatenation() throws IOException { + // Pin the projection so column order is deterministic across execution paths — the + // analytics-engine route reads parquet schema in storage order, which can differ from the + // v2 / Lucene path's _source-iteration order. Adding an explicit | fields makes the test + // a strict assertion on the fieldformat expression rather than a coincidence of projection + // order. JSONObject result = executeQuery( StringEscapeUtils.escapeJson( - "source=test_eval | fieldformat greeting = 'Hello ' + name")); + "source=test_eval | fieldformat greeting = 'Hello ' + name | fields name, title," + + " age, greeting")); verifySchema( result, schema("name", "string"),