Skip to content

Commit 7e67fcb

Browse files
authored
added isnotnull condition support (#5278)
* added isnotnull condition support Signed-off-by: Thy Tran <58045538+ThyTran1402@users.noreply.github.com> * added tests for ingesttest Signed-off-by: Thy Tran <58045538+ThyTran1402@users.noreply.github.com> * fixed IS NOT NULL doctest output in condition.md Signed-off-by: Thy Tran <58045538+ThyTran1402@users.noreply.github.com> --------- Signed-off-by: Thy Tran <58045538+ThyTran1402@users.noreply.github.com>
1 parent 0cfe542 commit 7e67fcb

6 files changed

Lines changed: 90 additions & 0 deletions

File tree

docs/user/ppl/functions/condition.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ PPL conditional functions enable global filtering of query results based on spec
77

88
Returns `TRUE` if the field is `NULL`, `FALSE` otherwise.
99

10+
The `field IS NULL` predicate syntax is also supported as a synonym.
11+
1012
The `isnull()` function is commonly used:
1113
- In `eval` expressions to create conditional fields.
1214
- With the `if()` function to provide default values.
@@ -69,6 +71,14 @@ source=accounts
6971
| where isnull(employer)
7072
| fields account_number, firstname, employer
7173
```
74+
75+
The `IS NULL` predicate syntax can be used as an equivalent alternative:
76+
77+
```ppl
78+
source=accounts
79+
| where employer IS NULL
80+
| fields account_number, firstname, employer
81+
```
7282

7383
The query returns the following results:
7484

@@ -87,6 +97,8 @@ fetched rows / total rows = 1/1
8797

8898
Returns `TRUE` if the field is NOT `NULL`, `FALSE` otherwise.
8999

100+
The `field IS NOT NULL` predicate syntax is also supported as a synonym.
101+
90102
The `isnotnull()` function is commonly used:
91103
- In `eval` expressions to create Boolean flags.
92104
- In `where` clauses to filter out null values.
@@ -141,6 +153,27 @@ fetched rows / total rows = 1/1
141153
| 18 | null |
142154
+----------------+----------+
143155
```
156+
157+
The `IS NOT NULL` predicate syntax is equivalent to `isnotnull()`:
158+
159+
```ppl
160+
source=accounts
161+
| where employer IS NOT NULL
162+
| fields account_number, employer
163+
```
164+
165+
The query returns the following results:
166+
167+
```text
168+
fetched rows / total rows = 3/3
169+
+----------------+----------+
170+
| account_number | employer |
171+
|----------------+----------|
172+
| 1 | Pyrami |
173+
| 6 | Netagy |
174+
| 13 | Quility |
175+
+----------------+----------+
176+
```
144177

145178
The following example demonstrates using `isnotnull` with the `if` function to create validation messages:
146179

integ-test/src/test/java/org/opensearch/sql/ppl/WhereCommandIT.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,27 @@ public void testIsNotNullFunction() throws IOException {
193193
verifyDataRows(result, rows("Amber JOHnny"));
194194
}
195195

196+
@Test
197+
public void testIsNullPredicate() throws IOException {
198+
JSONObject result =
199+
executeQuery(
200+
String.format(
201+
"source=%s | where age IS NULL | fields firstname",
202+
TEST_INDEX_BANK_WITH_NULL_VALUES));
203+
verifyDataRows(result, rows("Virginia"));
204+
}
205+
206+
@Test
207+
public void testIsNotNullPredicate() throws IOException {
208+
JSONObject result =
209+
executeQuery(
210+
String.format(
211+
"source=%s | where age IS NOT NULL and like(firstname, 'Ambe_%%') | fields"
212+
+ " firstname",
213+
TEST_INDEX_BANK_WITH_NULL_VALUES));
214+
verifyDataRows(result, rows("Amber JOHnny"));
215+
}
216+
196217
@Test
197218
public void testWhereWithMetadataFields() throws IOException {
198219
JSONObject result =

ppl/src/main/antlr/OpenSearchPPLLexer.g4

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,9 @@ PATH: 'PATH';
189189
CASE: 'CASE';
190190
ELSE: 'ELSE';
191191
IN: 'IN';
192+
IS: 'IS';
192193
EXISTS: 'EXISTS';
194+
NULL: 'NULL';
193195

194196
// Geo IP eval function
195197
GEOIP: 'GEOIP';

ppl/src/main/antlr/OpenSearchPPLParser.g4

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -927,6 +927,11 @@ expression
927927
| left = expression comparisonOperator right = expression # compareExpr
928928
| expression NOT? IN LT_PRTHS valueList RT_PRTHS # inExpr
929929
| expression NOT? BETWEEN expression AND expression # between
930+
| expression IS nullNotnull # isNullPredicate
931+
;
932+
933+
nullNotnull
934+
: NOT? NULL
930935
;
931936

932937

@@ -1610,6 +1615,8 @@ wildcard
16101615
keywordsCanBeId
16111616
: searchableKeyWord
16121617
| IN
1618+
| IS
1619+
| NULL
16131620
;
16141621

16151622
searchableKeyWord

ppl/src/main/java/org/opensearch/sql/ppl/parser/AstExpressionBuilder.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,15 @@ public UnresolvedExpression visitInExpr(InExprContext ctx) {
249249
return ctx.NOT() != null ? new Not(expr) : expr;
250250
}
251251

252+
@Override
253+
public UnresolvedExpression visitIsNullPredicate(OpenSearchPPLParser.IsNullPredicateContext ctx) {
254+
return new Function(
255+
ctx.nullNotnull().NOT() == null
256+
? IS_NULL.getName().getFunctionName()
257+
: IS_NOT_NULL.getName().getFunctionName(),
258+
Arrays.asList(visit(ctx.expression())));
259+
}
260+
252261
/** Value Expression. */
253262
@Override
254263
public UnresolvedExpression visitBinaryArithmetic(BinaryArithmeticContext ctx) {

ppl/src/test/java/org/opensearch/sql/ppl/parser/AstExpressionBuilderTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,24 @@ public void testBooleanIsNotNullFunction() {
299299
filter(relation("t"), function("is not null", field("a"))));
300300
}
301301

302+
@Test
303+
public void testIsNullPredicate() {
304+
assertEqual(
305+
"source=t | where a is null", filter(relation("t"), function("is null", field("a"))));
306+
assertEqual(
307+
"source=t | where a IS NULL", filter(relation("t"), function("is null", field("a"))));
308+
}
309+
310+
@Test
311+
public void testIsNotNullPredicate() {
312+
assertEqual(
313+
"source=t | where a is not null",
314+
filter(relation("t"), function("is not null", field("a"))));
315+
assertEqual(
316+
"source=t | where a IS NOT NULL",
317+
filter(relation("t"), function("is not null", field("a"))));
318+
}
319+
302320
/** Todo. search operator should not include functionCall, need to change antlr. */
303321
@Ignore("search operator should not include functionCall, need to change antlr")
304322
public void testEvalExpr() {

0 commit comments

Comments
 (0)