From 4c6b519f27279616a91f10c9d7572dc4d29159f6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 4 Nov 2025 05:16:46 +0000 Subject: [PATCH] Enhance tests and doc for eval isnull/isnotnull functions (#4724) * Enhance tests and doc for eval isnull/isnotnull functions Signed-off-by: Kai Huang * fix Signed-off-by: Kai Huang --------- Signed-off-by: Kai Huang (cherry picked from commit c9a8f47ea993c95bb6b13023de68ab54ddedba00) Signed-off-by: github-actions[bot] --- docs/user/ppl/functions/condition.rst | 94 +++++++++++++------ .../CalcitePPLConditionBuiltinFunctionIT.java | 67 +++++++++++++ 2 files changed, 131 insertions(+), 30 deletions(-) diff --git a/docs/user/ppl/functions/condition.rst b/docs/user/ppl/functions/condition.rst index 825c9de2fd3..132b4ab4b0e 100644 --- a/docs/user/ppl/functions/condition.rst +++ b/docs/user/ppl/functions/condition.rst @@ -14,9 +14,14 @@ ISNULL Description >>>>>>>>>>> -Usage: isnull(field) return true if field is null. +Usage: isnull(field) returns TRUE if field is NULL, FALSE otherwise. -Argument type: all the supported data type. +The `isnull()` function is commonly used: +- In `eval` expressions to create conditional fields +- With the `if()` function to provide default values +- In `where` clauses to filter null records + +Argument type: all the supported data types. Return type: BOOLEAN @@ -33,15 +38,44 @@ Example:: | True | null | Dale | +--------+----------+-----------+ +Using with if() to label records:: + + os> source=accounts | eval status = if(isnull(employer), 'unemployed', 'employed') | fields firstname, employer, status + fetched rows / total rows = 4/4 + +-----------+----------+------------+ + | firstname | employer | status | + |-----------+----------+------------| + | Amber | Pyrami | employed | + | Hattie | Netagy | employed | + | Nanette | Quility | employed | + | Dale | null | unemployed | + +-----------+----------+------------+ + +Filtering with where clause:: + + os> source=accounts | where isnull(employer) | fields account_number, firstname, employer + fetched rows / total rows = 1/1 + +----------------+-----------+----------+ + | account_number | firstname | employer | + |----------------+-----------+----------| + | 18 | Dale | null | + +----------------+-----------+----------+ + ISNOTNULL --------- Description >>>>>>>>>>> -Usage: isnotnull(field) return true if field is not null. +Usage: isnotnull(field) returns TRUE if field is NOT NULL, FALSE otherwise. -Argument type: all the supported data type. +The `isnotnull()` function is commonly used: +- In `eval` expressions to create boolean flags +- In `where` clauses to filter out null values +- With the `if()` function for conditional logic +- To validate data presence + +Argument type: all the supported data types. Return type: BOOLEAN @@ -49,6 +83,19 @@ Synonyms: `ISPRESENT`_ Example:: + os> source=accounts | eval has_employer = isnotnull(employer) | fields firstname, employer, has_employer + fetched rows / total rows = 4/4 + +-----------+----------+--------------+ + | firstname | employer | has_employer | + |-----------+----------+--------------| + | Amber | Pyrami | True | + | Hattie | Netagy | True | + | Nanette | Quility | True | + | Dale | null | False | + +-----------+----------+--------------+ + +Filtering with where clause:: + os> source=accounts | where not isnotnull(employer) | fields account_number, employer fetched rows / total rows = 1/1 +----------------+----------+ @@ -57,6 +104,19 @@ Example:: | 18 | null | +----------------+----------+ +Using with if() for validation messages:: + + os> source=accounts | eval validation = if(isnotnull(employer), 'valid', 'missing employer') | fields firstname, employer, validation + fetched rows / total rows = 4/4 + +-----------+----------+------------------+ + | firstname | employer | validation | + |-----------+----------+------------------| + | Amber | Pyrami | valid | + | Hattie | Netagy | valid | + | Nanette | Quility | valid | + | Dale | null | missing employer | + +-----------+----------+------------------+ + EXISTS ------ @@ -122,32 +182,6 @@ Example:: | null | null | Dale | +---------+----------+-----------+ - -ISNULL ------- - -Description ->>>>>>>>>>> - -Usage: isnull(field1, field2) return null if two parameters are same, otherwise return field1. - -Argument type: all the supported data type - -Return type: any - -Example:: - - os> source=accounts | eval result = isnull(employer) | fields result, employer, firstname - fetched rows / total rows = 4/4 - +--------+----------+-----------+ - | result | employer | firstname | - |--------+----------+-----------| - | False | Pyrami | Amber | - | False | Netagy | Hattie | - | False | Quility | Nanette | - | True | null | Dale | - +--------+----------+-----------+ - IF ------ diff --git a/integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalcitePPLConditionBuiltinFunctionIT.java b/integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalcitePPLConditionBuiltinFunctionIT.java index af2cf5e964d..f7c81e797df 100644 --- a/integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalcitePPLConditionBuiltinFunctionIT.java +++ b/integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalcitePPLConditionBuiltinFunctionIT.java @@ -335,4 +335,71 @@ public void testEarliestWithEval() throws IOException { verifyDataRows(actual, rows(false, true)); } + + @Test + public void testEvalIsNullWithIf() throws IOException { + JSONObject actual = + executeQuery( + String.format( + "source=%s | eval n=if(isnull(name), 'yes', 'no') | fields name, n", + TEST_INDEX_STATE_COUNTRY_WITH_NULL)); + + verifySchema(actual, schema("name", "string"), schema("n", "string")); + + verifyDataRows( + actual, + rows("John", "no"), + rows("Jane", "no"), + rows(null, "yes"), + rows("Jake", "no"), + rows("Kevin", "no"), + rows("Hello", "no"), + rows(" ", "no"), + rows("", "no")); + } + + @Test + public void testEvalIsNotNullDirect() throws IOException { + JSONObject actual = + executeQuery( + String.format( + "source=%s | eval is_not_null_name=isnotnull(name) | fields name, is_not_null_name", + TEST_INDEX_STATE_COUNTRY_WITH_NULL)); + + verifySchema(actual, schema("name", "string"), schema("is_not_null_name", "boolean")); + + verifyDataRows( + actual, + rows("John", true), + rows("Jane", true), + rows(null, false), + rows("Jake", true), + rows("Kevin", true), + rows("Hello", true), + rows(" ", true), + rows("", true)); + } + + @Test + public void testEvalIsNullInComplexExpression() throws IOException { + JSONObject actual = + executeQuery( + String.format( + "source=%s | eval safe_name=if(isnull(name), 'Unknown', name) | fields safe_name," + + " age", + TEST_INDEX_STATE_COUNTRY_WITH_NULL)); + + verifySchema(actual, schema("safe_name", "string"), schema("age", "int")); + + verifyDataRows( + actual, + rows("John", 25), + rows("Jane", 20), + rows("Unknown", 10), + rows("Jake", 70), + rows("Kevin", null), + rows("Hello", 30), + rows(" ", 27), + rows("", 57)); + } }