Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 20b24cd

Browse files
author
Nikita Glukhov
committed
Add jsonpath .map() item method
1 parent 4cac4dd commit 20b24cd

File tree

7 files changed

+126
-3
lines changed

7 files changed

+126
-3
lines changed

src/backend/utils/adt/jsonpath.c

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
114114
case jpiPlus:
115115
case jpiMinus:
116116
case jpiExists:
117+
case jpiMap:
117118
{
118119
int32 arg;
119120

@@ -122,6 +123,7 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
122123

123124
chld = flattenJsonPathParseItem(buf, item->value.arg,
124125
item->type == jpiFilter ||
126+
item->type == jpiMap ||
125127
allowCurrent,
126128
insideArraySubscript);
127129
*(int32*)(buf->data + arg) = chld;
@@ -496,6 +498,12 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey, bool printBracket
496498
case jpiKeyValue:
497499
appendBinaryStringInfo(buf, ".keyvalue()", 11);
498500
break;
501+
case jpiMap:
502+
appendBinaryStringInfo(buf, ".map(", 5);
503+
jspGetArg(v, &elem);
504+
printJsonPathItem(buf, &elem, false, false);
505+
appendStringInfoChar(buf, ')');
506+
break;
499507
default:
500508
elog(ERROR, "Unknown jsonpath item type: %d", v->type);
501509
}
@@ -623,6 +631,7 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
623631
case jpiMinus:
624632
case jpiFilter:
625633
case jpiDatetime:
634+
case jpiMap:
626635
read_int32(v->content.arg, base, pos);
627636
break;
628637
case jpiIndexArray:
@@ -649,7 +658,8 @@ jspGetArg(JsonPathItem *v, JsonPathItem *a)
649658
v->type == jpiExists ||
650659
v->type == jpiPlus ||
651660
v->type == jpiMinus ||
652-
v->type == jpiDatetime
661+
v->type == jpiDatetime ||
662+
v->type == jpiMap
653663
);
654664

655665
jspInitByBuffer(a, v->base, v->content.arg);
@@ -701,7 +711,8 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
701711
v->type == jpiDouble ||
702712
v->type == jpiDatetime ||
703713
v->type == jpiKeyValue ||
704-
v->type == jpiStartsWith
714+
v->type == jpiStartsWith ||
715+
v->type == jpiMap
705716
);
706717

707718
if (a)

src/backend/utils/adt/jsonpath_exec.c

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1884,6 +1884,85 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
18841884
res = executeStartsWithPredicate(cxt, jsp, jb);
18851885
res = appendBoolResult(cxt, jsp, found, res, needBool);
18861886
break;
1887+
case jpiMap:
1888+
if (JsonbType(jb) != jbvArray)
1889+
{
1890+
if (cxt->lax)
1891+
{
1892+
JsonValueList reslist = { 0 };
1893+
1894+
jspGetArg(jsp, &elem);
1895+
res = recursiveExecute(cxt, &elem, jb, &reslist);
1896+
1897+
if (jperIsError(res))
1898+
return res;
1899+
1900+
if (JsonValueListLength(&reslist) != 1)
1901+
return jperMakeError(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED);
1902+
1903+
res = recursiveExecuteNext(cxt, jsp, NULL,
1904+
JsonValueListHead(&reslist),
1905+
found, true);
1906+
}
1907+
else
1908+
res = jperMakeError(ERRCODE_JSON_ARRAY_NOT_FOUND);
1909+
}
1910+
else
1911+
{
1912+
JsonbValue element_buf;
1913+
JsonbValue *element;
1914+
JsonbIterator *it = NULL;
1915+
JsonbIteratorToken tok;
1916+
JsonValueList result = { 0 };
1917+
int size = JsonbArraySize(jb);
1918+
int i;
1919+
1920+
jspGetArg(jsp, &elem);
1921+
1922+
if (jb->type == jbvBinary && size > 0)
1923+
{
1924+
element = &element_buf;
1925+
it = JsonbIteratorInit(jb->val.binary.data);
1926+
tok = JsonbIteratorNext(&it, &element_buf, false);
1927+
if (tok != WJB_BEGIN_ARRAY)
1928+
elog(ERROR, "unexpected jsonb token at the array start");
1929+
}
1930+
1931+
for (i = 0; i < size; i++)
1932+
{
1933+
JsonValueList reslist = { 0 };
1934+
1935+
if (it)
1936+
{
1937+
tok = JsonbIteratorNext(&it, element, true);
1938+
if (tok != WJB_ELEM)
1939+
break;
1940+
}
1941+
else
1942+
element = &jb->val.array.elems[i];
1943+
1944+
res = recursiveExecute(cxt, &elem, element, &reslist);
1945+
1946+
if (jperIsError(res))
1947+
break;
1948+
1949+
if (JsonValueListLength(&reslist) != 1)
1950+
{
1951+
res = jperMakeError(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED);
1952+
break;
1953+
}
1954+
1955+
JsonValueListConcat(&result, reslist);
1956+
}
1957+
1958+
if (jperIsError(res))
1959+
break;
1960+
1961+
res = recursiveExecuteNext(cxt, jsp, NULL,
1962+
wrapItemsInArray(&result),
1963+
found, false);
1964+
}
1965+
break;
18871966
default:
18881967
elog(ERROR,"2Wrong state: %d", jsp->type);
18891968
}

src/backend/utils/adt/jsonpath_gram.y

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,8 @@ makeAny(int first, int last)
224224

225225
%token <str> TO_P NULL_P TRUE_P FALSE_P IS_P UNKNOWN_P STARTS_P WITH_P
226226
%token <str> STRING_P NUMERIC_P INT_P EXISTS_P STRICT_P LAX_P LAST_P
227-
%token <str> ABS_P SIZE_P TYPE_P FLOOR_P DOUBLE_P CEILING_P DATETIME_P KEYVALUE_P
227+
%token <str> ABS_P SIZE_P TYPE_P FLOOR_P DOUBLE_P CEILING_P DATETIME_P
228+
%token <str> KEYVALUE_P MAP_P
228229

229230
%token <str> OR_P AND_P NOT_P
230231
%token <str> LESS_P LESSEQUAL_P EQUAL_P NOTEQUAL_P GREATEREQUAL_P GREATER_P
@@ -387,6 +388,8 @@ accessor_op:
387388
| '.' method '(' ')' { $$ = makeItemType($2); }
388389
| '.' DATETIME_P '(' opt_datetime_template ')'
389390
{ $$ = makeItemUnary(jpiDatetime, $4); }
391+
| '.' MAP_P '(' expr_or_predicate ')'
392+
{ $$ = makeItemUnary(jpiMap, $4); }
390393
| '?' '(' predicate ')' { $$ = makeItemUnary(jpiFilter, $3); }
391394
;
392395

@@ -422,6 +425,7 @@ key_name:
422425
| LAST_P
423426
| STARTS_P
424427
| WITH_P
428+
| MAP_P
425429
;
426430

427431
method:

src/backend/utils/adt/jsonpath_scan.l

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ static keyword keywords[] = {
255255
{ 2, false, TO_P, "to"},
256256
{ 3, false, ABS_P, "abs"},
257257
{ 3, false, LAX_P, "lax"},
258+
{ 3, false, MAP_P, "map"},
258259
{ 4, false, LAST_P, "last"},
259260
{ 4, true, NULL_P, "null"},
260261
{ 4, false, SIZE_P, "size"},

src/include/utils/jsonpath.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ typedef enum JsonPathItemType {
8383
jpiSubscript,
8484
jpiLast,
8585
jpiStartsWith,
86+
jpiMap,
8687
} JsonPathItemType;
8788

8889

src/test/regress/expected/jsonb_jsonpath.out

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1596,3 +1596,24 @@ SELECT jsonb '[{"a": 1}, {"a": 2}]' @? '$[*].a > 2';
15961596
f
15971597
(1 row)
15981598

1599+
-- extension: map item method
1600+
select _jsonpath_query(jsonb '1', 'strict $.map(@ + 10)');
1601+
ERROR: SQL/JSON array not found
1602+
select _jsonpath_query(jsonb '1', 'lax $.map(@ + 10)');
1603+
_jsonpath_query
1604+
-----------------
1605+
11
1606+
(1 row)
1607+
1608+
select _jsonpath_query(jsonb '[1, 2, 3]', '$.map(@ + 10)');
1609+
_jsonpath_query
1610+
-----------------
1611+
[11, 12, 13]
1612+
(1 row)
1613+
1614+
select _jsonpath_query(jsonb '[[1, 2], [3, 4, 5], [], [6, 7]]', '$.map(@.map(@ + 10))');
1615+
_jsonpath_query
1616+
----------------------------------------
1617+
[[11, 12], [13, 14, 15], [], [16, 17]]
1618+
(1 row)
1619+

src/test/regress/sql/jsonb_jsonpath.sql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,3 +373,9 @@ SELECT jsonb '[{"a": 1}, {"a": 2}]' @* '$[*] ? (@.a > 10)';
373373

374374
SELECT jsonb '[{"a": 1}, {"a": 2}]' @? '$[*].a > 1';
375375
SELECT jsonb '[{"a": 1}, {"a": 2}]' @? '$[*].a > 2';
376+
377+
-- extension: map item method
378+
select _jsonpath_query(jsonb '1', 'strict $.map(@ + 10)');
379+
select _jsonpath_query(jsonb '1', 'lax $.map(@ + 10)');
380+
select _jsonpath_query(jsonb '[1, 2, 3]', '$.map(@ + 10)');
381+
select _jsonpath_query(jsonb '[[1, 2], [3, 4, 5], [], [6, 7]]', '$.map(@.map(@ + 10))');

0 commit comments

Comments
 (0)