Skip to content

Commit d0166fb

Browse files
committed
implement #N path key - N-th element of array
1 parent 058fb69 commit d0166fb

10 files changed

+130
-9
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ the simplest case path is just an key name. In general path is key names and
8989
placeholders combined by dot signs. Path can use following placeholders:
9090

9191
* `#` – any index of array;
92+
* `#N` – N-th index of array;
9293
* `%` – any key of object;
9394
* `*` – any sequence of array indexes and object keys;
9495
* `@#` – length of array or object, could be only used as last component of

expected/jsquery.out

+48
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,18 @@ select 'is.not < 1'::jsquery;
517517
"is"."not" < 1
518518
(1 row)
519519

520+
select 'a.b.#4 > 4'::jsquery;
521+
jsquery
522+
----------------
523+
"a"."b".#4 > 4
524+
(1 row)
525+
526+
select 'a.b.#10203.* > 4'::jsquery;
527+
jsquery
528+
----------------------
529+
"a"."b".#10203.* > 4
530+
(1 row)
531+
520532
select '{"a": {"b": null}}'::jsonb @@ 'a.b = 1';
521533
?column?
522534
----------
@@ -997,6 +1009,42 @@ select '{"a": {"b": 3, "c": "hey"}, "x": [5,6]}'::jsonb @@ '%=[5,6]';
9971009
t
9981010
(1 row)
9991011

1012+
select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.#1 = 2';
1013+
?column?
1014+
----------
1015+
t
1016+
(1 row)
1017+
1018+
select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.#2 = 2';
1019+
?column?
1020+
----------
1021+
f
1022+
(1 row)
1023+
1024+
select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.#3 = 2';
1025+
?column?
1026+
----------
1027+
f
1028+
(1 row)
1029+
1030+
select '{"a": {"b": [{"x":1},{"x":2},{"x":3}]}}'::jsonb @@ 'a.b.#1.x = 2';
1031+
?column?
1032+
----------
1033+
t
1034+
(1 row)
1035+
1036+
select '{"a": {"b": [{"x":1},{"x":2},{"x":3}]}}'::jsonb @@ 'a.b.#2.x = 2';
1037+
?column?
1038+
----------
1039+
f
1040+
(1 row)
1041+
1042+
select '{"a": {"b": [{"x":1},{"x":2},{"x":3}]}}'::jsonb @@ 'a.b.#3.x = 2';
1043+
?column?
1044+
----------
1045+
f
1046+
(1 row)
1047+
10001048
select '"XXX"'::jsonb @@ '$="XXX"';
10011049
?column?
10021050
----------

jsquery.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ typedef enum JsQueryItemType {
5656
jqiCurrent,
5757
jqiLength,
5858
jqiIn,
59-
jqiIs
59+
jqiIs,
60+
jqiIndexArray
6061
} JsQueryItemType;
6162

6263
/*
@@ -104,6 +105,8 @@ typedef struct JsQueryItem {
104105
int current;
105106
int32 *arrayPtr;
106107
} array;
108+
109+
uint32 arrayIndex;
107110
};
108111
} JsQueryItem;
109112

@@ -153,6 +156,8 @@ struct JsQueryParseItem {
153156
int nelems;
154157
JsQueryParseItem **elems;
155158
} array;
159+
160+
uint32 arrayIndex;
156161
};
157162
};
158163

jsquery_extract.c

+1
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ recursiveExtract(JsQueryItem *jsq, bool not, bool indirect, PathItem *path)
105105
pathItem->parent = path;
106106
jsqGetNext(jsq, &elem);
107107
return recursiveExtract(&elem, not, true, pathItem);
108+
case jqiIndexArray:
108109
case jqiAnyArray:
109110
case jqiAllArray:
110111
if ((not && jsq->type == jqiAnyArray) || (!not && jsq->type == jqiAllArray))

jsquery_gram.y

+24-6
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,16 @@ makeItemType(int type)
6565
return v;
6666
}
6767

68+
static JsQueryParseItem*
69+
makeIndexArray(string *s)
70+
{
71+
JsQueryParseItem* v = makeItemType(jqiIndexArray);
72+
73+
v->arrayIndex = pg_atoi(s->val, 4, 0);
74+
75+
return v;
76+
}
77+
6878
static JsQueryParseItem*
6979
makeItemString(string *s)
7080
{
@@ -213,13 +223,13 @@ makeItemList(List *list) {
213223
ARRAY_T FALSE_P NUMERIC_T OBJECT_T
214224
STRING_T BOOLEAN_T
215225

216-
%token <str> STRING_P NUMERIC_P
226+
%token <str> STRING_P NUMERIC_P INT_P
217227

218228
%type <value> result scalar_value
219229

220230
%type <elems> path value_list
221231

222-
%type <value> key key_any right_expr expr array
232+
%type <value> key key_any right_expr expr array numeric
223233

224234
%token <hint> HINT_P
225235

@@ -257,22 +267,28 @@ scalar_value:
257267
| STRING_T { $$ = makeItemString(&$1); }
258268
| BOOLEAN_T { $$ = makeItemString(&$1); }
259269
| NUMERIC_P { $$ = makeItemNumeric(&$1); }
270+
| INT_P { $$ = makeItemNumeric(&$1); }
260271
;
261272

262273
value_list:
263274
scalar_value { $$ = lappend(NIL, $1); }
264275
| value_list ',' scalar_value { $$ = lappend($1, $3); }
265276
;
266277

278+
numeric:
279+
NUMERIC_P { $$ = makeItemNumeric(&$1); }
280+
| INT_P { $$ = makeItemNumeric(&$1); }
281+
;
282+
267283
right_expr:
268284
'=' scalar_value { $$ = makeItemUnary(jqiEqual, $2); }
269285
| IN_P '(' value_list ')' { $$ = makeItemUnary(jqiIn, makeItemArray($3)); }
270286
| '=' array { $$ = makeItemUnary(jqiEqual, $2); }
271287
| '=' '*' { $$ = makeItemUnary(jqiEqual, makeItemType(jqiAny)); }
272-
| '<' NUMERIC_P { $$ = makeItemUnary(jqiLess, makeItemNumeric(&$2)); }
273-
| '>' NUMERIC_P { $$ = makeItemUnary(jqiGreater, makeItemNumeric(&$2)); }
274-
| '<' '=' NUMERIC_P { $$ = makeItemUnary(jqiLessOrEqual, makeItemNumeric(&$3)); }
275-
| '>' '=' NUMERIC_P { $$ = makeItemUnary(jqiGreaterOrEqual, makeItemNumeric(&$3)); }
288+
| '<' numeric { $$ = makeItemUnary(jqiLess, $2); }
289+
| '>' numeric { $$ = makeItemUnary(jqiGreater, $2); }
290+
| '<' '=' numeric { $$ = makeItemUnary(jqiLessOrEqual, $3); }
291+
| '>' '=' numeric { $$ = makeItemUnary(jqiGreaterOrEqual, $3); }
276292
| '@' '>' array { $$ = makeItemUnary(jqiContains, $3); }
277293
| '<' '@' array { $$ = makeItemUnary(jqiContained, $3); }
278294
| '&' '&' array { $$ = makeItemUnary(jqiOverlap, $3); }
@@ -311,6 +327,7 @@ key:
311327
| '%' ':' { $$ = makeItemType(jqiAllKey); }
312328
| '$' { $$ = makeItemType(jqiCurrent); }
313329
| '@' '#' { $$ = makeItemType(jqiLength); }
330+
| '#' INT_P { $$ = makeIndexArray(&$2); }
314331
| STRING_P { $$ = makeItemKey(&$1); }
315332
| IN_P { $$ = makeItemKey(&$1); }
316333
| IS_P { $$ = makeItemKey(&$1); }
@@ -325,6 +342,7 @@ key:
325342
| STRING_T { $$ = makeItemKey(&$1); }
326343
| BOOLEAN_T { $$ = makeItemKey(&$1); }
327344
| NUMERIC_P { $$ = makeItemKey(&$1); }
345+
| INT_P { $$ = makeItemKey(&$1); }
328346
;
329347

330348
/*

jsquery_io.c

+9-1
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ flattenJsQueryParseItem(StringInfo buf, JsQueryParseItem *item, bool onlyCurrent
113113
*(int32*)(buf->data + arg) = chld;
114114
}
115115
break;
116+
case jqiIndexArray:
117+
appendBinaryStringInfo(buf, (char*)&item->arrayIndex,
118+
sizeof(item->arrayIndex));
116119
case jqiAny:
117120
case jqiAnyArray:
118121
case jqiAnyKey:
@@ -272,7 +275,7 @@ printJsQueryItem(StringInfo buf, JsQueryItem *v, bool inKey, bool printBracketes
272275
break;
273276
case jqiArray:
274277
if (printBracketes)
275-
appendStringInfoChar(buf, '[');
278+
appendStringInfoChar(buf, '[');
276279

277280
while(jsqIterateArray(v, &elem))
278281
{
@@ -364,6 +367,11 @@ printJsQueryItem(StringInfo buf, JsQueryItem *v, bool inKey, bool printBracketes
364367
appendStringInfoChar(buf, '%');
365368
appendStringInfoChar(buf, ':');
366369
break;
370+
case jqiIndexArray:
371+
if (inKey)
372+
appendStringInfoChar(buf, '.');
373+
appendStringInfo(buf, "#%u", v->arrayIndex);
374+
break;
367375
default:
368376
elog(ERROR, "Unknown JsQueryItem type: %d", v->type);
369377
}

jsquery_op.c

+20
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,19 @@ recursiveExecute(JsQueryItem *jsq, JsonbValue *jb, JsQueryItem *jsqLeftArg)
518518
}
519519
}
520520
break;
521+
case jqiIndexArray:
522+
if (JsonbType(jb) == jbvArray)
523+
{
524+
JsonbValue *v;
525+
526+
jsqGetNext(jsq, &elem);
527+
528+
v = getIthJsonbValueFromContainer(jb->val.binary.data,
529+
jsq->arrayIndex);
530+
531+
res = v && recursiveExecute(&elem, v, NULL);
532+
}
533+
break;
521534
case jqiAnyKey:
522535
case jqiAllKey:
523536
if (JsonbType(jb) == jbvObject)
@@ -670,6 +683,10 @@ compareJsQuery(JsQueryItem *v1, JsQueryItem *v2)
670683
case jqiAllArray:
671684
case jqiAllKey:
672685
break;
686+
case jqiIndexArray:
687+
if (v1->arrayIndex != v2->arrayIndex)
688+
res = (v1->arrayIndex > v2->arrayIndex) ? 1 : -1;
689+
break;
673690
case jqiKey:
674691
case jqiString:
675692
{
@@ -964,6 +981,9 @@ hashJsQuery(JsQueryItem *v, pg_crc32 *crc)
964981
case jqiAllArray:
965982
case jqiAllKey:
966983
break;
984+
case jqiIndexArray:
985+
COMP_CRC32(*crc, &v->arrayIndex, sizeof(v->arrayIndex));
986+
break;
967987
default:
968988
elog(ERROR, "Unknown JsQueryItem type: %d", v->type);
969989
}

jsquery_scan.l

+7-1
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,19 @@ unicode \\u[0-9A-Fa-f]{4}
8585
return NUMERIC_P;
8686
}
8787
88-
<INITIAL>[+-]?[0-9]+ {
88+
<INITIAL>[+-][0-9]+ {
8989
addstring(true, yytext, yyleng);
9090
addchar(false, '\0');
9191
yylval->str = scanstring;
9292
return NUMERIC_P;
9393
}
9494
95+
<INITIAL>[0-9]+ {
96+
addstring(true, yytext, yyleng);
97+
addchar(false, '\0');
98+
yylval->str = scanstring;
99+
return INT_P;
100+
}
95101
96102
<INITIAL>{any}+ {
97103
addstring(true, yytext, yyleng);

jsquery_support.c

+4
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ jsqInitByBuffer(JsQueryItem *v, char *base, int32 pos)
8080
case jqiAllArray:
8181
case jqiAllKey:
8282
break;
83+
case jqiIndexArray:
84+
read_int32(v->arrayIndex, base, pos);
85+
break;
8386
case jqiKey:
8487
case jqiString:
8588
read_int32(v->value.datalen, base, pos);
@@ -144,6 +147,7 @@ jsqGetNext(JsQueryItem *v, JsQueryItem *a)
144147
Assert(
145148
v->type == jqiKey ||
146149
v->type == jqiAny ||
150+
v->type == jqiIndexArray ||
147151
v->type == jqiAnyArray ||
148152
v->type == jqiAnyKey ||
149153
v->type == jqiAll ||

sql/jsquery.sql

+10
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ select 'is.in < 1'::jsquery;
101101
select 'is.is < 1'::jsquery;
102102
select 'is.not < 1'::jsquery;
103103

104+
select 'a.b.#4 > 4'::jsquery;
105+
select 'a.b.#10203.* > 4'::jsquery;
106+
104107
select '{"a": {"b": null}}'::jsonb @@ 'a.b = 1';
105108
select '{"a": {"b": null}}'::jsonb @@ 'a.b = null';
106109
select '{"a": {"b": null}}'::jsonb @@ 'a.b = false';
@@ -202,6 +205,13 @@ select '{"a": {"b": 3, "c": "hey"}, "x": [5,6]}'::jsonb @@ '%.%="hey"';
202205
select '{"a": {"b": 3, "c": "hey"}, "x": [5,6]}'::jsonb @@ '%="hey"';
203206
select '{"a": {"b": 3, "c": "hey"}, "x": [5,6]}'::jsonb @@ '%=[5,6]';
204207

208+
select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.#1 = 2';
209+
select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.#2 = 2';
210+
select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.#3 = 2';
211+
select '{"a": {"b": [{"x":1},{"x":2},{"x":3}]}}'::jsonb @@ 'a.b.#1.x = 2';
212+
select '{"a": {"b": [{"x":1},{"x":2},{"x":3}]}}'::jsonb @@ 'a.b.#2.x = 2';
213+
select '{"a": {"b": [{"x":1},{"x":2},{"x":3}]}}'::jsonb @@ 'a.b.#3.x = 2';
214+
205215
select '"XXX"'::jsonb @@ '$="XXX"';
206216
select '"XXX"'::jsonb @@ '#.$="XXX"';
207217

0 commit comments

Comments
 (0)