Skip to content

Commit cec734f

Browse files
LantaoJinxinyual
authored andcommitted
Implement ppl relation subquery command with Calcite (#3378)
Signed-off-by: Lantao Jin <ltjin@amazon.com> Signed-off-by: xinyual <xinyual@amazon.com>
1 parent ea4a4b9 commit cec734f

File tree

9 files changed

+562
-85
lines changed

9 files changed

+562
-85
lines changed

integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteSortCommandIT.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,4 @@ public void init() throws IOException {
2222
@Ignore
2323
@Override
2424
public void testSortIpField() throws IOException {}
25-
26-
// TODO: Fix incorrect results for NULL values, addressed by issue:
27-
// https://github.com/opensearch-project/sql/issues/3375
28-
@Ignore
29-
@Override
30-
public void testSortWithNullValue() throws IOException {}
3125
}

integ-test/src/test/java/org/opensearch/sql/calcite/standalone/CalcitePPLJoinIT.java

Lines changed: 186 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.junit.Ignore;
1919
import org.junit.Test;
2020
import org.opensearch.client.Request;
21+
import org.opensearch.sql.legacy.TestsConstants;
2122

2223
@Ignore
2324
public class CalcitePPLJoinIT extends CalcitePPLIntegTestCase {
@@ -30,22 +31,22 @@ public void init() throws IOException {
3031
loadIndex(Index.OCCUPATION);
3132
loadIndex(Index.HOBBIES);
3233
Request request1 =
33-
new Request("PUT", "/opensearch-sql_test_index_state_country/_doc/5?refresh=true");
34+
new Request("PUT", "/" + TestsConstants.TEST_INDEX_STATE_COUNTRY + "/_doc/5?refresh=true");
3435
request1.setJsonEntity(
3536
"{\"name\":\"Jim\",\"age\":27,\"state\":\"B.C\",\"country\":\"Canada\",\"year\":2023,\"month\":4}");
3637
client().performRequest(request1);
3738
Request request2 =
38-
new Request("PUT", "/opensearch-sql_test_index_state_country/_doc/6?refresh=true");
39+
new Request("PUT", "/" + TestsConstants.TEST_INDEX_STATE_COUNTRY + "/_doc/6?refresh=true");
3940
request2.setJsonEntity(
4041
"{\"name\":\"Peter\",\"age\":57,\"state\":\"B.C\",\"country\":\"Canada\",\"year\":2023,\"month\":4}");
4142
client().performRequest(request2);
4243
Request request3 =
43-
new Request("PUT", "/opensearch-sql_test_index_state_country/_doc/7?refresh=true");
44+
new Request("PUT", "/" + TestsConstants.TEST_INDEX_STATE_COUNTRY + "/_doc/7?refresh=true");
4445
request3.setJsonEntity(
4546
"{\"name\":\"Rick\",\"age\":70,\"state\":\"B.C\",\"country\":\"Canada\",\"year\":2023,\"month\":4}");
4647
client().performRequest(request3);
4748
Request request4 =
48-
new Request("PUT", "/opensearch-sql_test_index_state_country/_doc/8?refresh=true");
49+
new Request("PUT", "/" + TestsConstants.TEST_INDEX_STATE_COUNTRY + "/_doc/8?refresh=true");
4950
request4.setJsonEntity(
5051
"{\"name\":\"David\",\"age\":40,\"state\":\"Washington\",\"country\":\"USA\",\"year\":2023,\"month\":4}");
5152
client().performRequest(request4);
@@ -214,9 +215,9 @@ public void testComplexLeftJoin() {
214215
actual,
215216
rows("Jane", 20, "Quebec", "Canada", "Scientist", "Canada", 90000),
216217
rows("John", 25, "Ontario", "Canada", "Doctor", "Canada", 120000),
217-
rows("Jim", 27, "B.C", "Canada", null, null, 0),
218-
rows("Peter", 57, "B.C", "Canada", null, null, 0),
219-
rows("Rick", 70, "B.C", "Canada", null, null, 0));
218+
rows("Jim", 27, "B.C", "Canada", null, null, null),
219+
rows("Peter", 57, "B.C", "Canada", null, null, null),
220+
rows("Rick", 70, "B.C", "Canada", null, null, null));
220221
}
221222

222223
@Test
@@ -241,10 +242,10 @@ public void testComplexRightJoin() {
241242
actual,
242243
rows("Jane", 20, "Quebec", "Canada", "Scientist", "Canada", 90000),
243244
rows("John", 25, "Ontario", "Canada", "Doctor", "Canada", 120000),
244-
rows(null, 0, null, null, "Engineer", "England", 100000),
245-
rows(null, 0, null, null, "Artist", "USA", 70000),
246-
rows(null, 0, null, null, "Doctor", "USA", 120000),
247-
rows(null, 0, null, null, "Unemployed", "Canada", 0));
245+
rows(null, null, null, null, "Engineer", "England", 100000),
246+
rows(null, null, null, null, "Artist", "USA", 70000),
247+
rows(null, null, null, null, "Doctor", "USA", 120000),
248+
rows(null, null, null, null, "Unemployed", "Canada", 0));
248249
}
249250

250251
@Test
@@ -366,6 +367,54 @@ public void testMultipleJoins() {
366367
TEST_INDEX_STATE_COUNTRY));
367368
}
368369

370+
@Ignore // TODO seems a calcite bug
371+
public void testMultipleJoinsWithRelationSubquery() {
372+
JSONObject actual =
373+
executeQuery(
374+
String.format(
375+
"""
376+
source = %s
377+
| where country = 'Canada' OR country = 'England'
378+
| inner join left=a, right=b
379+
ON a.name = b.name AND a.year = 2023 AND a.month = 4 AND b.year = 2023 AND b.month = 4
380+
[
381+
source = %s
382+
]
383+
| eval a_name = a.name
384+
| eval a_country = a.country
385+
| eval b_country = b.country
386+
| fields a_name, age, state, a_country, occupation, b_country, salary
387+
| left join left=a, right=b
388+
ON a.a_name = b.name
389+
[
390+
source = %s
391+
]
392+
| eval aa_country = a.a_country
393+
| eval ab_country = a.b_country
394+
| eval bb_country = b.country
395+
| fields a_name, age, state, aa_country, occupation, ab_country, salary, bb_country, hobby, language
396+
| cross join left=a, right=b
397+
[
398+
source = %s
399+
]
400+
| eval new_country = a.aa_country
401+
| eval new_salary = b.salary
402+
| stats avg(new_salary) as avg_salary by span(age, 5) as age_span, state
403+
| left semi join left=a, right=b
404+
ON a.state = b.state
405+
[
406+
source = %s
407+
]
408+
| eval new_avg_salary = floor(avg_salary)
409+
| fields state, age_span, new_avg_salary
410+
""",
411+
TEST_INDEX_STATE_COUNTRY,
412+
TEST_INDEX_OCCUPATION,
413+
TEST_INDEX_HOBBIES,
414+
TEST_INDEX_OCCUPATION,
415+
TEST_INDEX_STATE_COUNTRY));
416+
}
417+
369418
@Test
370419
public void testMultipleJoinsWithoutTableAliases() {
371420
JSONObject actual =
@@ -416,7 +465,7 @@ public void testMultipleJoinsWithPartTableAliases() {
416465
}
417466

418467
@Test
419-
public void testMultipleJoinsWithSelfJoin1() {
468+
public void testMultipleJoinsWithSelfJoin() {
420469
JSONObject actual =
421470
executeQuery(
422471
String.format(
@@ -443,8 +492,8 @@ public void testMultipleJoinsWithSelfJoin1() {
443492
rows("John", "John", "John", "John"));
444493
}
445494

446-
@Ignore // TODO table subquery not support
447-
public void testMultipleJoinsWithSelfJoin2() {
495+
@Test
496+
public void testMultipleJoinsWithSubquerySelfJoin() {
448497
JSONObject actual =
449498
executeQuery(
450499
String.format(
@@ -455,10 +504,24 @@ public void testMultipleJoinsWithSelfJoin2() {
455504
TEST_INDEX_OCCUPATION,
456505
TEST_INDEX_HOBBIES,
457506
TEST_INDEX_STATE_COUNTRY));
507+
verifySchema(
508+
actual,
509+
schema("name", "string"),
510+
schema("name0", "string"),
511+
schema("name1", "string"),
512+
schema("name2", "string"));
513+
verifyDataRows(
514+
actual,
515+
rows("David", "David", "David", "David"),
516+
rows("David", "David", "David", "David"),
517+
rows("Hello", "Hello", "Hello", "Hello"),
518+
rows("Jake", "Jake", "Jake", "Jake"),
519+
rows("Jane", "Jane", "Jane", "Jane"),
520+
rows("John", "John", "John", "John"));
458521
}
459522

460523
@Test
461-
public void testCheckAccessTheReferenceByAliases1() {
524+
public void testCheckAccessTheReferenceByAliases() {
462525
String res1 =
463526
execute(
464527
String.format(
@@ -494,8 +557,8 @@ public void testCheckAccessTheReferenceByAliases1() {
494557
assertEquals(res4, res5);
495558
}
496559

497-
@Ignore // TODO table subquery not support
498-
public void testCheckAccessTheReferenceByAliases2() {
560+
@Test
561+
public void testCheckAccessTheReferenceBySubqueryAliases() {
499562
String res1 =
500563
execute(
501564
String.format(
@@ -533,7 +596,7 @@ public void testCheckAccessTheReferenceByAliases2() {
533596
}
534597

535598
@Test
536-
public void testCheckAccessTheReferenceByAliases3() {
599+
public void testCheckAccessTheReferenceByOverrideAliases() {
537600
String res1 =
538601
execute(
539602
String.format(
@@ -555,4 +618,109 @@ public void testCheckAccessTheReferenceByAliases3() {
555618
assertEquals(res1, res2);
556619
assertEquals(res1, res3);
557620
}
621+
622+
@Test
623+
public void testCheckAccessTheReferenceByOverrideSubqueryAliases() {
624+
String res1 =
625+
execute(
626+
String.format(
627+
"source = %s | JOIN left = t1 right = t2 ON t1.name = t2.name [ source = %s as tt ]"
628+
+ " | fields tt.name",
629+
TEST_INDEX_STATE_COUNTRY, TEST_INDEX_OCCUPATION));
630+
String res2 =
631+
execute(
632+
String.format(
633+
"source = %s | JOIN left = t1 right = t2 ON t1.name = t2.name [ source = %s as tt ]"
634+
+ " as t2 | fields tt.name",
635+
TEST_INDEX_STATE_COUNTRY, TEST_INDEX_OCCUPATION));
636+
String res3 =
637+
execute(
638+
String.format(
639+
"source = %s | JOIN left = t1 right = t2 ON t1.name = t2.name [ source = %s ] as tt"
640+
+ " | fields tt.name",
641+
TEST_INDEX_STATE_COUNTRY, TEST_INDEX_OCCUPATION));
642+
assertEquals(res1, res2);
643+
assertEquals(res1, res3);
644+
}
645+
646+
@Test
647+
public void testCheckAccessTheReferenceByOverrideSubqueryAliases2() {
648+
String res1 =
649+
execute(
650+
String.format(
651+
"source = %s | JOIN left = t1 right = t2 ON t1.name = t2.name [ source = %s as tt ]"
652+
+ " | fields t2.name",
653+
TEST_INDEX_STATE_COUNTRY, TEST_INDEX_OCCUPATION));
654+
String res2 =
655+
execute(
656+
String.format(
657+
"source = %s | JOIN left = t1 right = t2 ON t1.name = t2.name [ source = %s as tt ]"
658+
+ " as t2 | fields t2.name",
659+
TEST_INDEX_STATE_COUNTRY, TEST_INDEX_OCCUPATION));
660+
String res3 =
661+
execute(
662+
String.format(
663+
"source = %s | JOIN left = t1 right = t2 ON t1.name = t2.name [ source = %s ] as tt"
664+
+ " | fields t2.name",
665+
TEST_INDEX_STATE_COUNTRY, TEST_INDEX_OCCUPATION));
666+
assertEquals(res1, res2);
667+
assertEquals(res1, res3);
668+
}
669+
670+
@Test
671+
public void testInnerJoinWithRelationSubquery() {
672+
JSONObject actual =
673+
executeQuery(
674+
String.format(
675+
"""
676+
source = %s
677+
| where country = 'USA' OR country = 'England'
678+
| inner join left=a, right=b
679+
ON a.name = b.name
680+
[
681+
source = %s
682+
| where salary > 0
683+
| fields name, country, salary
684+
| sort salary
685+
| head 3
686+
]
687+
| stats avg(salary) by span(age, 10) as age_span, b.country
688+
""",
689+
TEST_INDEX_STATE_COUNTRY, TEST_INDEX_OCCUPATION));
690+
verifySchema(
691+
actual,
692+
schema("b.country", "string"),
693+
schema("age_span", "double"),
694+
schema("avg(salary)", "double"));
695+
verifyDataRows(actual, rows("USA", 30, 70000.0), rows("England", 70, 100000));
696+
}
697+
698+
@Test
699+
public void testLeftJoinWithRelationSubquery() {
700+
JSONObject actual =
701+
executeQuery(
702+
String.format(
703+
"""
704+
source = %s
705+
| where country = 'USA' OR country = 'England'
706+
| left join left=a, right=b
707+
ON a.name = b.name
708+
[
709+
source = %s
710+
| where salary > 0
711+
| fields name, country, salary
712+
| sort salary
713+
| head 3
714+
]
715+
| stats avg(salary) by span(age, 10) as age_span, b.country
716+
""",
717+
TEST_INDEX_STATE_COUNTRY, TEST_INDEX_OCCUPATION));
718+
verifySchema(
719+
actual,
720+
schema("b.country", "string"),
721+
schema("age_span", "double"),
722+
schema("avg(salary)", "double"));
723+
verifyDataRows(
724+
actual, rows("USA", 30, 70000.0), rows("England", 70, 100000), rows(null, 40, 0));
725+
}
558726
}

integ-test/src/test/java/org/opensearch/sql/calcite/standalone/CalcitePPLSortIT.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66
package org.opensearch.sql.calcite.standalone;
77

88
import static org.opensearch.sql.legacy.TestsConstants.TEST_INDEX_BANK;
9+
import static org.opensearch.sql.legacy.TestsConstants.TEST_INDEX_BANK_WITH_NULL_VALUES;
910
import static org.opensearch.sql.util.MatcherUtils.rows;
1011
import static org.opensearch.sql.util.MatcherUtils.schema;
1112
import static org.opensearch.sql.util.MatcherUtils.verifyDataRows;
13+
import static org.opensearch.sql.util.MatcherUtils.verifyOrder;
1214
import static org.opensearch.sql.util.MatcherUtils.verifySchema;
1315

1416
import java.io.IOException;
@@ -24,6 +26,7 @@ public void init() throws IOException {
2426
super.init();
2527

2628
loadIndex(Index.BANK);
29+
loadIndex(Index.BANK_WITH_NULL_VALUES);
2730
}
2831

2932
@Test
@@ -193,4 +196,22 @@ public void testSortAgeNameAndFieldsNameAge() {
193196
rows("Amber JOHnny", 32),
194197
rows("Nanette", 28));
195198
}
199+
200+
@Test
201+
public void testSortWithNullValue() throws IOException {
202+
JSONObject result =
203+
executeQuery(
204+
String.format(
205+
"source=%s | sort balance | fields firstname, balance",
206+
TEST_INDEX_BANK_WITH_NULL_VALUES));
207+
verifyOrder(
208+
result,
209+
rows("Dale", 4180),
210+
rows("Nanette", 32838),
211+
rows("Amber JOHnny", 39225),
212+
rows("Dillard", 48086),
213+
rows("Hattie", null),
214+
rows("Elinor", null),
215+
rows("Virginia", null));
216+
}
196217
}

opensearch/src/main/java/org/opensearch/sql/opensearch/storage/scan/CalciteOpenSearchIndexScan.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ public CalciteOpenSearchIndexScan pushDownFilter(Filter filter) {
167167
// TODO: handle the case where condition contains a score function
168168
return newScan;
169169
} catch (Exception e) {
170-
LOG.warn("Cannot analyze the filter condition {}", filter.getCondition(), e);
170+
LOG.warn("Cannot pushdown the filter condition {}, ", filter.getCondition());
171171
}
172172
return null;
173173
}

0 commit comments

Comments
 (0)