|
16 | 16 | import java.util.LinkedHashMap;
|
17 | 17 | import java.util.List;
|
18 | 18 | import java.util.Map;
|
| 19 | + |
19 | 20 | import org.junit.jupiter.api.Test;
|
20 | 21 | import org.junit.jupiter.api.extension.ExtendWith;
|
21 | 22 | import org.mockito.junit.jupiter.MockitoExtension;
|
22 | 23 | import org.opensearch.sql.data.model.ExprBooleanValue;
|
23 | 24 | import org.opensearch.sql.data.model.ExprCollectionValue;
|
24 | 25 | import org.opensearch.sql.data.model.ExprDoubleValue;
|
| 26 | +import org.opensearch.sql.data.model.ExprFloatValue; |
25 | 27 | import org.opensearch.sql.data.model.ExprIntegerValue;
|
26 | 28 | import org.opensearch.sql.data.model.ExprLongValue;
|
27 | 29 | import org.opensearch.sql.data.model.ExprNullValue;
|
|
32 | 34 | import org.opensearch.sql.exception.ExpressionEvaluationException;
|
33 | 35 | import org.opensearch.sql.exception.SemanticCheckException;
|
34 | 36 | import org.opensearch.sql.expression.DSL;
|
| 37 | +import org.opensearch.sql.expression.Expression; |
35 | 38 | import org.opensearch.sql.expression.FunctionExpression;
|
36 | 39 |
|
37 | 40 | @ExtendWith(MockitoExtension.class)
|
@@ -159,8 +162,107 @@ void json_returnsSemanticCheckException() {
|
159 | 162 | // missing bracket
|
160 | 163 | assertThrows(SemanticCheckException.class, () -> DSL.castJson(DSL.literal("{{[}}")).valueOf());
|
161 | 164 |
|
162 |
| - // mnissing quote |
| 165 | + // missing quote |
163 | 166 | assertThrows(
|
164 | 167 | SemanticCheckException.class, () -> DSL.castJson(DSL.literal("\"missing quote")).valueOf());
|
165 | 168 | }
|
| 169 | + |
| 170 | + @Test |
| 171 | + void json_extract_return_object() { |
| 172 | + List<String> validJsonStrings = |
| 173 | + List.of( |
| 174 | + // test json objects are valid |
| 175 | + "{\"a\":\"1\",\"b\":\"2\"}", |
| 176 | + "{\"a\":1,\"b\":{\"c\":2,\"d\":3}}", |
| 177 | + "{\"arr1\": [1,2,3], \"arr2\": [4,5,6]}", |
| 178 | + |
| 179 | + // test json arrays are valid |
| 180 | + "[1, 2, 3, 4]", |
| 181 | + "[{\"a\":1,\"b\":2}, {\"c\":3,\"d\":2}]", |
| 182 | + |
| 183 | + // test json scalars are valid |
| 184 | + "\"abc\"", |
| 185 | + "1234", |
| 186 | + "12.34", |
| 187 | + "true", |
| 188 | + "false", |
| 189 | + "null", |
| 190 | + |
| 191 | + // test empty string is valid |
| 192 | + ""); |
| 193 | + |
| 194 | + validJsonStrings.stream() |
| 195 | + .forEach( |
| 196 | + str -> |
| 197 | + assertEquals( |
| 198 | + LITERAL_TRUE, |
| 199 | + DSL.jsonValid(DSL.literal((ExprValueUtils.stringValue(str)))).valueOf(), |
| 200 | + String.format("String %s must be valid json", str))); |
| 201 | + } |
| 202 | + |
| 203 | + @Test |
| 204 | + void json_extract_search_arrays() { |
| 205 | + Expression jsonArray = DSL.literal(ExprValueUtils.stringValue("{\"a\":[1,2.3,\"abc\",true,null,{\"c\":1},[1,2,3]]}")); |
| 206 | + List<ExprValue> expectedExprValue = List.of( |
| 207 | + new ExprIntegerValue(1), |
| 208 | + new ExprFloatValue(2.3), |
| 209 | + new ExprStringValue("abc"), |
| 210 | + LITERAL_TRUE, |
| 211 | + LITERAL_NULL, |
| 212 | + ExprTupleValue.fromExprValueMap(Map.of("c", new ExprIntegerValue(1))), |
| 213 | + new ExprCollectionValue(List.of(new ExprIntegerValue(1), new ExprIntegerValue(2), new ExprIntegerValue(3))) |
| 214 | + ); |
| 215 | + |
| 216 | + // extract specific index from JSON list |
| 217 | + for (int i = 0 ; i < expectedExprValue.size() ; i++ ) { |
| 218 | + String path = String.format("$.a[%d]", i); |
| 219 | + Expression pathExpr = DSL.literal(ExprValueUtils.stringValue(path)); |
| 220 | + FunctionExpression expression = DSL.jsonExtract(jsonArray, pathExpr); |
| 221 | + assertEquals(expectedExprValue.get(i), expression.valueOf()); |
| 222 | + } |
| 223 | + |
| 224 | + // extract * from JSON list |
| 225 | + Expression starPath = DSL.literal(ExprValueUtils.stringValue("$.a[*]")); |
| 226 | + FunctionExpression starExpression = DSL.castInt(DSL.jsonExtract(jsonArray, starPath)); |
| 227 | + assertEquals( |
| 228 | + new ExprCollectionValue(expectedExprValue), starExpression.valueOf()); |
| 229 | + } |
| 230 | + |
| 231 | + @Test |
| 232 | + void json_extract_returns_null() { |
| 233 | + List<String> jsonStrings = |
| 234 | + List.of( |
| 235 | + "{\"a\":\"1\",\"b\":\"2\"}", |
| 236 | + "{\"a\":1,\"b\":{\"c\":2,\"d\":3}}", |
| 237 | + "{\"arr1\": [1,2,3], \"arr2\": [4,5,6]}", |
| 238 | + "[1, 2, 3, 4]", |
| 239 | + "[{\"a\":1,\"b\":2}, {\"c\":3,\"d\":2}]", |
| 240 | + "\"abc\"", |
| 241 | + "1234", |
| 242 | + "12.34", |
| 243 | + "true", |
| 244 | + "false", |
| 245 | + "null", |
| 246 | + ""); |
| 247 | + |
| 248 | + jsonStrings.stream() |
| 249 | + .forEach( |
| 250 | + str -> |
| 251 | + assertEquals( |
| 252 | + LITERAL_NULL, |
| 253 | + DSL.jsonExtract( |
| 254 | + DSL.literal((ExprValueUtils.stringValue(str))), |
| 255 | + DSL.literal("$.a.path_not_found_key")).valueOf(), |
| 256 | + String.format("JSON string %s should return null", str))); |
| 257 | + } |
| 258 | + |
| 259 | + @Test |
| 260 | + void json_returns_SemanticCheckException() { |
| 261 | + // invalid path |
| 262 | + assertThrows( |
| 263 | + SemanticCheckException.class, () -> DSL.jsonExtract(DSL.literal("invalid"), DSL.literal("invalid")).valueOf()); |
| 264 | + |
| 265 | + // invalid json |
| 266 | + assertThrows(SemanticCheckException.class, () -> DSL.jsonExtract(DSL.literal("{\"invalid\":\"json\", \"string\"}"), DSL.literal("invalid")).valueOf()); |
| 267 | + } |
166 | 268 | }
|
0 commit comments