From 27e5214523199c10aed21161c15bef3a46c30663 Mon Sep 17 00:00:00 2001 From: Nikita Glukhov Date: Thu, 11 Apr 2019 02:38:30 +0300 Subject: [PATCH 01/21] Fix indexing of empty arrays and objects --- data/test_jsquery.data | 2 ++ expected/jsquery.out | 20 ++++++++++---------- jsonb_gin_ops.c | 10 ++-------- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/data/test_jsquery.data b/data/test_jsquery.data index 01d8660..add7fb5 100644 --- a/data/test_jsquery.data +++ b/data/test_jsquery.data @@ -1015,6 +1015,8 @@ {"t": "a"} {"t": true} {"t": false} +{} +[] [1, 2, 3] ["a", "b", "c"] 1 diff --git a/expected/jsquery.out b/expected/jsquery.out index 7a06abb..d552e6d 100644 --- a/expected/jsquery.out +++ b/expected/jsquery.out @@ -2631,13 +2631,13 @@ select count(*) from test_jsquery where v @@ '$ is numeric'; select count(*) from test_jsquery where v @@ '$ is array'; count ------- - 2 + 3 (1 row) select count(*) from test_jsquery where v @@ '$ is object'; count ------- - 1017 + 1018 (1 row) select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is numeric'; @@ -2655,7 +2655,7 @@ select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is string'; select count(*) from test_jsquery where v @@ 'NOT similar_product_ids.#: (NOT $ = "0440180295")'; count ------- - 40 + 42 (1 row) select count(*) from test_jsquery where v @@ '$ > 2'; @@ -2679,7 +2679,7 @@ select count(*) from test_jsquery where v @@ 't'; select count(*) from test_jsquery where v @@ '$'; count ------- - 1034 + 1036 (1 row) select count(*) from test_jsquery where v @@ 'similar_product_ids.#'; @@ -2909,13 +2909,13 @@ select count(*) from test_jsquery where v @@ '$ is numeric'; select count(*) from test_jsquery where v @@ '$ is array'; count ------- - 2 + 3 (1 row) select count(*) from test_jsquery where v @@ '$ is object'; count ------- - 1017 + 1018 (1 row) select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is numeric'; @@ -2957,7 +2957,7 @@ select count(*) from test_jsquery where v @@ 't'; select count(*) from test_jsquery where v @@ '$'; count ------- - 1034 + 1036 (1 row) select count(*) from test_jsquery where v @@ 'similar_product_ids.#'; @@ -3232,13 +3232,13 @@ select count(*) from test_jsquery where v @@ '$ is numeric'; select count(*) from test_jsquery where v @@ '$ is array'; count ------- - 2 + 3 (1 row) select count(*) from test_jsquery where v @@ '$ is object'; count ------- - 1017 + 1018 (1 row) select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is numeric'; @@ -3280,7 +3280,7 @@ select count(*) from test_jsquery where v @@ 't'; select count(*) from test_jsquery where v @@ '$'; count ------- - 1034 + 1036 (1 row) select count(*) from test_jsquery where v @@ 'similar_product_ids.#'; diff --git a/jsonb_gin_ops.c b/jsonb_gin_ops.c index a9bc22c..6776f23 100644 --- a/jsonb_gin_ops.c +++ b/jsonb_gin_ops.c @@ -656,10 +656,7 @@ gin_extract_jsonb_value_path_internal(Jsonb *jb, int32 *nentries, uint32 **bloom uint32 hash; if (total == 0) - { - *nentries = 0; - return NULL; - } + total = 2; /* single entry for empty object/array */ entries = (Datum *) palloc(sizeof(Datum) * total); if (bloom) @@ -1093,10 +1090,7 @@ gin_extract_jsonb_path_value_internal(Jsonb *jb, int32 *nentries) Datum *entries = NULL; if (total == 0) - { - *nentries = 0; - return NULL; - } + total = 2; /* single entry for empty object/array */ entries = (Datum *) palloc(sizeof(Datum) * total); From 3aa9cd9050897102cdeb136106cc04af8885e4fa Mon Sep 17 00:00:00 2001 From: Nikita Glukhov Date: Thu, 11 Apr 2019 04:18:06 +0300 Subject: [PATCH 02/21] Allow empty arrays in grammar --- jsquery_gram.y | 1 + 1 file changed, 1 insertion(+) diff --git a/jsquery_gram.y b/jsquery_gram.y index 2d6d531..3943abb 100644 --- a/jsquery_gram.y +++ b/jsquery_gram.y @@ -255,6 +255,7 @@ result: array: '[' value_list ']' { $$ = makeItemArray($2); } + | '[' ']' { $$ = makeItemArray(NIL); } ; scalar_value: From 29db97274659c81d05912be15e8fea1301a39049 Mon Sep 17 00:00:00 2001 From: Nikita Glukhov Date: Tue, 5 Sep 2017 19:04:00 +0300 Subject: [PATCH 03/21] Fix Assert() --- jsquery_support.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsquery_support.c b/jsquery_support.c index 08a805d..278e561 100644 --- a/jsquery_support.c +++ b/jsquery_support.c @@ -209,7 +209,7 @@ jsqGetNumeric(JsQueryItem *v) int32 jsqGetIsType(JsQueryItem *v) { - Assert(v->type = jqiIs); + Assert(v->type == jqiIs); return (int32)*v->value.data; } From d8567851e393ce2577eeb403341dd86b580457c5 Mon Sep 17 00:00:00 2001 From: Nikita Glukhov Date: Sat, 3 Feb 2018 18:53:22 +0300 Subject: [PATCH 04/21] Fix v10 build --- jsonb_gin_ops.c | 4 ++++ jsquery_op.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/jsonb_gin_ops.c b/jsonb_gin_ops.c index 6776f23..d72488e 100644 --- a/jsonb_gin_ops.c +++ b/jsonb_gin_ops.c @@ -24,6 +24,10 @@ #include "jsquery.h" +#ifdef PG_GETARG_JSONB_P +#define PG_GETARG_JSONB PG_GETARG_JSONB_P +#endif + typedef struct PathHashStack { uint32 hash; diff --git a/jsquery_op.c b/jsquery_op.c index e848421..0472314 100644 --- a/jsquery_op.c +++ b/jsquery_op.c @@ -29,6 +29,10 @@ #include "jsquery.h" +#ifdef PG_GETARG_JSONB_P +#define PG_GETARG_JSONB PG_GETARG_JSONB_P +#endif + typedef struct ResultAccum { StringInfo buf; bool missAppend; From 89f62ca3cb13c8aa3032138387212c51bd214d8e Mon Sep 17 00:00:00 2001 From: Nikita Glukhov Date: Sun, 17 Mar 2019 21:59:00 +0300 Subject: [PATCH 05/21] Fix ambiguity of operator @@ --- expected/jsquery.out | 482 +++++++++++++++++++------------------- sql/jsquery.sql | 534 +++++++++++++++++++++---------------------- 2 files changed, 508 insertions(+), 508 deletions(-) diff --git a/expected/jsquery.out b/expected/jsquery.out index d552e6d..5289fdf 100644 --- a/expected/jsquery.out +++ b/expected/jsquery.out @@ -530,325 +530,325 @@ select 'a.b.#10203.* > 4'::jsquery; "a"."b".#10203.* > 4 (1 row) -select '{"a": {"b": null}}'::jsonb @@ 'a.b = 1'; +select '{"a": {"b": null}}'::jsonb @@ 'a.b = 1'::jsquery; ?column? ---------- f (1 row) -select '{"a": {"b": null}}'::jsonb @@ 'a.b = null'; +select '{"a": {"b": null}}'::jsonb @@ 'a.b = null'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": null}}'::jsonb @@ 'a.b = false'; +select '{"a": {"b": null}}'::jsonb @@ 'a.b = false'::jsquery; ?column? ---------- f (1 row) -select '{"a": {"b": false}}'::jsonb @@ 'a.b = false'; +select '{"a": {"b": false}}'::jsonb @@ 'a.b = false'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": false}}'::jsonb @@ 'a.b = true'; +select '{"a": {"b": false}}'::jsonb @@ 'a.b = true'::jsquery; ?column? ---------- f (1 row) -select '{"a": {"b": true}}'::jsonb @@ 'a.b = true'; +select '{"a": {"b": true}}'::jsonb @@ 'a.b = true'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": 1}}'::jsonb @@ 'a.b = 1'; +select '{"a": {"b": 1}}'::jsonb @@ 'a.b = 1'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": 1}}'::jsonb @@ 'a.b < 1'; +select '{"a": {"b": 1}}'::jsonb @@ 'a.b < 1'::jsquery; ?column? ---------- f (1 row) -select '{"a": {"b": 1}}'::jsonb @@ 'a.b <= 1'; +select '{"a": {"b": 1}}'::jsonb @@ 'a.b <= 1'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": 1}}'::jsonb @@ 'a.b >= 1'; +select '{"a": {"b": 1}}'::jsonb @@ 'a.b >= 1'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": 1}}'::jsonb @@ 'a.b > 1'; +select '{"a": {"b": 1}}'::jsonb @@ 'a.b > 1'::jsquery; ?column? ---------- f (1 row) -select '{"a": {"b": 1}}'::jsonb @@ 'a.b = 2'; +select '{"a": {"b": 1}}'::jsonb @@ 'a.b = 2'::jsquery; ?column? ---------- f (1 row) -select '{"a": {"b": 1}}'::jsonb @@ 'a.b < 2'; +select '{"a": {"b": 1}}'::jsonb @@ 'a.b < 2'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": 1}}'::jsonb @@ 'a.b <= 2'; +select '{"a": {"b": 1}}'::jsonb @@ 'a.b <= 2'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": 1}}'::jsonb @@ 'a.b >= 2'; +select '{"a": {"b": 1}}'::jsonb @@ 'a.b >= 2'::jsquery; ?column? ---------- f (1 row) -select '{"a": {"b": 1}}'::jsonb @@ 'a.b > 2'; +select '{"a": {"b": 1}}'::jsonb @@ 'a.b > 2'::jsquery; ?column? ---------- f (1 row) -select '{"a": {"b": 1}}'::jsonb @@ 'a.b = 0'; +select '{"a": {"b": 1}}'::jsonb @@ 'a.b = 0'::jsquery; ?column? ---------- f (1 row) -select '{"a": {"b": 1}}'::jsonb @@ 'a.b < 0'; +select '{"a": {"b": 1}}'::jsonb @@ 'a.b < 0'::jsquery; ?column? ---------- f (1 row) -select '{"a": {"b": 1}}'::jsonb @@ 'a.b <= 0'; +select '{"a": {"b": 1}}'::jsonb @@ 'a.b <= 0'::jsquery; ?column? ---------- f (1 row) -select '{"a": {"b": 1}}'::jsonb @@ 'a.b >= 0'; +select '{"a": {"b": 1}}'::jsonb @@ 'a.b >= 0'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": 1}}'::jsonb @@ 'a.b > 0'; +select '{"a": {"b": 1}}'::jsonb @@ 'a.b > 0'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": 1}}'::jsonb @@ '*.b > 0'; +select '{"a": {"b": 1}}'::jsonb @@ '*.b > 0'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": 1}}'::jsonb @@ '*.b > 0'; +select '{"a": {"b": 1}}'::jsonb @@ '*.b > 0'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": 1}}'::jsonb @@ 'a.* > 0'; +select '{"a": {"b": 1}}'::jsonb @@ 'a.* > 0'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": 1}}'::jsonb @@ 'a.* > 0'; +select '{"a": {"b": 1}}'::jsonb @@ 'a.* > 0'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b && [ 1 ]'; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b && [ 1 ]'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b @> [ 1 ]'; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b @> [ 1 ]'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b <@ [ 1 ]'; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b <@ [ 1 ]'::jsquery; ?column? ---------- f (1 row) -select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b @> [ 1,2,3,4 ]'; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b @> [ 1,2,3,4 ]'::jsquery; ?column? ---------- f (1 row) -select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b <@ [ 1,2,3,4 ]'; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b <@ [ 1,2,3,4 ]'::jsquery; ?column? ---------- t (1 row) -select '[{"a": 2}, {"a": 3}]'::jsonb @@ '*.a = 4'; +select '[{"a": 2}, {"a": 3}]'::jsonb @@ '*.a = 4'::jsquery; ?column? ---------- f (1 row) -select '[{"a": 2}, {"a": 3}]'::jsonb @@ '*.a = 3'; +select '[{"a": 2}, {"a": 3}]'::jsonb @@ '*.a = 3'::jsquery; ?column? ---------- t (1 row) -select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '#.a = 4'; +select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '#.a = 4'::jsquery; ?column? ---------- f (1 row) -select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '*.a = 4'; +select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '*.a = 4'::jsquery; ?column? ---------- t (1 row) -select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '#(a = 1 OR a=3)'; +select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '#(a = 1 OR a=3)'::jsquery; ?column? ---------- t (1 row) -select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '#(a = 3 OR a=1)'; +select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '#(a = 3 OR a=1)'::jsquery; ?column? ---------- t (1 row) -select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '#(a = 3 and a=1)'; +select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '#(a = 3 and a=1)'::jsquery; ?column? ---------- f (1 row) -select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '#(a = 3 and a=2)' as "false"; +select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '#(a = 3 and a=2)'::jsquery as "false"; false ------- f (1 row) -select '[{"a": 2, "b":3}, {"a": 3, "b": 1}]'::jsonb @@ '#(b = 1 and a=3)'; +select '[{"a": 2, "b":3}, {"a": 3, "b": 1}]'::jsonb @@ '#(b = 1 and a=3)'::jsquery; ?column? ---------- t (1 row) -select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '#.a.a = 4'; +select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '#.a.a = 4'::jsquery; ?column? ---------- t (1 row) -select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '*.a.a = 4'; +select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '*.a.a = 4'::jsquery; ?column? ---------- t (1 row) -select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '*.#.a.a = 4'; +select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '*.#.a.a = 4'::jsquery; ?column? ---------- t (1 row) -select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '#.*.a.a = 4'; +select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '#.*.a.a = 4'::jsquery; ?column? ---------- t (1 row) -select '{"a": 1}'::jsonb @@ 'a in (0,1,2)'; +select '{"a": 1}'::jsonb @@ 'a in (0,1,2)'::jsquery; ?column? ---------- t (1 row) -select '{"a": 1}'::jsonb @@ 'a in (0,2)'; +select '{"a": 1}'::jsonb @@ 'a in (0,2)'::jsquery; ?column? ---------- f (1 row) -select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.#=2'; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.#=2'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b && [ 5 ]'; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b && [ 5 ]'::jsquery; ?column? ---------- f (1 row) -select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a=*'; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a=*'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b=*'; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b=*'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.c=*'; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.c=*'::jsquery; ?column? ---------- f (1 row) -select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b = [1,2,3]'; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b = [1,2,3]'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.# = [1,2,3]'; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.# = [1,2,3]'::jsquery; ?column? ---------- f (1 row) -select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b && [1,2,3]'; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b && [1,2,3]'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.# && [1,2,3]'; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.# && [1,2,3]'::jsquery; ?column? ---------- f @@ -890,175 +890,175 @@ select '{"x":[0,1,1,2]}'::jsonb @@ 'x @> [1,0,3]'::jsquery; f (1 row) -select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b && [ 2 ]'; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b && [ 2 ]'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b($ && [ 2 ])'; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b($ && [ 2 ])'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.$.b && [ 2 ]'; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.$.b && [ 2 ]'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.$.b ($ && [ 2 ])'; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.$.b ($ && [ 2 ])'::jsquery; ?column? ---------- t (1 row) -select '[1,2,3]'::jsonb @@ '# && [2]'; +select '[1,2,3]'::jsonb @@ '# && [2]'::jsquery; ?column? ---------- f (1 row) -select '[1,2,3]'::jsonb @@ '#($ && [2])'; +select '[1,2,3]'::jsonb @@ '#($ && [2])'::jsquery; ?column? ---------- f (1 row) -select '[1,2,3]'::jsonb @@ '$ && [2]'; +select '[1,2,3]'::jsonb @@ '$ && [2]'::jsquery; ?column? ---------- t (1 row) -select '[1,2,3]'::jsonb @@ '$ ($ && [2])'; +select '[1,2,3]'::jsonb @@ '$ ($ && [2])'::jsquery; ?column? ---------- t (1 row) -select '[1,2,3]'::jsonb @@ '$ = 2'; +select '[1,2,3]'::jsonb @@ '$ = 2'::jsquery; ?column? ---------- f (1 row) -select '[1,2,3]'::jsonb @@ '# = 2'; +select '[1,2,3]'::jsonb @@ '# = 2'::jsquery; ?column? ---------- t (1 row) -select '[1,2,3]'::jsonb @@ '#.$ = 2'; +select '[1,2,3]'::jsonb @@ '#.$ = 2'::jsquery; ?column? ---------- t (1 row) -select '[1,2,3]'::jsonb @@ '#($ = 2)'; +select '[1,2,3]'::jsonb @@ '#($ = 2)'::jsquery; ?column? ---------- t (1 row) -select '[3,4]'::jsonb @@ '#($ > 2 and $ < 5)'; +select '[3,4]'::jsonb @@ '#($ > 2 and $ < 5)'::jsquery; ?column? ---------- t (1 row) -select '[3,4]'::jsonb @@ '# > 2 and # < 5'; +select '[3,4]'::jsonb @@ '# > 2 and # < 5'::jsquery; ?column? ---------- t (1 row) -select '[1,6]'::jsonb @@ '#($ > 2 and $ < 5)'; +select '[1,6]'::jsonb @@ '#($ > 2 and $ < 5)'::jsquery; ?column? ---------- f (1 row) -select '[1,6]'::jsonb @@ '# > 2 and # < 5'; +select '[1,6]'::jsonb @@ '# > 2 and # < 5'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": 3, "c": "hey"}, "x": [5,6]}'::jsonb @@ '%.b=3'; +select '{"a": {"b": 3, "c": "hey"}, "x": [5,6]}'::jsonb @@ '%.b=3'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": 3, "c": "hey"}, "x": [5,6]}'::jsonb @@ 'a.%=3'; +select '{"a": {"b": 3, "c": "hey"}, "x": [5,6]}'::jsonb @@ 'a.%=3'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": 3, "c": "hey"}, "x": [5,6]}'::jsonb @@ '%.%="hey"'; +select '{"a": {"b": 3, "c": "hey"}, "x": [5,6]}'::jsonb @@ '%.%="hey"'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": 3, "c": "hey"}, "x": [5,6]}'::jsonb @@ '%="hey"'; +select '{"a": {"b": 3, "c": "hey"}, "x": [5,6]}'::jsonb @@ '%="hey"'::jsquery; ?column? ---------- f (1 row) -select '{"a": {"b": 3, "c": "hey"}, "x": [5,6]}'::jsonb @@ '%=[5,6]'; +select '{"a": {"b": 3, "c": "hey"}, "x": [5,6]}'::jsonb @@ '%=[5,6]'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.#1 = 2'; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.#1 = 2'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.#2 = 2'; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.#2 = 2'::jsquery; ?column? ---------- f (1 row) -select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.#3 = 2'; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.#3 = 2'::jsquery; ?column? ---------- f (1 row) -select '{"a": {"b": [{"x":1},{"x":2},{"x":3}]}}'::jsonb @@ 'a.b.#1.x = 2'; +select '{"a": {"b": [{"x":1},{"x":2},{"x":3}]}}'::jsonb @@ 'a.b.#1.x = 2'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": [{"x":1},{"x":2},{"x":3}]}}'::jsonb @@ 'a.b.#2.x = 2'; +select '{"a": {"b": [{"x":1},{"x":2},{"x":3}]}}'::jsonb @@ 'a.b.#2.x = 2'::jsquery; ?column? ---------- f (1 row) -select '{"a": {"b": [{"x":1},{"x":2},{"x":3}]}}'::jsonb @@ 'a.b.#3.x = 2'; +select '{"a": {"b": [{"x":1},{"x":2},{"x":3}]}}'::jsonb @@ 'a.b.#3.x = 2'::jsquery; ?column? ---------- f (1 row) -select '"XXX"'::jsonb @@ '$="XXX"'; +select '"XXX"'::jsonb @@ '$="XXX"'::jsquery; ?column? ---------- t (1 row) -select '"XXX"'::jsonb @@ '#.$="XXX"'; +select '"XXX"'::jsonb @@ '#.$="XXX"'::jsquery; ?column? ---------- f @@ -1071,19 +1071,19 @@ select 'a\t = "dollar \u0024 character"'::jsquery; "a\t" = "dollar $ character" (1 row) -select '{ "a": "dollar \u0024 character" }'::jsonb @@ '* = "dollar \u0024 character"'; +select '{ "a": "dollar \u0024 character" }'::jsonb @@ '* = "dollar \u0024 character"'::jsquery; ?column? ---------- t (1 row) -select '{ "a": "dollar \u0024 character" }'::jsonb @@ '* = "dollar $ character"'; +select '{ "a": "dollar \u0024 character" }'::jsonb @@ '* = "dollar $ character"'::jsquery; ?column? ---------- t (1 row) -select '{ "a": "dollar $ character" }'::jsonb @@ '* = "dollar \u0024 character"'; +select '{ "a": "dollar $ character" }'::jsonb @@ '* = "dollar \u0024 character"'::jsquery; ?column? ---------- t @@ -1471,31 +1471,31 @@ select 'a.$.? (b>0 and x.*= 0 ).c.k'::jsquery; "a".$. ?(("b" > 0 AND "x".* = 0)) ."c"."k" (1 row) -select '[{"a":1, "b":10}, {"a":2, "b":20}, {"a":3, "b":30}]'::jsonb @@ '#. ?(a < 0) (b=20)'; +select '[{"a":1, "b":10}, {"a":2, "b":20}, {"a":3, "b":30}]'::jsonb @@ '#. ?(a < 0) (b=20)'::jsquery; ?column? ---------- f (1 row) -select '[{"a":1, "b":10}, {"a":2, "b":20}, {"a":3, "b":30}]'::jsonb @@ '#. ?(a > 0) (b=20)'; +select '[{"a":1, "b":10}, {"a":2, "b":20}, {"a":3, "b":30}]'::jsonb @@ '#. ?(a > 0) (b=20)'::jsquery; ?column? ---------- t (1 row) -select '[{"a":1, "b":10}, {"a":2, "b":20}, {"a":3, "b":30}]'::jsonb @@ '#. ?(a > 1) (b=20)'; +select '[{"a":1, "b":10}, {"a":2, "b":20}, {"a":3, "b":30}]'::jsonb @@ '#. ?(a > 1) (b=20)'::jsquery; ?column? ---------- t (1 row) -select '[{"a":1, "b":10}, {"a":2, "b":20}, {"a":3, "b":30}]'::jsonb @@ '#. ?(a > 2) (b=20)'; +select '[{"a":1, "b":10}, {"a":2, "b":20}, {"a":3, "b":30}]'::jsonb @@ '#. ?(a > 2) (b=20)'::jsquery; ?column? ---------- f (1 row) -select '[{"a":1, "b":10}, {"a":2, "b":20}, {"a":3, "b":30}]'::jsonb @@ '#. ?(a > 3) (b=20)'; +select '[{"a":1, "b":10}, {"a":2, "b":20}, {"a":3, "b":30}]'::jsonb @@ '#. ?(a > 3) (b=20)'::jsquery; ?column? ---------- f @@ -1856,49 +1856,49 @@ select '[16]' @@ '(@# > 0 and #: = 16)'::jsquery; t (1 row) -select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.b or b.d'; +select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.b or b.d'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.c or b.d'; +select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.c or b.d'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.g or b.d'; +select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.g or b.d'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.g or b.c'; +select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.g or b.c'::jsquery; ?column? ---------- f (1 row) -select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.b and b.d'; +select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.b and b.d'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.c and b.d'; +select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.c and b.d'::jsquery; ?column? ---------- t (1 row) -select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.c and b.b'; +select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.c and b.b'::jsquery; ?column? ---------- f (1 row) -select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.g and b.d'; +select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.g and b.d'::jsquery; ?column? ---------- f @@ -2465,249 +2465,249 @@ select count(*) from test_jsquery where (v->>'review_helpful_votes')::int4 > 16 8 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes > 0'; +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 0'::jsquery; count ------- 654 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes > 19'; +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 19'::jsquery; count ------- 13 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes < 19'; +select count(*) from test_jsquery where v @@ 'review_helpful_votes < 19'::jsquery; count ------- 985 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes >= 19'; +select count(*) from test_jsquery where v @@ 'review_helpful_votes >= 19'::jsquery; count ------- 16 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes <= 19'; +select count(*) from test_jsquery where v @@ 'review_helpful_votes <= 19'::jsquery; count ------- 988 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes = 19'; +select count(*) from test_jsquery where v @@ 'review_helpful_votes = 19'::jsquery; count ------- 3 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16' AND - v @@ 'review_helpful_votes < 20'; +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16'::jsquery AND + v @@ 'review_helpful_votes < 20'::jsquery; count ------- 8 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16 and review_helpful_votes < 20'; +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16 and review_helpful_votes < 20'::jsquery; count ------- 8 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes ($ > 16 and $ < 20)'; +select count(*) from test_jsquery where v @@ 'review_helpful_votes ($ > 16 and $ < 20)'::jsquery; count ------- 8 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"]'; +select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"]'::jsquery; count ------- 7 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids(# = "0440180295") '; +select count(*) from test_jsquery where v @@ 'similar_product_ids(# = "0440180295") '::jsquery; count ------- 7 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids.#($ = "0440180295") '; +select count(*) from test_jsquery where v @@ 'similar_product_ids.#($ = "0440180295") '::jsquery; count ------- 7 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"] and product_sales_rank > 300000'; +select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"] and product_sales_rank > 300000'::jsquery; count ------- 4 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids <@ ["B00000DG0U", "B00004SQXU", "B0001XAM18", "B00000FDBU", "B00000FDBV", "B000002H2H", "B000002H6C", "B000002H5E", "B000002H97", "B000002HMH"]'; +select count(*) from test_jsquery where v @@ 'similar_product_ids <@ ["B00000DG0U", "B00004SQXU", "B0001XAM18", "B00000FDBU", "B00000FDBV", "B000002H2H", "B000002H6C", "B000002H5E", "B000002H97", "B000002HMH"]'::jsquery; count ------- 54 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids @> ["B000002H2H", "B000002H6C"]'; +select count(*) from test_jsquery where v @@ 'similar_product_ids @> ["B000002H2H", "B000002H6C"]'::jsquery; count ------- 3 (1 row) -select count(*) from test_jsquery where v @@ 'customer_id = null'; +select count(*) from test_jsquery where v @@ 'customer_id = null'::jsquery; count ------- 1 (1 row) -select count(*) from test_jsquery where v @@ 'review_votes = true'; +select count(*) from test_jsquery where v @@ 'review_votes = true'::jsquery; count ------- 1 (1 row) -select count(*) from test_jsquery where v @@ 'product_group = false'; +select count(*) from test_jsquery where v @@ 'product_group = false'::jsquery; count ------- 1 (1 row) -select count(*) from test_jsquery where v @@ 't = *'; +select count(*) from test_jsquery where v @@ 't = *'::jsquery; count ------- 10 (1 row) -select count(*) from test_jsquery where v @@ 't is boolean'; +select count(*) from test_jsquery where v @@ 't is boolean'::jsquery; count ------- 2 (1 row) -select count(*) from test_jsquery where v @@ 't is string'; +select count(*) from test_jsquery where v @@ 't is string'::jsquery; count ------- 2 (1 row) -select count(*) from test_jsquery where v @@ 't is numeric'; +select count(*) from test_jsquery where v @@ 't is numeric'::jsquery; count ------- 2 (1 row) -select count(*) from test_jsquery where v @@ 't is array'; +select count(*) from test_jsquery where v @@ 't is array'::jsquery; count ------- 2 (1 row) -select count(*) from test_jsquery where v @@ 't is object'; +select count(*) from test_jsquery where v @@ 't is object'::jsquery; count ------- 2 (1 row) -select count(*) from test_jsquery where v @@ '$ is boolean'; +select count(*) from test_jsquery where v @@ '$ is boolean'::jsquery; count ------- 3 (1 row) -select count(*) from test_jsquery where v @@ '$ is string'; +select count(*) from test_jsquery where v @@ '$ is string'::jsquery; count ------- 4 (1 row) -select count(*) from test_jsquery where v @@ '$ is numeric'; +select count(*) from test_jsquery where v @@ '$ is numeric'::jsquery; count ------- 5 (1 row) -select count(*) from test_jsquery where v @@ '$ is array'; +select count(*) from test_jsquery where v @@ '$ is array'::jsquery; count ------- 3 (1 row) -select count(*) from test_jsquery where v @@ '$ is object'; +select count(*) from test_jsquery where v @@ '$ is object'::jsquery; count ------- 1018 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is numeric'; +select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is numeric'::jsquery; count ------- 51 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is string'; +select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is string'::jsquery; count ------- 1001 (1 row) -select count(*) from test_jsquery where v @@ 'NOT similar_product_ids.#: (NOT $ = "0440180295")'; +select count(*) from test_jsquery where v @@ 'NOT similar_product_ids.#: (NOT $ = "0440180295")'::jsquery; count ------- 42 (1 row) -select count(*) from test_jsquery where v @@ '$ > 2'; +select count(*) from test_jsquery where v @@ '$ > 2'::jsquery; count ------- 3 (1 row) -select count(*) from test_jsquery where v @@ '$ = false'; +select count(*) from test_jsquery where v @@ '$ = false'::jsquery; count ------- 2 (1 row) -select count(*) from test_jsquery where v @@ 't'; +select count(*) from test_jsquery where v @@ 't'::jsquery; count ------- 10 (1 row) -select count(*) from test_jsquery where v @@ '$'; +select count(*) from test_jsquery where v @@ '$'::jsquery; count ------- 1036 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids.#'; +select count(*) from test_jsquery where v @@ 'similar_product_ids.#'::jsquery; count ------- 1001 (1 row) -select count(*) from test_jsquery where v @@ '$ . ? (review_votes > 10) . review_rating < 7'; +select count(*) from test_jsquery where v @@ '$ . ? (review_votes > 10) . review_rating < 7'::jsquery; count ------- 79 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids . ? (# = "B0002W4TL2") . $'; +select count(*) from test_jsquery where v @@ 'similar_product_ids . ? (# = "B0002W4TL2") . $'::jsquery; count ------- 3 (1 row) -select v from test_jsquery where v @@ 'array <@ [2,3]' order by v; +select v from test_jsquery where v @@ 'array <@ [2,3]'::jsquery order by v; v ------------------- {"array": [2]} {"array": [2, 3]} (2 rows) -select v from test_jsquery where v @@ 'array && [2,3]' order by v; +select v from test_jsquery where v @@ 'array && [2,3]'::jsquery order by v; v ---------------------- {"array": [2]} @@ -2717,7 +2717,7 @@ select v from test_jsquery where v @@ 'array && [2,3]' order by v; {"array": [3, 4, 5]} (5 rows) -select v from test_jsquery where v @@ 'array @> [2,3]' order by v; +select v from test_jsquery where v @@ 'array @> [2,3]'::jsquery order by v; v ---------------------- {"array": [2, 3]} @@ -2725,7 +2725,7 @@ select v from test_jsquery where v @@ 'array @> [2,3]' order by v; {"array": [2, 3, 4]} (3 rows) -select v from test_jsquery where v @@ 'array = [2,3]' order by v; +select v from test_jsquery where v @@ 'array = [2,3]'::jsquery order by v; v ------------------- {"array": [2, 3]} @@ -2733,7 +2733,7 @@ select v from test_jsquery where v @@ 'array = [2,3]' order by v; create index t_idx on test_jsquery using gin (v jsonb_value_path_ops); set enable_seqscan = off; -explain (costs off) select count(*) from test_jsquery where v @@ 'review_helpful_votes > 0'; +explain (costs off) select count(*) from test_jsquery where v @@ 'review_helpful_votes > 0'::jsquery; QUERY PLAN ------------------------------------------------------------------------ Aggregate @@ -2743,242 +2743,242 @@ explain (costs off) select count(*) from test_jsquery where v @@ 'review_helpful Index Cond: (v @@ '"review_helpful_votes" > 0'::jsquery) (5 rows) -select count(*) from test_jsquery where v @@ 'review_helpful_votes > 0'; +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 0'::jsquery; count ------- 654 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes > 19'; +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 19'::jsquery; count ------- 13 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes < 19'; +select count(*) from test_jsquery where v @@ 'review_helpful_votes < 19'::jsquery; count ------- 985 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes >= 19'; +select count(*) from test_jsquery where v @@ 'review_helpful_votes >= 19'::jsquery; count ------- 16 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes <= 19'; +select count(*) from test_jsquery where v @@ 'review_helpful_votes <= 19'::jsquery; count ------- 988 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes = 19'; +select count(*) from test_jsquery where v @@ 'review_helpful_votes = 19'::jsquery; count ------- 3 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16' AND - v @@ 'review_helpful_votes < 20'; +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16'::jsquery AND + v @@ 'review_helpful_votes < 20'::jsquery; count ------- 8 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16 and review_helpful_votes < 20'; +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16 and review_helpful_votes < 20'::jsquery; count ------- 8 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes ($ > 16 and $ < 20)'; +select count(*) from test_jsquery where v @@ 'review_helpful_votes ($ > 16 and $ < 20)'::jsquery; count ------- 8 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"]'; +select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"]'::jsquery; count ------- 7 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids(# = "0440180295") '; +select count(*) from test_jsquery where v @@ 'similar_product_ids(# = "0440180295") '::jsquery; count ------- 7 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids.#($ = "0440180295") '; +select count(*) from test_jsquery where v @@ 'similar_product_ids.#($ = "0440180295") '::jsquery; count ------- 7 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"] and product_sales_rank > 300000'; +select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"] and product_sales_rank > 300000'::jsquery; count ------- 4 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids <@ ["B00000DG0U", "B00004SQXU", "B0001XAM18", "B00000FDBU", "B00000FDBV", "B000002H2H", "B000002H6C", "B000002H5E", "B000002H97", "B000002HMH"]'; +select count(*) from test_jsquery where v @@ 'similar_product_ids <@ ["B00000DG0U", "B00004SQXU", "B0001XAM18", "B00000FDBU", "B00000FDBV", "B000002H2H", "B000002H6C", "B000002H5E", "B000002H97", "B000002HMH"]'::jsquery; count ------- 54 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids @> ["B000002H2H", "B000002H6C"]'; +select count(*) from test_jsquery where v @@ 'similar_product_ids @> ["B000002H2H", "B000002H6C"]'::jsquery; count ------- 3 (1 row) -select count(*) from test_jsquery where v @@ 'customer_id = null'; +select count(*) from test_jsquery where v @@ 'customer_id = null'::jsquery; count ------- 1 (1 row) -select count(*) from test_jsquery where v @@ 'review_votes = true'; +select count(*) from test_jsquery where v @@ 'review_votes = true'::jsquery; count ------- 1 (1 row) -select count(*) from test_jsquery where v @@ 'product_group = false'; +select count(*) from test_jsquery where v @@ 'product_group = false'::jsquery; count ------- 1 (1 row) -select count(*) from test_jsquery where v @@ 't = *'; +select count(*) from test_jsquery where v @@ 't = *'::jsquery; count ------- 10 (1 row) -select count(*) from test_jsquery where v @@ 't is boolean'; +select count(*) from test_jsquery where v @@ 't is boolean'::jsquery; count ------- 2 (1 row) -select count(*) from test_jsquery where v @@ 't is string'; +select count(*) from test_jsquery where v @@ 't is string'::jsquery; count ------- 2 (1 row) -select count(*) from test_jsquery where v @@ 't is numeric'; +select count(*) from test_jsquery where v @@ 't is numeric'::jsquery; count ------- 2 (1 row) -select count(*) from test_jsquery where v @@ 't is array'; +select count(*) from test_jsquery where v @@ 't is array'::jsquery; count ------- 2 (1 row) -select count(*) from test_jsquery where v @@ 't is object'; +select count(*) from test_jsquery where v @@ 't is object'::jsquery; count ------- 2 (1 row) -select count(*) from test_jsquery where v @@ '$ is boolean'; +select count(*) from test_jsquery where v @@ '$ is boolean'::jsquery; count ------- 3 (1 row) -select count(*) from test_jsquery where v @@ '$ is string'; +select count(*) from test_jsquery where v @@ '$ is string'::jsquery; count ------- 4 (1 row) -select count(*) from test_jsquery where v @@ '$ is numeric'; +select count(*) from test_jsquery where v @@ '$ is numeric'::jsquery; count ------- 5 (1 row) -select count(*) from test_jsquery where v @@ '$ is array'; +select count(*) from test_jsquery where v @@ '$ is array'::jsquery; count ------- 3 (1 row) -select count(*) from test_jsquery where v @@ '$ is object'; +select count(*) from test_jsquery where v @@ '$ is object'::jsquery; count ------- 1018 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is numeric'; +select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is numeric'::jsquery; count ------- 51 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is string'; +select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is string'::jsquery; count ------- 1001 (1 row) -select count(*) from test_jsquery where v @@ 'NOT similar_product_ids.#: (NOT $ = "0440180295")'; +select count(*) from test_jsquery where v @@ 'NOT similar_product_ids.#: (NOT $ = "0440180295")'::jsquery; count ------- 7 (1 row) -select count(*) from test_jsquery where v @@ '$ > 2'; +select count(*) from test_jsquery where v @@ '$ > 2'::jsquery; count ------- 3 (1 row) -select count(*) from test_jsquery where v @@ '$ = false'; +select count(*) from test_jsquery where v @@ '$ = false'::jsquery; count ------- 2 (1 row) -select count(*) from test_jsquery where v @@ 't'; +select count(*) from test_jsquery where v @@ 't'::jsquery; count ------- 10 (1 row) -select count(*) from test_jsquery where v @@ '$'; +select count(*) from test_jsquery where v @@ '$'::jsquery; count ------- 1036 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids.#'; +select count(*) from test_jsquery where v @@ 'similar_product_ids.#'::jsquery; count ------- 1001 (1 row) -select count(*) from test_jsquery where v @@ '$ . ? (review_votes > 10) . review_rating < 7'; +select count(*) from test_jsquery where v @@ '$ . ? (review_votes > 10) . review_rating < 7'::jsquery; count ------- 79 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids . ? (# = "B0002W4TL2") . $'; +select count(*) from test_jsquery where v @@ 'similar_product_ids . ? (# = "B0002W4TL2") . $'::jsquery; count ------- 3 (1 row) -explain (costs off) select v from test_jsquery where v @@ 'array <@ [2,3]' order by v; +explain (costs off) select v from test_jsquery where v @@ 'array <@ [2,3]'::jsquery order by v; QUERY PLAN --------------------------------------------------------------- Sort @@ -2989,7 +2989,7 @@ explain (costs off) select v from test_jsquery where v @@ 'array <@ [2,3]' order Index Cond: (v @@ '"array" <@ [2, 3]'::jsquery) (6 rows) -explain (costs off) select v from test_jsquery where v @@ 'array && [2,3]' order by v; +explain (costs off) select v from test_jsquery where v @@ 'array && [2,3]'::jsquery order by v; QUERY PLAN --------------------------------------------------------------- Sort @@ -3000,7 +3000,7 @@ explain (costs off) select v from test_jsquery where v @@ 'array && [2,3]' order Index Cond: (v @@ '"array" && [2, 3]'::jsquery) (6 rows) -explain (costs off) select v from test_jsquery where v @@ 'array @> [2,3]' order by v; +explain (costs off) select v from test_jsquery where v @@ 'array @> [2,3]'::jsquery order by v; QUERY PLAN --------------------------------------------------------------- Sort @@ -3011,7 +3011,7 @@ explain (costs off) select v from test_jsquery where v @@ 'array @> [2,3]' order Index Cond: (v @@ '"array" @> [2, 3]'::jsquery) (6 rows) -explain (costs off) select v from test_jsquery where v @@ 'array = [2,3]' order by v; +explain (costs off) select v from test_jsquery where v @@ 'array = [2,3]'::jsquery order by v; QUERY PLAN -------------------------------------------------------------- Sort @@ -3022,14 +3022,14 @@ explain (costs off) select v from test_jsquery where v @@ 'array = [2,3]' order Index Cond: (v @@ '"array" = [2, 3]'::jsquery) (6 rows) -select v from test_jsquery where v @@ 'array <@ [2,3]' order by v; +select v from test_jsquery where v @@ 'array <@ [2,3]'::jsquery order by v; v ------------------- {"array": [2]} {"array": [2, 3]} (2 rows) -select v from test_jsquery where v @@ 'array && [2,3]' order by v; +select v from test_jsquery where v @@ 'array && [2,3]'::jsquery order by v; v ---------------------- {"array": [2]} @@ -3039,7 +3039,7 @@ select v from test_jsquery where v @@ 'array && [2,3]' order by v; {"array": [3, 4, 5]} (5 rows) -select v from test_jsquery where v @@ 'array @> [2,3]' order by v; +select v from test_jsquery where v @@ 'array @> [2,3]'::jsquery order by v; v ---------------------- {"array": [2, 3]} @@ -3047,7 +3047,7 @@ select v from test_jsquery where v @@ 'array @> [2,3]' order by v; {"array": [2, 3, 4]} (3 rows) -select v from test_jsquery where v @@ 'array = [2,3]' order by v; +select v from test_jsquery where v @@ 'array = [2,3]'::jsquery order by v; v ------------------- {"array": [2, 3]} @@ -3056,7 +3056,7 @@ select v from test_jsquery where v @@ 'array = [2,3]' order by v; drop index t_idx; create index t_idx on test_jsquery using gin (v jsonb_path_value_ops); set enable_seqscan = off; -explain (costs off) select count(*) from test_jsquery where v @@ 'review_helpful_votes > 0'; +explain (costs off) select count(*) from test_jsquery where v @@ 'review_helpful_votes > 0'::jsquery; QUERY PLAN ------------------------------------------------------------------------ Aggregate @@ -3066,242 +3066,242 @@ explain (costs off) select count(*) from test_jsquery where v @@ 'review_helpful Index Cond: (v @@ '"review_helpful_votes" > 0'::jsquery) (5 rows) -select count(*) from test_jsquery where v @@ 'review_helpful_votes > 0'; +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 0'::jsquery; count ------- 654 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes > 19'; +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 19'::jsquery; count ------- 13 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes < 19'; +select count(*) from test_jsquery where v @@ 'review_helpful_votes < 19'::jsquery; count ------- 985 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes >= 19'; +select count(*) from test_jsquery where v @@ 'review_helpful_votes >= 19'::jsquery; count ------- 16 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes <= 19'; +select count(*) from test_jsquery where v @@ 'review_helpful_votes <= 19'::jsquery; count ------- 988 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes = 19'; +select count(*) from test_jsquery where v @@ 'review_helpful_votes = 19'::jsquery; count ------- 3 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16' AND - v @@ 'review_helpful_votes < 20'; +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16'::jsquery AND + v @@ 'review_helpful_votes < 20'::jsquery; count ------- 8 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16 and review_helpful_votes < 20'; +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16 and review_helpful_votes < 20'::jsquery; count ------- 8 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes ($ > 16 and $ < 20)'; +select count(*) from test_jsquery where v @@ 'review_helpful_votes ($ > 16 and $ < 20)'::jsquery; count ------- 8 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"]'; +select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"]'::jsquery; count ------- 7 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids(# = "0440180295") '; +select count(*) from test_jsquery where v @@ 'similar_product_ids(# = "0440180295") '::jsquery; count ------- 7 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids.#($ = "0440180295") '; +select count(*) from test_jsquery where v @@ 'similar_product_ids.#($ = "0440180295") '::jsquery; count ------- 7 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"] and product_sales_rank > 300000'; +select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"] and product_sales_rank > 300000'::jsquery; count ------- 4 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids <@ ["B00000DG0U", "B00004SQXU", "B0001XAM18", "B00000FDBU", "B00000FDBV", "B000002H2H", "B000002H6C", "B000002H5E", "B000002H97", "B000002HMH"]'; +select count(*) from test_jsquery where v @@ 'similar_product_ids <@ ["B00000DG0U", "B00004SQXU", "B0001XAM18", "B00000FDBU", "B00000FDBV", "B000002H2H", "B000002H6C", "B000002H5E", "B000002H97", "B000002HMH"]'::jsquery; count ------- 54 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids @> ["B000002H2H", "B000002H6C"]'; +select count(*) from test_jsquery where v @@ 'similar_product_ids @> ["B000002H2H", "B000002H6C"]'::jsquery; count ------- 3 (1 row) -select count(*) from test_jsquery where v @@ 'customer_id = null'; +select count(*) from test_jsquery where v @@ 'customer_id = null'::jsquery; count ------- 1 (1 row) -select count(*) from test_jsquery where v @@ 'review_votes = true'; +select count(*) from test_jsquery where v @@ 'review_votes = true'::jsquery; count ------- 1 (1 row) -select count(*) from test_jsquery where v @@ 'product_group = false'; +select count(*) from test_jsquery where v @@ 'product_group = false'::jsquery; count ------- 1 (1 row) -select count(*) from test_jsquery where v @@ 't = *'; +select count(*) from test_jsquery where v @@ 't = *'::jsquery; count ------- 10 (1 row) -select count(*) from test_jsquery where v @@ 't is boolean'; +select count(*) from test_jsquery where v @@ 't is boolean'::jsquery; count ------- 2 (1 row) -select count(*) from test_jsquery where v @@ 't is string'; +select count(*) from test_jsquery where v @@ 't is string'::jsquery; count ------- 2 (1 row) -select count(*) from test_jsquery where v @@ 't is numeric'; +select count(*) from test_jsquery where v @@ 't is numeric'::jsquery; count ------- 2 (1 row) -select count(*) from test_jsquery where v @@ 't is array'; +select count(*) from test_jsquery where v @@ 't is array'::jsquery; count ------- 2 (1 row) -select count(*) from test_jsquery where v @@ 't is object'; +select count(*) from test_jsquery where v @@ 't is object'::jsquery; count ------- 2 (1 row) -select count(*) from test_jsquery where v @@ '$ is boolean'; +select count(*) from test_jsquery where v @@ '$ is boolean'::jsquery; count ------- 3 (1 row) -select count(*) from test_jsquery where v @@ '$ is string'; +select count(*) from test_jsquery where v @@ '$ is string'::jsquery; count ------- 4 (1 row) -select count(*) from test_jsquery where v @@ '$ is numeric'; +select count(*) from test_jsquery where v @@ '$ is numeric'::jsquery; count ------- 5 (1 row) -select count(*) from test_jsquery where v @@ '$ is array'; +select count(*) from test_jsquery where v @@ '$ is array'::jsquery; count ------- 3 (1 row) -select count(*) from test_jsquery where v @@ '$ is object'; +select count(*) from test_jsquery where v @@ '$ is object'::jsquery; count ------- 1018 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is numeric'; +select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is numeric'::jsquery; count ------- 51 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is string'; +select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is string'::jsquery; count ------- 1001 (1 row) -select count(*) from test_jsquery where v @@ 'NOT similar_product_ids.#: (NOT $ = "0440180295")'; +select count(*) from test_jsquery where v @@ 'NOT similar_product_ids.#: (NOT $ = "0440180295")'::jsquery; count ------- 7 (1 row) -select count(*) from test_jsquery where v @@ '$ > 2'; +select count(*) from test_jsquery where v @@ '$ > 2'::jsquery; count ------- 3 (1 row) -select count(*) from test_jsquery where v @@ '$ = false'; +select count(*) from test_jsquery where v @@ '$ = false'::jsquery; count ------- 2 (1 row) -select count(*) from test_jsquery where v @@ 't'; +select count(*) from test_jsquery where v @@ 't'::jsquery; count ------- 10 (1 row) -select count(*) from test_jsquery where v @@ '$'; +select count(*) from test_jsquery where v @@ '$'::jsquery; count ------- 1036 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids.#'; +select count(*) from test_jsquery where v @@ 'similar_product_ids.#'::jsquery; count ------- 950 (1 row) -select count(*) from test_jsquery where v @@ '$ . ? (review_votes > 10) . review_rating < 7'; +select count(*) from test_jsquery where v @@ '$ . ? (review_votes > 10) . review_rating < 7'::jsquery; count ------- 79 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids . ? (# = "B0002W4TL2") . $'; +select count(*) from test_jsquery where v @@ 'similar_product_ids . ? (# = "B0002W4TL2") . $'::jsquery; count ------- 3 (1 row) -explain (costs off) select v from test_jsquery where v @@ 'array <@ [2,3]' order by v; +explain (costs off) select v from test_jsquery where v @@ 'array <@ [2,3]'::jsquery order by v; QUERY PLAN --------------------------------------------------------------- Sort @@ -3312,7 +3312,7 @@ explain (costs off) select v from test_jsquery where v @@ 'array <@ [2,3]' order Index Cond: (v @@ '"array" <@ [2, 3]'::jsquery) (6 rows) -explain (costs off) select v from test_jsquery where v @@ 'array && [2,3]' order by v; +explain (costs off) select v from test_jsquery where v @@ 'array && [2,3]'::jsquery order by v; QUERY PLAN --------------------------------------------------------------- Sort @@ -3323,7 +3323,7 @@ explain (costs off) select v from test_jsquery where v @@ 'array && [2,3]' order Index Cond: (v @@ '"array" && [2, 3]'::jsquery) (6 rows) -explain (costs off) select v from test_jsquery where v @@ 'array @> [2,3]' order by v; +explain (costs off) select v from test_jsquery where v @@ 'array @> [2,3]'::jsquery order by v; QUERY PLAN --------------------------------------------------------------- Sort @@ -3334,7 +3334,7 @@ explain (costs off) select v from test_jsquery where v @@ 'array @> [2,3]' order Index Cond: (v @@ '"array" @> [2, 3]'::jsquery) (6 rows) -explain (costs off) select v from test_jsquery where v @@ 'array = [2,3]' order by v; +explain (costs off) select v from test_jsquery where v @@ 'array = [2,3]'::jsquery order by v; QUERY PLAN -------------------------------------------------------------- Sort @@ -3345,14 +3345,14 @@ explain (costs off) select v from test_jsquery where v @@ 'array = [2,3]' order Index Cond: (v @@ '"array" = [2, 3]'::jsquery) (6 rows) -select v from test_jsquery where v @@ 'array <@ [2,3]' order by v; +select v from test_jsquery where v @@ 'array <@ [2,3]'::jsquery order by v; v ------------------- {"array": [2]} {"array": [2, 3]} (2 rows) -select v from test_jsquery where v @@ 'array && [2,3]' order by v; +select v from test_jsquery where v @@ 'array && [2,3]'::jsquery order by v; v ---------------------- {"array": [2]} @@ -3362,7 +3362,7 @@ select v from test_jsquery where v @@ 'array && [2,3]' order by v; {"array": [3, 4, 5]} (5 rows) -select v from test_jsquery where v @@ 'array @> [2,3]' order by v; +select v from test_jsquery where v @@ 'array @> [2,3]'::jsquery order by v; v ---------------------- {"array": [2, 3]} @@ -3370,7 +3370,7 @@ select v from test_jsquery where v @@ 'array @> [2,3]' order by v; {"array": [2, 3, 4]} (3 rows) -select v from test_jsquery where v @@ 'array = [2,3]' order by v; +select v from test_jsquery where v @@ 'array = [2,3]'::jsquery order by v; v ------------------- {"array": [2, 3]} diff --git a/sql/jsquery.sql b/sql/jsquery.sql index fb501b6..26487eb 100644 --- a/sql/jsquery.sql +++ b/sql/jsquery.sql @@ -105,74 +105,74 @@ select 'is.not < 1'::jsquery; select 'a.b.#4 > 4'::jsquery; select 'a.b.#10203.* > 4'::jsquery; -select '{"a": {"b": null}}'::jsonb @@ 'a.b = 1'; -select '{"a": {"b": null}}'::jsonb @@ 'a.b = null'; -select '{"a": {"b": null}}'::jsonb @@ 'a.b = false'; -select '{"a": {"b": false}}'::jsonb @@ 'a.b = false'; -select '{"a": {"b": false}}'::jsonb @@ 'a.b = true'; -select '{"a": {"b": true}}'::jsonb @@ 'a.b = true'; - - -select '{"a": {"b": 1}}'::jsonb @@ 'a.b = 1'; -select '{"a": {"b": 1}}'::jsonb @@ 'a.b < 1'; -select '{"a": {"b": 1}}'::jsonb @@ 'a.b <= 1'; -select '{"a": {"b": 1}}'::jsonb @@ 'a.b >= 1'; -select '{"a": {"b": 1}}'::jsonb @@ 'a.b > 1'; - -select '{"a": {"b": 1}}'::jsonb @@ 'a.b = 2'; -select '{"a": {"b": 1}}'::jsonb @@ 'a.b < 2'; -select '{"a": {"b": 1}}'::jsonb @@ 'a.b <= 2'; -select '{"a": {"b": 1}}'::jsonb @@ 'a.b >= 2'; -select '{"a": {"b": 1}}'::jsonb @@ 'a.b > 2'; - -select '{"a": {"b": 1}}'::jsonb @@ 'a.b = 0'; -select '{"a": {"b": 1}}'::jsonb @@ 'a.b < 0'; -select '{"a": {"b": 1}}'::jsonb @@ 'a.b <= 0'; -select '{"a": {"b": 1}}'::jsonb @@ 'a.b >= 0'; -select '{"a": {"b": 1}}'::jsonb @@ 'a.b > 0'; - -select '{"a": {"b": 1}}'::jsonb @@ '*.b > 0'; -select '{"a": {"b": 1}}'::jsonb @@ '*.b > 0'; -select '{"a": {"b": 1}}'::jsonb @@ 'a.* > 0'; -select '{"a": {"b": 1}}'::jsonb @@ 'a.* > 0'; - -select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b && [ 1 ]'; -select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b @> [ 1 ]'; -select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b <@ [ 1 ]'; -select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b @> [ 1,2,3,4 ]'; -select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b <@ [ 1,2,3,4 ]'; - -select '[{"a": 2}, {"a": 3}]'::jsonb @@ '*.a = 4'; -select '[{"a": 2}, {"a": 3}]'::jsonb @@ '*.a = 3'; - -select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '#.a = 4'; -select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '*.a = 4'; - -select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '#(a = 1 OR a=3)'; -select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '#(a = 3 OR a=1)'; -select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '#(a = 3 and a=1)'; -select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '#(a = 3 and a=2)' as "false"; -select '[{"a": 2, "b":3}, {"a": 3, "b": 1}]'::jsonb @@ '#(b = 1 and a=3)'; - -select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '#.a.a = 4'; -select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '*.a.a = 4'; -select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '*.#.a.a = 4'; -select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '#.*.a.a = 4'; - -select '{"a": 1}'::jsonb @@ 'a in (0,1,2)'; -select '{"a": 1}'::jsonb @@ 'a in (0,2)'; - -select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.#=2'; -select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b && [ 5 ]'; - -select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a=*'; -select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b=*'; -select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.c=*'; - -select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b = [1,2,3]'; -select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.# = [1,2,3]'; -select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b && [1,2,3]'; -select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.# && [1,2,3]'; +select '{"a": {"b": null}}'::jsonb @@ 'a.b = 1'::jsquery; +select '{"a": {"b": null}}'::jsonb @@ 'a.b = null'::jsquery; +select '{"a": {"b": null}}'::jsonb @@ 'a.b = false'::jsquery; +select '{"a": {"b": false}}'::jsonb @@ 'a.b = false'::jsquery; +select '{"a": {"b": false}}'::jsonb @@ 'a.b = true'::jsquery; +select '{"a": {"b": true}}'::jsonb @@ 'a.b = true'::jsquery; + + +select '{"a": {"b": 1}}'::jsonb @@ 'a.b = 1'::jsquery; +select '{"a": {"b": 1}}'::jsonb @@ 'a.b < 1'::jsquery; +select '{"a": {"b": 1}}'::jsonb @@ 'a.b <= 1'::jsquery; +select '{"a": {"b": 1}}'::jsonb @@ 'a.b >= 1'::jsquery; +select '{"a": {"b": 1}}'::jsonb @@ 'a.b > 1'::jsquery; + +select '{"a": {"b": 1}}'::jsonb @@ 'a.b = 2'::jsquery; +select '{"a": {"b": 1}}'::jsonb @@ 'a.b < 2'::jsquery; +select '{"a": {"b": 1}}'::jsonb @@ 'a.b <= 2'::jsquery; +select '{"a": {"b": 1}}'::jsonb @@ 'a.b >= 2'::jsquery; +select '{"a": {"b": 1}}'::jsonb @@ 'a.b > 2'::jsquery; + +select '{"a": {"b": 1}}'::jsonb @@ 'a.b = 0'::jsquery; +select '{"a": {"b": 1}}'::jsonb @@ 'a.b < 0'::jsquery; +select '{"a": {"b": 1}}'::jsonb @@ 'a.b <= 0'::jsquery; +select '{"a": {"b": 1}}'::jsonb @@ 'a.b >= 0'::jsquery; +select '{"a": {"b": 1}}'::jsonb @@ 'a.b > 0'::jsquery; + +select '{"a": {"b": 1}}'::jsonb @@ '*.b > 0'::jsquery; +select '{"a": {"b": 1}}'::jsonb @@ '*.b > 0'::jsquery; +select '{"a": {"b": 1}}'::jsonb @@ 'a.* > 0'::jsquery; +select '{"a": {"b": 1}}'::jsonb @@ 'a.* > 0'::jsquery; + +select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b && [ 1 ]'::jsquery; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b @> [ 1 ]'::jsquery; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b <@ [ 1 ]'::jsquery; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b @> [ 1,2,3,4 ]'::jsquery; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b <@ [ 1,2,3,4 ]'::jsquery; + +select '[{"a": 2}, {"a": 3}]'::jsonb @@ '*.a = 4'::jsquery; +select '[{"a": 2}, {"a": 3}]'::jsonb @@ '*.a = 3'::jsquery; + +select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '#.a = 4'::jsquery; +select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '*.a = 4'::jsquery; + +select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '#(a = 1 OR a=3)'::jsquery; +select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '#(a = 3 OR a=1)'::jsquery; +select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '#(a = 3 and a=1)'::jsquery; +select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '#(a = 3 and a=2)'::jsquery as "false"; +select '[{"a": 2, "b":3}, {"a": 3, "b": 1}]'::jsonb @@ '#(b = 1 and a=3)'::jsquery; + +select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '#.a.a = 4'::jsquery; +select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '*.a.a = 4'::jsquery; +select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '*.#.a.a = 4'::jsquery; +select '[{"a": 2}, {"a": 3}, {"a": {"a":4}}]'::jsonb @@ '#.*.a.a = 4'::jsquery; + +select '{"a": 1}'::jsonb @@ 'a in (0,1,2)'::jsquery; +select '{"a": 1}'::jsonb @@ 'a in (0,2)'::jsquery; + +select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.#=2'::jsquery; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b && [ 5 ]'::jsquery; + +select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a=*'::jsquery; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b=*'::jsquery; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.c=*'::jsquery; + +select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b = [1,2,3]'::jsquery; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.# = [1,2,3]'::jsquery; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b && [1,2,3]'::jsquery; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.# && [1,2,3]'::jsquery; select 'asd.# = 3'::jsquery & 'zzz = true' | 'xxx.# = zero'; select !'asd.# = 3'::jsquery & 'zzz = true' | !'xxx.# = zero'; @@ -182,47 +182,47 @@ select '{"x":[0,1,1,2]}'::jsonb @@ 'x @> [1,0]'::jsquery; select '{"x":[0,1,1,2]}'::jsonb @@ 'x @> [1,0,1]'::jsquery; select '{"x":[0,1,1,2]}'::jsonb @@ 'x @> [1,0,3]'::jsquery; -select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b && [ 2 ]'; -select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b($ && [ 2 ])'; -select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.$.b && [ 2 ]'; -select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.$.b ($ && [ 2 ])'; - -select '[1,2,3]'::jsonb @@ '# && [2]'; -select '[1,2,3]'::jsonb @@ '#($ && [2])'; -select '[1,2,3]'::jsonb @@ '$ && [2]'; -select '[1,2,3]'::jsonb @@ '$ ($ && [2])'; -select '[1,2,3]'::jsonb @@ '$ = 2'; -select '[1,2,3]'::jsonb @@ '# = 2'; -select '[1,2,3]'::jsonb @@ '#.$ = 2'; -select '[1,2,3]'::jsonb @@ '#($ = 2)'; - -select '[3,4]'::jsonb @@ '#($ > 2 and $ < 5)'; -select '[3,4]'::jsonb @@ '# > 2 and # < 5'; -select '[1,6]'::jsonb @@ '#($ > 2 and $ < 5)'; -select '[1,6]'::jsonb @@ '# > 2 and # < 5'; - -select '{"a": {"b": 3, "c": "hey"}, "x": [5,6]}'::jsonb @@ '%.b=3'; -select '{"a": {"b": 3, "c": "hey"}, "x": [5,6]}'::jsonb @@ 'a.%=3'; -select '{"a": {"b": 3, "c": "hey"}, "x": [5,6]}'::jsonb @@ '%.%="hey"'; -select '{"a": {"b": 3, "c": "hey"}, "x": [5,6]}'::jsonb @@ '%="hey"'; -select '{"a": {"b": 3, "c": "hey"}, "x": [5,6]}'::jsonb @@ '%=[5,6]'; - -select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.#1 = 2'; -select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.#2 = 2'; -select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.#3 = 2'; -select '{"a": {"b": [{"x":1},{"x":2},{"x":3}]}}'::jsonb @@ 'a.b.#1.x = 2'; -select '{"a": {"b": [{"x":1},{"x":2},{"x":3}]}}'::jsonb @@ 'a.b.#2.x = 2'; -select '{"a": {"b": [{"x":1},{"x":2},{"x":3}]}}'::jsonb @@ 'a.b.#3.x = 2'; - -select '"XXX"'::jsonb @@ '$="XXX"'; -select '"XXX"'::jsonb @@ '#.$="XXX"'; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b && [ 2 ]'::jsquery; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ '*.b($ && [ 2 ])'::jsquery; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.$.b && [ 2 ]'::jsquery; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.$.b ($ && [ 2 ])'::jsquery; + +select '[1,2,3]'::jsonb @@ '# && [2]'::jsquery; +select '[1,2,3]'::jsonb @@ '#($ && [2])'::jsquery; +select '[1,2,3]'::jsonb @@ '$ && [2]'::jsquery; +select '[1,2,3]'::jsonb @@ '$ ($ && [2])'::jsquery; +select '[1,2,3]'::jsonb @@ '$ = 2'::jsquery; +select '[1,2,3]'::jsonb @@ '# = 2'::jsquery; +select '[1,2,3]'::jsonb @@ '#.$ = 2'::jsquery; +select '[1,2,3]'::jsonb @@ '#($ = 2)'::jsquery; + +select '[3,4]'::jsonb @@ '#($ > 2 and $ < 5)'::jsquery; +select '[3,4]'::jsonb @@ '# > 2 and # < 5'::jsquery; +select '[1,6]'::jsonb @@ '#($ > 2 and $ < 5)'::jsquery; +select '[1,6]'::jsonb @@ '# > 2 and # < 5'::jsquery; + +select '{"a": {"b": 3, "c": "hey"}, "x": [5,6]}'::jsonb @@ '%.b=3'::jsquery; +select '{"a": {"b": 3, "c": "hey"}, "x": [5,6]}'::jsonb @@ 'a.%=3'::jsquery; +select '{"a": {"b": 3, "c": "hey"}, "x": [5,6]}'::jsonb @@ '%.%="hey"'::jsquery; +select '{"a": {"b": 3, "c": "hey"}, "x": [5,6]}'::jsonb @@ '%="hey"'::jsquery; +select '{"a": {"b": 3, "c": "hey"}, "x": [5,6]}'::jsonb @@ '%=[5,6]'::jsquery; + +select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.#1 = 2'::jsquery; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.#2 = 2'::jsquery; +select '{"a": {"b": [1,2,3]}}'::jsonb @@ 'a.b.#3 = 2'::jsquery; +select '{"a": {"b": [{"x":1},{"x":2},{"x":3}]}}'::jsonb @@ 'a.b.#1.x = 2'::jsquery; +select '{"a": {"b": [{"x":1},{"x":2},{"x":3}]}}'::jsonb @@ 'a.b.#2.x = 2'::jsquery; +select '{"a": {"b": [{"x":1},{"x":2},{"x":3}]}}'::jsonb @@ 'a.b.#3.x = 2'::jsquery; + +select '"XXX"'::jsonb @@ '$="XXX"'::jsquery; +select '"XXX"'::jsonb @@ '#.$="XXX"'::jsquery; --Unicode select 'a\t = "dollar \u0024 character"'::jsquery; -select '{ "a": "dollar \u0024 character" }'::jsonb @@ '* = "dollar \u0024 character"'; -select '{ "a": "dollar \u0024 character" }'::jsonb @@ '* = "dollar $ character"'; -select '{ "a": "dollar $ character" }'::jsonb @@ '* = "dollar \u0024 character"'; +select '{ "a": "dollar \u0024 character" }'::jsonb @@ '* = "dollar \u0024 character"'::jsquery; +select '{ "a": "dollar \u0024 character" }'::jsonb @@ '* = "dollar $ character"'::jsquery; +select '{ "a": "dollar $ character" }'::jsonb @@ '* = "dollar \u0024 character"'::jsquery; select 'a\r = "\n\""'::jsquery; select 'a\r = "\u0000"'::jsquery; select 'a\r = \u0000'::jsquery; @@ -301,11 +301,11 @@ select '?( not b>0). x'::jsquery; select 'a.?(b>0 and x= 0 ) .c'::jsquery; select 'a.$. ?(b>0 and x= 0 ) . c.k'::jsquery; select 'a.$.? (b>0 and x.*= 0 ).c.k'::jsquery; -select '[{"a":1, "b":10}, {"a":2, "b":20}, {"a":3, "b":30}]'::jsonb @@ '#. ?(a < 0) (b=20)'; -select '[{"a":1, "b":10}, {"a":2, "b":20}, {"a":3, "b":30}]'::jsonb @@ '#. ?(a > 0) (b=20)'; -select '[{"a":1, "b":10}, {"a":2, "b":20}, {"a":3, "b":30}]'::jsonb @@ '#. ?(a > 1) (b=20)'; -select '[{"a":1, "b":10}, {"a":2, "b":20}, {"a":3, "b":30}]'::jsonb @@ '#. ?(a > 2) (b=20)'; -select '[{"a":1, "b":10}, {"a":2, "b":20}, {"a":3, "b":30}]'::jsonb @@ '#. ?(a > 3) (b=20)'; +select '[{"a":1, "b":10}, {"a":2, "b":20}, {"a":3, "b":30}]'::jsonb @@ '#. ?(a < 0) (b=20)'::jsquery; +select '[{"a":1, "b":10}, {"a":2, "b":20}, {"a":3, "b":30}]'::jsonb @@ '#. ?(a > 0) (b=20)'::jsquery; +select '[{"a":1, "b":10}, {"a":2, "b":20}, {"a":3, "b":30}]'::jsonb @@ '#. ?(a > 1) (b=20)'::jsquery; +select '[{"a":1, "b":10}, {"a":2, "b":20}, {"a":3, "b":30}]'::jsonb @@ '#. ?(a > 2) (b=20)'::jsquery; +select '[{"a":1, "b":10}, {"a":2, "b":20}, {"a":3, "b":30}]'::jsonb @@ '#. ?(a > 3) (b=20)'::jsquery; select '[{"a":1, "b":10}, {"a":2, "b":20}]'::jsonb ~~ '#.a'; select '[{"a":1, "b":10}, {"a":2, "b":20}]'::jsonb ~~ '#. ?(a > 1). b'; select '[{"a":1, "b":10}, {"a":2, "b":20}, {"a":3, "b":30}]'::jsonb ~~ '# . ?(a > 1)'; @@ -369,14 +369,14 @@ SELECT 'test.# IN (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,2 select '[]' @@ '(@# > 0 and #: = 16)'::jsquery; select '[16]' @@ '(@# > 0 and #: = 16)'::jsquery; -select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.b or b.d'; -select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.c or b.d'; -select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.g or b.d'; -select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.g or b.c'; -select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.b and b.d'; -select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.c and b.d'; -select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.c and b.b'; -select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.g and b.d'; +select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.b or b.d'::jsquery; +select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.c or b.d'::jsquery; +select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.g or b.d'::jsquery; +select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.g or b.c'::jsquery; +select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.b and b.d'::jsquery; +select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.c and b.d'::jsquery; +select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.c and b.b'::jsquery; +select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb @@ 'a.g and b.d'::jsquery; select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb ~~ 'a.b and b.d'; select '{"a": {"b": 1, "c": 2}, "b": {"d":3}}'::jsonb ~~ 'a.b and b.c'; @@ -463,164 +463,164 @@ select count(*) from test_jsquery where (v->>'review_helpful_votes')::int4 > 16 (v->>'review_helpful_votes')::int4 < 20; -select count(*) from test_jsquery where v @@ 'review_helpful_votes > 0'; -select count(*) from test_jsquery where v @@ 'review_helpful_votes > 19'; -select count(*) from test_jsquery where v @@ 'review_helpful_votes < 19'; -select count(*) from test_jsquery where v @@ 'review_helpful_votes >= 19'; -select count(*) from test_jsquery where v @@ 'review_helpful_votes <= 19'; -select count(*) from test_jsquery where v @@ 'review_helpful_votes = 19'; -select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16' AND - v @@ 'review_helpful_votes < 20'; -select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16 and review_helpful_votes < 20'; -select count(*) from test_jsquery where v @@ 'review_helpful_votes ($ > 16 and $ < 20)'; -select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"]'; -select count(*) from test_jsquery where v @@ 'similar_product_ids(# = "0440180295") '; -select count(*) from test_jsquery where v @@ 'similar_product_ids.#($ = "0440180295") '; -select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"] and product_sales_rank > 300000'; -select count(*) from test_jsquery where v @@ 'similar_product_ids <@ ["B00000DG0U", "B00004SQXU", "B0001XAM18", "B00000FDBU", "B00000FDBV", "B000002H2H", "B000002H6C", "B000002H5E", "B000002H97", "B000002HMH"]'; -select count(*) from test_jsquery where v @@ 'similar_product_ids @> ["B000002H2H", "B000002H6C"]'; -select count(*) from test_jsquery where v @@ 'customer_id = null'; -select count(*) from test_jsquery where v @@ 'review_votes = true'; -select count(*) from test_jsquery where v @@ 'product_group = false'; -select count(*) from test_jsquery where v @@ 't = *'; -select count(*) from test_jsquery where v @@ 't is boolean'; -select count(*) from test_jsquery where v @@ 't is string'; -select count(*) from test_jsquery where v @@ 't is numeric'; -select count(*) from test_jsquery where v @@ 't is array'; -select count(*) from test_jsquery where v @@ 't is object'; -select count(*) from test_jsquery where v @@ '$ is boolean'; -select count(*) from test_jsquery where v @@ '$ is string'; -select count(*) from test_jsquery where v @@ '$ is numeric'; -select count(*) from test_jsquery where v @@ '$ is array'; -select count(*) from test_jsquery where v @@ '$ is object'; -select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is numeric'; -select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is string'; -select count(*) from test_jsquery where v @@ 'NOT similar_product_ids.#: (NOT $ = "0440180295")'; -select count(*) from test_jsquery where v @@ '$ > 2'; -select count(*) from test_jsquery where v @@ '$ = false'; -select count(*) from test_jsquery where v @@ 't'; -select count(*) from test_jsquery where v @@ '$'; -select count(*) from test_jsquery where v @@ 'similar_product_ids.#'; -select count(*) from test_jsquery where v @@ '$ . ? (review_votes > 10) . review_rating < 7'; -select count(*) from test_jsquery where v @@ 'similar_product_ids . ? (# = "B0002W4TL2") . $'; - -select v from test_jsquery where v @@ 'array <@ [2,3]' order by v; -select v from test_jsquery where v @@ 'array && [2,3]' order by v; -select v from test_jsquery where v @@ 'array @> [2,3]' order by v; -select v from test_jsquery where v @@ 'array = [2,3]' order by v; +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 0'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 19'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes < 19'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes >= 19'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes <= 19'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes = 19'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16'::jsquery AND + v @@ 'review_helpful_votes < 20'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16 and review_helpful_votes < 20'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes ($ > 16 and $ < 20)'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"]'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids(# = "0440180295") '::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids.#($ = "0440180295") '::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"] and product_sales_rank > 300000'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids <@ ["B00000DG0U", "B00004SQXU", "B0001XAM18", "B00000FDBU", "B00000FDBV", "B000002H2H", "B000002H6C", "B000002H5E", "B000002H97", "B000002HMH"]'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids @> ["B000002H2H", "B000002H6C"]'::jsquery; +select count(*) from test_jsquery where v @@ 'customer_id = null'::jsquery; +select count(*) from test_jsquery where v @@ 'review_votes = true'::jsquery; +select count(*) from test_jsquery where v @@ 'product_group = false'::jsquery; +select count(*) from test_jsquery where v @@ 't = *'::jsquery; +select count(*) from test_jsquery where v @@ 't is boolean'::jsquery; +select count(*) from test_jsquery where v @@ 't is string'::jsquery; +select count(*) from test_jsquery where v @@ 't is numeric'::jsquery; +select count(*) from test_jsquery where v @@ 't is array'::jsquery; +select count(*) from test_jsquery where v @@ 't is object'::jsquery; +select count(*) from test_jsquery where v @@ '$ is boolean'::jsquery; +select count(*) from test_jsquery where v @@ '$ is string'::jsquery; +select count(*) from test_jsquery where v @@ '$ is numeric'::jsquery; +select count(*) from test_jsquery where v @@ '$ is array'::jsquery; +select count(*) from test_jsquery where v @@ '$ is object'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is numeric'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is string'::jsquery; +select count(*) from test_jsquery where v @@ 'NOT similar_product_ids.#: (NOT $ = "0440180295")'::jsquery; +select count(*) from test_jsquery where v @@ '$ > 2'::jsquery; +select count(*) from test_jsquery where v @@ '$ = false'::jsquery; +select count(*) from test_jsquery where v @@ 't'::jsquery; +select count(*) from test_jsquery where v @@ '$'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids.#'::jsquery; +select count(*) from test_jsquery where v @@ '$ . ? (review_votes > 10) . review_rating < 7'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids . ? (# = "B0002W4TL2") . $'::jsquery; + +select v from test_jsquery where v @@ 'array <@ [2,3]'::jsquery order by v; +select v from test_jsquery where v @@ 'array && [2,3]'::jsquery order by v; +select v from test_jsquery where v @@ 'array @> [2,3]'::jsquery order by v; +select v from test_jsquery where v @@ 'array = [2,3]'::jsquery order by v; create index t_idx on test_jsquery using gin (v jsonb_value_path_ops); set enable_seqscan = off; -explain (costs off) select count(*) from test_jsquery where v @@ 'review_helpful_votes > 0'; - -select count(*) from test_jsquery where v @@ 'review_helpful_votes > 0'; -select count(*) from test_jsquery where v @@ 'review_helpful_votes > 19'; -select count(*) from test_jsquery where v @@ 'review_helpful_votes < 19'; -select count(*) from test_jsquery where v @@ 'review_helpful_votes >= 19'; -select count(*) from test_jsquery where v @@ 'review_helpful_votes <= 19'; -select count(*) from test_jsquery where v @@ 'review_helpful_votes = 19'; -select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16' AND - v @@ 'review_helpful_votes < 20'; -select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16 and review_helpful_votes < 20'; -select count(*) from test_jsquery where v @@ 'review_helpful_votes ($ > 16 and $ < 20)'; -select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"]'; -select count(*) from test_jsquery where v @@ 'similar_product_ids(# = "0440180295") '; -select count(*) from test_jsquery where v @@ 'similar_product_ids.#($ = "0440180295") '; -select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"] and product_sales_rank > 300000'; -select count(*) from test_jsquery where v @@ 'similar_product_ids <@ ["B00000DG0U", "B00004SQXU", "B0001XAM18", "B00000FDBU", "B00000FDBV", "B000002H2H", "B000002H6C", "B000002H5E", "B000002H97", "B000002HMH"]'; -select count(*) from test_jsquery where v @@ 'similar_product_ids @> ["B000002H2H", "B000002H6C"]'; -select count(*) from test_jsquery where v @@ 'customer_id = null'; -select count(*) from test_jsquery where v @@ 'review_votes = true'; -select count(*) from test_jsquery where v @@ 'product_group = false'; -select count(*) from test_jsquery where v @@ 't = *'; -select count(*) from test_jsquery where v @@ 't is boolean'; -select count(*) from test_jsquery where v @@ 't is string'; -select count(*) from test_jsquery where v @@ 't is numeric'; -select count(*) from test_jsquery where v @@ 't is array'; -select count(*) from test_jsquery where v @@ 't is object'; -select count(*) from test_jsquery where v @@ '$ is boolean'; -select count(*) from test_jsquery where v @@ '$ is string'; -select count(*) from test_jsquery where v @@ '$ is numeric'; -select count(*) from test_jsquery where v @@ '$ is array'; -select count(*) from test_jsquery where v @@ '$ is object'; -select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is numeric'; -select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is string'; -select count(*) from test_jsquery where v @@ 'NOT similar_product_ids.#: (NOT $ = "0440180295")'; -select count(*) from test_jsquery where v @@ '$ > 2'; -select count(*) from test_jsquery where v @@ '$ = false'; -select count(*) from test_jsquery where v @@ 't'; -select count(*) from test_jsquery where v @@ '$'; -select count(*) from test_jsquery where v @@ 'similar_product_ids.#'; -select count(*) from test_jsquery where v @@ '$ . ? (review_votes > 10) . review_rating < 7'; -select count(*) from test_jsquery where v @@ 'similar_product_ids . ? (# = "B0002W4TL2") . $'; - -explain (costs off) select v from test_jsquery where v @@ 'array <@ [2,3]' order by v; -explain (costs off) select v from test_jsquery where v @@ 'array && [2,3]' order by v; -explain (costs off) select v from test_jsquery where v @@ 'array @> [2,3]' order by v; -explain (costs off) select v from test_jsquery where v @@ 'array = [2,3]' order by v; - -select v from test_jsquery where v @@ 'array <@ [2,3]' order by v; -select v from test_jsquery where v @@ 'array && [2,3]' order by v; -select v from test_jsquery where v @@ 'array @> [2,3]' order by v; -select v from test_jsquery where v @@ 'array = [2,3]' order by v; +explain (costs off) select count(*) from test_jsquery where v @@ 'review_helpful_votes > 0'::jsquery; + +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 0'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 19'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes < 19'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes >= 19'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes <= 19'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes = 19'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16'::jsquery AND + v @@ 'review_helpful_votes < 20'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16 and review_helpful_votes < 20'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes ($ > 16 and $ < 20)'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"]'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids(# = "0440180295") '::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids.#($ = "0440180295") '::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"] and product_sales_rank > 300000'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids <@ ["B00000DG0U", "B00004SQXU", "B0001XAM18", "B00000FDBU", "B00000FDBV", "B000002H2H", "B000002H6C", "B000002H5E", "B000002H97", "B000002HMH"]'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids @> ["B000002H2H", "B000002H6C"]'::jsquery; +select count(*) from test_jsquery where v @@ 'customer_id = null'::jsquery; +select count(*) from test_jsquery where v @@ 'review_votes = true'::jsquery; +select count(*) from test_jsquery where v @@ 'product_group = false'::jsquery; +select count(*) from test_jsquery where v @@ 't = *'::jsquery; +select count(*) from test_jsquery where v @@ 't is boolean'::jsquery; +select count(*) from test_jsquery where v @@ 't is string'::jsquery; +select count(*) from test_jsquery where v @@ 't is numeric'::jsquery; +select count(*) from test_jsquery where v @@ 't is array'::jsquery; +select count(*) from test_jsquery where v @@ 't is object'::jsquery; +select count(*) from test_jsquery where v @@ '$ is boolean'::jsquery; +select count(*) from test_jsquery where v @@ '$ is string'::jsquery; +select count(*) from test_jsquery where v @@ '$ is numeric'::jsquery; +select count(*) from test_jsquery where v @@ '$ is array'::jsquery; +select count(*) from test_jsquery where v @@ '$ is object'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is numeric'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is string'::jsquery; +select count(*) from test_jsquery where v @@ 'NOT similar_product_ids.#: (NOT $ = "0440180295")'::jsquery; +select count(*) from test_jsquery where v @@ '$ > 2'::jsquery; +select count(*) from test_jsquery where v @@ '$ = false'::jsquery; +select count(*) from test_jsquery where v @@ 't'::jsquery; +select count(*) from test_jsquery where v @@ '$'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids.#'::jsquery; +select count(*) from test_jsquery where v @@ '$ . ? (review_votes > 10) . review_rating < 7'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids . ? (# = "B0002W4TL2") . $'::jsquery; + +explain (costs off) select v from test_jsquery where v @@ 'array <@ [2,3]'::jsquery order by v; +explain (costs off) select v from test_jsquery where v @@ 'array && [2,3]'::jsquery order by v; +explain (costs off) select v from test_jsquery where v @@ 'array @> [2,3]'::jsquery order by v; +explain (costs off) select v from test_jsquery where v @@ 'array = [2,3]'::jsquery order by v; + +select v from test_jsquery where v @@ 'array <@ [2,3]'::jsquery order by v; +select v from test_jsquery where v @@ 'array && [2,3]'::jsquery order by v; +select v from test_jsquery where v @@ 'array @> [2,3]'::jsquery order by v; +select v from test_jsquery where v @@ 'array = [2,3]'::jsquery order by v; drop index t_idx; create index t_idx on test_jsquery using gin (v jsonb_path_value_ops); set enable_seqscan = off; -explain (costs off) select count(*) from test_jsquery where v @@ 'review_helpful_votes > 0'; - -select count(*) from test_jsquery where v @@ 'review_helpful_votes > 0'; -select count(*) from test_jsquery where v @@ 'review_helpful_votes > 19'; -select count(*) from test_jsquery where v @@ 'review_helpful_votes < 19'; -select count(*) from test_jsquery where v @@ 'review_helpful_votes >= 19'; -select count(*) from test_jsquery where v @@ 'review_helpful_votes <= 19'; -select count(*) from test_jsquery where v @@ 'review_helpful_votes = 19'; -select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16' AND - v @@ 'review_helpful_votes < 20'; -select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16 and review_helpful_votes < 20'; -select count(*) from test_jsquery where v @@ 'review_helpful_votes ($ > 16 and $ < 20)'; -select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"]'; -select count(*) from test_jsquery where v @@ 'similar_product_ids(# = "0440180295") '; -select count(*) from test_jsquery where v @@ 'similar_product_ids.#($ = "0440180295") '; -select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"] and product_sales_rank > 300000'; -select count(*) from test_jsquery where v @@ 'similar_product_ids <@ ["B00000DG0U", "B00004SQXU", "B0001XAM18", "B00000FDBU", "B00000FDBV", "B000002H2H", "B000002H6C", "B000002H5E", "B000002H97", "B000002HMH"]'; -select count(*) from test_jsquery where v @@ 'similar_product_ids @> ["B000002H2H", "B000002H6C"]'; -select count(*) from test_jsquery where v @@ 'customer_id = null'; -select count(*) from test_jsquery where v @@ 'review_votes = true'; -select count(*) from test_jsquery where v @@ 'product_group = false'; -select count(*) from test_jsquery where v @@ 't = *'; -select count(*) from test_jsquery where v @@ 't is boolean'; -select count(*) from test_jsquery where v @@ 't is string'; -select count(*) from test_jsquery where v @@ 't is numeric'; -select count(*) from test_jsquery where v @@ 't is array'; -select count(*) from test_jsquery where v @@ 't is object'; -select count(*) from test_jsquery where v @@ '$ is boolean'; -select count(*) from test_jsquery where v @@ '$ is string'; -select count(*) from test_jsquery where v @@ '$ is numeric'; -select count(*) from test_jsquery where v @@ '$ is array'; -select count(*) from test_jsquery where v @@ '$ is object'; -select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is numeric'; -select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is string'; -select count(*) from test_jsquery where v @@ 'NOT similar_product_ids.#: (NOT $ = "0440180295")'; -select count(*) from test_jsquery where v @@ '$ > 2'; -select count(*) from test_jsquery where v @@ '$ = false'; -select count(*) from test_jsquery where v @@ 't'; -select count(*) from test_jsquery where v @@ '$'; -select count(*) from test_jsquery where v @@ 'similar_product_ids.#'; -select count(*) from test_jsquery where v @@ '$ . ? (review_votes > 10) . review_rating < 7'; -select count(*) from test_jsquery where v @@ 'similar_product_ids . ? (# = "B0002W4TL2") . $'; - -explain (costs off) select v from test_jsquery where v @@ 'array <@ [2,3]' order by v; -explain (costs off) select v from test_jsquery where v @@ 'array && [2,3]' order by v; -explain (costs off) select v from test_jsquery where v @@ 'array @> [2,3]' order by v; -explain (costs off) select v from test_jsquery where v @@ 'array = [2,3]' order by v; - -select v from test_jsquery where v @@ 'array <@ [2,3]' order by v; -select v from test_jsquery where v @@ 'array && [2,3]' order by v; -select v from test_jsquery where v @@ 'array @> [2,3]' order by v; -select v from test_jsquery where v @@ 'array = [2,3]' order by v; +explain (costs off) select count(*) from test_jsquery where v @@ 'review_helpful_votes > 0'::jsquery; + +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 0'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 19'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes < 19'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes >= 19'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes <= 19'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes = 19'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16'::jsquery AND + v @@ 'review_helpful_votes < 20'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16 and review_helpful_votes < 20'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes ($ > 16 and $ < 20)'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"]'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids(# = "0440180295") '::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids.#($ = "0440180295") '::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"] and product_sales_rank > 300000'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids <@ ["B00000DG0U", "B00004SQXU", "B0001XAM18", "B00000FDBU", "B00000FDBV", "B000002H2H", "B000002H6C", "B000002H5E", "B000002H97", "B000002HMH"]'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids @> ["B000002H2H", "B000002H6C"]'::jsquery; +select count(*) from test_jsquery where v @@ 'customer_id = null'::jsquery; +select count(*) from test_jsquery where v @@ 'review_votes = true'::jsquery; +select count(*) from test_jsquery where v @@ 'product_group = false'::jsquery; +select count(*) from test_jsquery where v @@ 't = *'::jsquery; +select count(*) from test_jsquery where v @@ 't is boolean'::jsquery; +select count(*) from test_jsquery where v @@ 't is string'::jsquery; +select count(*) from test_jsquery where v @@ 't is numeric'::jsquery; +select count(*) from test_jsquery where v @@ 't is array'::jsquery; +select count(*) from test_jsquery where v @@ 't is object'::jsquery; +select count(*) from test_jsquery where v @@ '$ is boolean'::jsquery; +select count(*) from test_jsquery where v @@ '$ is string'::jsquery; +select count(*) from test_jsquery where v @@ '$ is numeric'::jsquery; +select count(*) from test_jsquery where v @@ '$ is array'::jsquery; +select count(*) from test_jsquery where v @@ '$ is object'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is numeric'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is string'::jsquery; +select count(*) from test_jsquery where v @@ 'NOT similar_product_ids.#: (NOT $ = "0440180295")'::jsquery; +select count(*) from test_jsquery where v @@ '$ > 2'::jsquery; +select count(*) from test_jsquery where v @@ '$ = false'::jsquery; +select count(*) from test_jsquery where v @@ 't'::jsquery; +select count(*) from test_jsquery where v @@ '$'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids.#'::jsquery; +select count(*) from test_jsquery where v @@ '$ . ? (review_votes > 10) . review_rating < 7'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids . ? (# = "B0002W4TL2") . $'::jsquery; + +explain (costs off) select v from test_jsquery where v @@ 'array <@ [2,3]'::jsquery order by v; +explain (costs off) select v from test_jsquery where v @@ 'array && [2,3]'::jsquery order by v; +explain (costs off) select v from test_jsquery where v @@ 'array @> [2,3]'::jsquery order by v; +explain (costs off) select v from test_jsquery where v @@ 'array = [2,3]'::jsquery order by v; + +select v from test_jsquery where v @@ 'array <@ [2,3]'::jsquery order by v; +select v from test_jsquery where v @@ 'array && [2,3]'::jsquery order by v; +select v from test_jsquery where v @@ 'array @> [2,3]'::jsquery order by v; +select v from test_jsquery where v @@ 'array = [2,3]'::jsquery order by v; RESET enable_seqscan; From 2539974ce52e97aa2e2aabe9a7aee56ed2749299 Mon Sep 17 00:00:00 2001 From: Nikita Glukhov Date: Mon, 15 Apr 2019 17:01:23 +0300 Subject: [PATCH 06/21] Disallow 'NOT #:' and 'NOT %:' transformation --- expected/jsquery.out | 11 ++++------- jsquery_extract.c | 13 +++++++++---- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/expected/jsquery.out b/expected/jsquery.out index 5289fdf..c30e7fe 100644 --- a/expected/jsquery.out +++ b/expected/jsquery.out @@ -2161,7 +2161,7 @@ SELECT gin_debug_query_path_value('#:(NOT x=1) AND %:(NOT y=1) AND *:(NOT z=1)') SELECT gin_debug_query_path_value('NOT #:(NOT x=1) AND NOT %:(NOT y=1) AND NOT *:(NOT z=1)'); gin_debug_query_path_value ---------------------------- - #.x = 1 , entry 0 + + NULL + (1 row) @@ -2370,10 +2370,7 @@ SELECT gin_debug_query_value_path('#:(NOT x=1) AND %:(NOT y=1) AND *:(NOT z=1)') SELECT gin_debug_query_value_path('NOT #:(NOT x=1) AND NOT %:(NOT y=1) AND NOT *:(NOT z=1)'); gin_debug_query_value_path ---------------------------- - AND + - #.x = 1 , entry 0 + - %.y = 1 , entry 1 + - *.z = 1 , entry 2 + + *.z = 1 , entry 0 + (1 row) @@ -2933,7 +2930,7 @@ select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is string': select count(*) from test_jsquery where v @@ 'NOT similar_product_ids.#: (NOT $ = "0440180295")'::jsquery; count ------- - 7 + 42 (1 row) select count(*) from test_jsquery where v @@ '$ > 2'::jsquery; @@ -3256,7 +3253,7 @@ select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is string': select count(*) from test_jsquery where v @@ 'NOT similar_product_ids.#: (NOT $ = "0440180295")'::jsquery; count ------- - 7 + 42 (1 row) select count(*) from test_jsquery where v @@ '$ > 2'::jsquery; diff --git a/jsquery_extract.c b/jsquery_extract.c index 1b61267..75758b8 100644 --- a/jsquery_extract.c +++ b/jsquery_extract.c @@ -114,6 +114,7 @@ recursiveExtract(JsQueryItem *jsq, bool not, bool indirect, PathItem *path) return recursiveExtract(&elem, not, indirect, pathItem); case jqiAny: case jqiAll: + /* 'NOT *: (predicate)' is equivalent to '*: (NOT predicate)' */ if ((not && jsq->type == jqiAny) || (!not && jsq->type == jqiAll)) return NULL; pathItem = (PathItem *)palloc(sizeof(PathItem)); @@ -130,9 +131,11 @@ recursiveExtract(JsQueryItem *jsq, bool not, bool indirect, PathItem *path) if (!jsqGetNext(jsq, &elem)) return makeAnyNode(not, indirect, pathItem); return recursiveExtract(&elem, not, true, pathItem); - case jqiAnyArray: case jqiAllArray: - if ((not && jsq->type == jqiAnyArray) || (!not && jsq->type == jqiAllArray)) + /* 'NOT #: (predicate)' is not equivalent to '#: (NOT predicate)' */ + return NULL; + case jqiAnyArray: + if (not) return NULL; pathItem = (PathItem *)palloc(sizeof(PathItem)); pathItem->type = iAnyArray; @@ -140,9 +143,11 @@ recursiveExtract(JsQueryItem *jsq, bool not, bool indirect, PathItem *path) if (!jsqGetNext(jsq, &elem)) return makeAnyNode(not, indirect, pathItem); return recursiveExtract(&elem, not, true, pathItem); - case jqiAnyKey: case jqiAllKey: - if ((not && jsq->type == jqiAnyKey) || (!not && jsq->type == jqiAllKey)) + /* 'NOT %: (predicate)' is not equivalent to '%: (NOT predicate)' */ + return NULL; + case jqiAnyKey: + if (not) return NULL; pathItem = (PathItem *)palloc(sizeof(PathItem)); pathItem->type = iAnyKey; From 15087814bdff01d37bfec5166fa9d4e09227ac0a Mon Sep 17 00:00:00 2001 From: Nikita Glukhov Date: Mon, 15 Apr 2019 17:09:51 +0300 Subject: [PATCH 07/21] Fix '#' queries on empty arrays --- expected/jsquery.out | 4 ++-- jsquery_op.c | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/expected/jsquery.out b/expected/jsquery.out index c30e7fe..48837a9 100644 --- a/expected/jsquery.out +++ b/expected/jsquery.out @@ -2682,7 +2682,7 @@ select count(*) from test_jsquery where v @@ '$'::jsquery; select count(*) from test_jsquery where v @@ 'similar_product_ids.#'::jsquery; count ------- - 1001 + 950 (1 row) select count(*) from test_jsquery where v @@ '$ . ? (review_votes > 10) . review_rating < 7'::jsquery; @@ -2960,7 +2960,7 @@ select count(*) from test_jsquery where v @@ '$'::jsquery; select count(*) from test_jsquery where v @@ 'similar_product_ids.#'::jsquery; count ------- - 1001 + 950 (1 row) select count(*) from test_jsquery where v @@ '$ . ? (review_votes > 10) . review_rating < 7'::jsquery; diff --git a/jsquery_op.c b/jsquery_op.c index 0472314..3ecf3fd 100644 --- a/jsquery_op.c +++ b/jsquery_op.c @@ -604,7 +604,9 @@ recursiveExecute(JsQueryItem *jsq, JsonbValue *jb, JsQueryItem *jsqLeftArg, if (hasNext == false) { - res = true; + if (jsq->type == jqiAllArray || + JsonContainerSize(jb->val.binary.data) > 0) + res = true; while(ra && (r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE) if (r == WJB_ELEM) From d6d0109239d496090f632fad25a610f87b1e27b6 Mon Sep 17 00:00:00 2001 From: Nikita Glukhov Date: Thu, 11 Apr 2019 16:45:37 +0300 Subject: [PATCH 08/21] Add jsonb_laxpath_value_ops --- expected/jsquery.out | 522 +++++++++++++++++++++++++++++++++++++++++++ jsonb_gin_ops.c | 84 +++++-- jsquery--1.1.sql | 27 +++ sql/jsquery.sql | 84 +++++++ 4 files changed, 703 insertions(+), 14 deletions(-) diff --git a/expected/jsquery.out b/expected/jsquery.out index 48837a9..8553091 100644 --- a/expected/jsquery.out +++ b/expected/jsquery.out @@ -2188,6 +2188,205 @@ SELECT gin_debug_query_path_value('similar_product_ids . ? (# = "B0002W4TL2") . (1 row) +SELECT gin_debug_query_laxpath_value('NOT NOT NOT x(y(NOT (a=1) and NOT (b=2)) OR NOT NOT (c=3)) and z = 5'); + gin_debug_query_laxpath_value +------------------------------- + AND + + z = 5 , entry 0 + + OR + + x.y.a = 1 , entry 1 + + x.y.b = 2 , entry 2 + + +(1 row) + +SELECT gin_debug_query_laxpath_value('NOT #(x=1) and NOT *(y=1) and NOT %(z=1) '); + gin_debug_query_laxpath_value +------------------------------- + NULL + + +(1 row) + +SELECT gin_debug_query_laxpath_value('#(NOT x=1) and *(NOT y=1) and %(NOT z=1) '); + gin_debug_query_laxpath_value +------------------------------- + NULL + + +(1 row) + +SELECT gin_debug_query_laxpath_value('NOT #(NOT x=1) and NOT *(NOT y=1) and NOT %(NOT z=1) '); + gin_debug_query_laxpath_value +------------------------------- + NULL + + +(1 row) + +SELECT gin_debug_query_laxpath_value('#(x = "a" and y > 0 and y < 1 and z > 0)'); + gin_debug_query_laxpath_value +------------------------------- + #.x = "a" , entry 0 + + +(1 row) + +SELECT gin_debug_query_laxpath_value('#(x = "a" and y /*-- index */ >= 0 and y < 1 and z > 0)'); + gin_debug_query_laxpath_value +------------------------------- + AND + + #.x = "a" , entry 0 + + #.y >= 0 , < 1 , entry 1 + + +(1 row) + +SELECT gin_debug_query_laxpath_value('#(x /*-- noindex */ = "a" and y > 0 and y <= 1 and z /*-- index */ > 0)'); + gin_debug_query_laxpath_value +------------------------------- + AND + + #.y > 0 , <= 1 , entry 0 + + #.z > 0 , entry 1 + + +(1 row) + +SELECT gin_debug_query_laxpath_value('x = 1 and (y /*-- index */ > 0 and y < 1 OR z > 0)'); + gin_debug_query_laxpath_value +------------------------------- + AND + + x = 1 , entry 0 + + OR + + y > 0 , < 1 , entry 1 + + z > 0 , entry 2 + + +(1 row) + +SELECT gin_debug_query_laxpath_value('%.x = 1'); + gin_debug_query_laxpath_value +------------------------------- + NULL + + +(1 row) + +SELECT gin_debug_query_laxpath_value('*.x = "b"'); + gin_debug_query_laxpath_value +------------------------------- + NULL + + +(1 row) + +SELECT gin_debug_query_laxpath_value('x && [1,2,3]'); + gin_debug_query_laxpath_value +------------------------------- + OR + + x.# = 1 , entry 0 + + x.# = 2 , entry 1 + + x.# = 3 , entry 2 + + +(1 row) + +SELECT gin_debug_query_laxpath_value('x @> [1,2,3]'); + gin_debug_query_laxpath_value +------------------------------- + AND + + x.# = 1 , entry 0 + + x.# = 2 , entry 1 + + x.# = 3 , entry 2 + + +(1 row) + +SELECT gin_debug_query_laxpath_value('x <@ [1,2,3]'); + gin_debug_query_laxpath_value +------------------------------- + OR + + x = [] , entry 0 + + x.# = 1 , entry 1 + + x.# = 2 , entry 2 + + x.# = 3 , entry 3 + + +(1 row) + +SELECT gin_debug_query_laxpath_value('x = *'); + gin_debug_query_laxpath_value +------------------------------- + x = * , entry 0 + + +(1 row) + +SELECT gin_debug_query_laxpath_value('x is boolean'); + gin_debug_query_laxpath_value +------------------------------- + x IS boolean , entry 0 + + +(1 row) + +SELECT gin_debug_query_laxpath_value('x is string'); + gin_debug_query_laxpath_value +------------------------------- + x IS string , entry 0 + + +(1 row) + +SELECT gin_debug_query_laxpath_value('x is numeric'); + gin_debug_query_laxpath_value +------------------------------- + x IS numeric , entry 0 + + +(1 row) + +SELECT gin_debug_query_laxpath_value('x is array'); + gin_debug_query_laxpath_value +------------------------------- + x IS array , entry 0 + + +(1 row) + +SELECT gin_debug_query_laxpath_value('x is object'); + gin_debug_query_laxpath_value +------------------------------- + x IS object , entry 0 + + +(1 row) + +SELECT gin_debug_query_laxpath_value('#:(x=1) AND %:(y=1) AND *:(z=1)'); + gin_debug_query_laxpath_value +------------------------------- + NULL + + +(1 row) + +SELECT gin_debug_query_laxpath_value('#:(NOT x=1) AND %:(NOT y=1) AND *:(NOT z=1)'); + gin_debug_query_laxpath_value +------------------------------- + NULL + + +(1 row) + +SELECT gin_debug_query_laxpath_value('NOT #:(NOT x=1) AND NOT %:(NOT y=1) AND NOT *:(NOT z=1)'); + gin_debug_query_laxpath_value +------------------------------- + NULL + + +(1 row) + +SELECT gin_debug_query_laxpath_value('$ = true'); + gin_debug_query_laxpath_value +------------------------------- + $ = true , entry 0 + + +(1 row) + +SELECT gin_debug_query_laxpath_value('$ . ? (review_votes > 10) . review_rating < 7'); + gin_debug_query_laxpath_value +-------------------------------- + AND + + review_rating < 7 , entry 0 + + review_votes > 10 , entry 1 + + +(1 row) + +SELECT gin_debug_query_laxpath_value('similar_product_ids . ? (# = "B0002W4TL2") . $'); + gin_debug_query_laxpath_value +------------------------------------------------- + similar_product_ids.# = "B0002W4TL2" , entry 0 + + +(1 row) + SELECT gin_debug_query_value_path('NOT NOT NOT x(y(NOT (a=1) and NOT (b=2)) OR NOT NOT (c=3)) and z = 5'); gin_debug_query_value_path ---------------------------- @@ -3373,4 +3572,327 @@ select v from test_jsquery where v @@ 'array = [2,3]'::jsquery order by v; {"array": [2, 3]} (1 row) +drop index t_idx; +create index t_idx on test_jsquery using gin (v jsonb_laxpath_value_ops); +set enable_seqscan = off; +explain (costs off) select count(*) from test_jsquery where v @@ 'review_helpful_votes > 0'::jsquery; + QUERY PLAN +------------------------------------------------------------------------ + Aggregate + -> Bitmap Heap Scan on test_jsquery + Recheck Cond: (v @@ '"review_helpful_votes" > 0'::jsquery) + -> Bitmap Index Scan on t_idx + Index Cond: (v @@ '"review_helpful_votes" > 0'::jsquery) +(5 rows) + +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 0'::jsquery; + count +------- + 654 +(1 row) + +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 19'::jsquery; + count +------- + 13 +(1 row) + +select count(*) from test_jsquery where v @@ 'review_helpful_votes < 19'::jsquery; + count +------- + 985 +(1 row) + +select count(*) from test_jsquery where v @@ 'review_helpful_votes >= 19'::jsquery; + count +------- + 16 +(1 row) + +select count(*) from test_jsquery where v @@ 'review_helpful_votes <= 19'::jsquery; + count +------- + 988 +(1 row) + +select count(*) from test_jsquery where v @@ 'review_helpful_votes = 19'::jsquery; + count +------- + 3 +(1 row) + +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16'::jsquery AND + v @@ 'review_helpful_votes < 20'::jsquery; + count +------- + 8 +(1 row) + +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16 and review_helpful_votes < 20'::jsquery; + count +------- + 8 +(1 row) + +select count(*) from test_jsquery where v @@ 'review_helpful_votes ($ > 16 and $ < 20)'::jsquery; + count +------- + 8 +(1 row) + +select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"]'::jsquery; + count +------- + 7 +(1 row) + +select count(*) from test_jsquery where v @@ 'similar_product_ids(# = "0440180295") '::jsquery; + count +------- + 7 +(1 row) + +select count(*) from test_jsquery where v @@ 'similar_product_ids.#($ = "0440180295") '::jsquery; + count +------- + 7 +(1 row) + +select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"] and product_sales_rank > 300000'::jsquery; + count +------- + 4 +(1 row) + +select count(*) from test_jsquery where v @@ 'similar_product_ids <@ ["B00000DG0U", "B00004SQXU", "B0001XAM18", "B00000FDBU", "B00000FDBV", "B000002H2H", "B000002H6C", "B000002H5E", "B000002H97", "B000002HMH"]'::jsquery; + count +------- + 54 +(1 row) + +select count(*) from test_jsquery where v @@ 'similar_product_ids @> ["B000002H2H", "B000002H6C"]'::jsquery; + count +------- + 3 +(1 row) + +select count(*) from test_jsquery where v @@ 'customer_id = null'::jsquery; + count +------- + 1 +(1 row) + +select count(*) from test_jsquery where v @@ 'review_votes = true'::jsquery; + count +------- + 1 +(1 row) + +select count(*) from test_jsquery where v @@ 'product_group = false'::jsquery; + count +------- + 1 +(1 row) + +select count(*) from test_jsquery where v @@ 't = *'::jsquery; + count +------- + 10 +(1 row) + +select count(*) from test_jsquery where v @@ 't is boolean'::jsquery; + count +------- + 2 +(1 row) + +select count(*) from test_jsquery where v @@ 't is string'::jsquery; + count +------- + 2 +(1 row) + +select count(*) from test_jsquery where v @@ 't is numeric'::jsquery; + count +------- + 2 +(1 row) + +select count(*) from test_jsquery where v @@ 't is array'::jsquery; + count +------- + 2 +(1 row) + +select count(*) from test_jsquery where v @@ 't is object'::jsquery; + count +------- + 2 +(1 row) + +select count(*) from test_jsquery where v @@ '$ is boolean'::jsquery; + count +------- + 3 +(1 row) + +select count(*) from test_jsquery where v @@ '$ is string'::jsquery; + count +------- + 4 +(1 row) + +select count(*) from test_jsquery where v @@ '$ is numeric'::jsquery; + count +------- + 5 +(1 row) + +select count(*) from test_jsquery where v @@ '$ is array'::jsquery; + count +------- + 3 +(1 row) + +select count(*) from test_jsquery where v @@ '$ is object'::jsquery; + count +------- + 1018 +(1 row) + +select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is numeric'::jsquery; + count +------- + 51 +(1 row) + +select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is string'::jsquery; + count +------- + 1001 +(1 row) + +select count(*) from test_jsquery where v @@ 'NOT similar_product_ids.#: (NOT $ = "0440180295")'::jsquery; + count +------- + 42 +(1 row) + +select count(*) from test_jsquery where v @@ '$ > 2'::jsquery; + count +------- + 3 +(1 row) + +select count(*) from test_jsquery where v @@ '$ = false'::jsquery; + count +------- + 2 +(1 row) + +select count(*) from test_jsquery where v @@ 't'::jsquery; + count +------- + 10 +(1 row) + +select count(*) from test_jsquery where v @@ '$'::jsquery; + count +------- + 1036 +(1 row) + +select count(*) from test_jsquery where v @@ 'similar_product_ids.#'::jsquery; + count +------- + 950 +(1 row) + +select count(*) from test_jsquery where v @@ '$ . ? (review_votes > 10) . review_rating < 7'::jsquery; + count +------- + 79 +(1 row) + +select count(*) from test_jsquery where v @@ 'similar_product_ids . ? (# = "B0002W4TL2") . $'::jsquery; + count +------- + 3 +(1 row) + +explain (costs off) select v from test_jsquery where v @@ 'array <@ [2,3]'::jsquery order by v; + QUERY PLAN +--------------------------------------------------------------- + Sort + Sort Key: v + -> Bitmap Heap Scan on test_jsquery + Recheck Cond: (v @@ '"array" <@ [2, 3]'::jsquery) + -> Bitmap Index Scan on t_idx + Index Cond: (v @@ '"array" <@ [2, 3]'::jsquery) +(6 rows) + +explain (costs off) select v from test_jsquery where v @@ 'array && [2,3]'::jsquery order by v; + QUERY PLAN +--------------------------------------------------------------- + Sort + Sort Key: v + -> Bitmap Heap Scan on test_jsquery + Recheck Cond: (v @@ '"array" && [2, 3]'::jsquery) + -> Bitmap Index Scan on t_idx + Index Cond: (v @@ '"array" && [2, 3]'::jsquery) +(6 rows) + +explain (costs off) select v from test_jsquery where v @@ 'array @> [2,3]'::jsquery order by v; + QUERY PLAN +--------------------------------------------------------------- + Sort + Sort Key: v + -> Bitmap Heap Scan on test_jsquery + Recheck Cond: (v @@ '"array" @> [2, 3]'::jsquery) + -> Bitmap Index Scan on t_idx + Index Cond: (v @@ '"array" @> [2, 3]'::jsquery) +(6 rows) + +explain (costs off) select v from test_jsquery where v @@ 'array = [2,3]'::jsquery order by v; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: v + -> Bitmap Heap Scan on test_jsquery + Recheck Cond: (v @@ '"array" = [2, 3]'::jsquery) + -> Bitmap Index Scan on t_idx + Index Cond: (v @@ '"array" = [2, 3]'::jsquery) +(6 rows) + +select v from test_jsquery where v @@ 'array <@ [2,3]'::jsquery order by v; + v +------------------- + {"array": [2]} + {"array": [2, 3]} +(2 rows) + +select v from test_jsquery where v @@ 'array && [2,3]'::jsquery order by v; + v +---------------------- + {"array": [2]} + {"array": [2, 3]} + {"array": [1, 2, 3]} + {"array": [2, 3, 4]} + {"array": [3, 4, 5]} +(5 rows) + +select v from test_jsquery where v @@ 'array @> [2,3]'::jsquery order by v; + v +---------------------- + {"array": [2, 3]} + {"array": [1, 2, 3]} + {"array": [2, 3, 4]} +(3 rows) + +select v from test_jsquery where v @@ 'array = [2,3]'::jsquery order by v; + v +------------------- + {"array": [2, 3]} +(1 row) + RESET enable_seqscan; diff --git a/jsonb_gin_ops.c b/jsonb_gin_ops.c index d72488e..237c32b 100644 --- a/jsonb_gin_ops.c +++ b/jsonb_gin_ops.c @@ -69,6 +69,12 @@ typedef struct int count, total; } Entries; +typedef struct PathValueExtra +{ + Entries *entries; + bool lax; +} PathValueExtra; + typedef struct { ExtractedNode *root; @@ -107,18 +113,24 @@ Datum gin_debug_query_value_path(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(gin_compare_jsonb_path_value); PG_FUNCTION_INFO_V1(gin_compare_partial_jsonb_path_value); PG_FUNCTION_INFO_V1(gin_extract_jsonb_path_value); +PG_FUNCTION_INFO_V1(gin_extract_jsonb_laxpath_value); PG_FUNCTION_INFO_V1(gin_extract_jsonb_query_path_value); +PG_FUNCTION_INFO_V1(gin_extract_jsonb_query_laxpath_value); PG_FUNCTION_INFO_V1(gin_consistent_jsonb_path_value); PG_FUNCTION_INFO_V1(gin_triconsistent_jsonb_path_value); PG_FUNCTION_INFO_V1(gin_debug_query_path_value); +PG_FUNCTION_INFO_V1(gin_debug_query_laxpath_value); Datum gin_compare_jsonb_path_value(PG_FUNCTION_ARGS); Datum gin_compare_partial_jsonb_path_value(PG_FUNCTION_ARGS); Datum gin_extract_jsonb_path_value(PG_FUNCTION_ARGS); +Datum gin_extract_jsonb_laxpath_value(PG_FUNCTION_ARGS); Datum gin_extract_jsonb_query_path_value(PG_FUNCTION_ARGS); +Datum gin_extract_jsonb_query_laxpath_value(PG_FUNCTION_ARGS); Datum gin_consistent_jsonb_path_value(PG_FUNCTION_ARGS); Datum gin_triconsistent_jsonb_path_value(PG_FUNCTION_ARGS); Datum gin_debug_query_path_value(PG_FUNCTION_ARGS); +Datum gin_debug_query_laxpath_value(PG_FUNCTION_ARGS); static int add_entry(Entries *e, Datum key, Pointer extra, bool pmatch) @@ -932,14 +944,14 @@ gin_triconsistent_jsonb_value_path(PG_FUNCTION_ARGS) } static bool -get_query_path_hash(PathItem *pathItem, uint32 *hash) +get_query_path_hash(PathItem *pathItem, uint32 *hash, bool lax) { check_stack_depth(); if (!pathItem) return true; - if (!get_query_path_hash(pathItem->parent, hash)) + if (!get_query_path_hash(pathItem->parent, hash, lax)) { return false; } @@ -956,7 +968,7 @@ get_query_path_hash(PathItem *pathItem, uint32 *hash) *hash = (*hash << 1) | (*hash >> 31); *hash ^= hash_any((unsigned char *)pathItem->s, pathItem->len); } - else if (pathItem->type == iAnyArray || pathItem->type == iIndexArray) + else if (!lax && (pathItem->type == iAnyArray || pathItem->type == iIndexArray)) { *hash = (*hash << 1) | (*hash >> 31); *hash ^= JB_FARRAY; @@ -969,9 +981,10 @@ get_query_path_hash(PathItem *pathItem, uint32 *hash) static bool check_path_value_entry_handler(ExtractedNode *node, Pointer extra) { + PathValueExtra *pvextra = (PathValueExtra *) extra; uint32 hash; hash = 0; - if (!get_query_path_hash(node->path, &hash)) + if (!get_query_path_hash(node->path, &hash, pvextra->lax)) return false; return true; } @@ -979,7 +992,8 @@ check_path_value_entry_handler(ExtractedNode *node, Pointer extra) static int make_path_value_entry_handler(ExtractedNode *node, Pointer extra) { - Entries *e = (Entries *)extra; + PathValueExtra *pvextra = (PathValueExtra *) extra; + Entries *e = pvextra->entries; uint32 hash; GINKey *key; KeyExtra *keyExtra; @@ -989,7 +1003,7 @@ make_path_value_entry_handler(ExtractedNode *node, Pointer extra) Assert(!isLogicalNodeType(node->type)); hash = 0; - if (!get_query_path_hash(node->path, &hash)) + if (!get_query_path_hash(node->path, &hash, pvextra->lax)) return -1; keyExtra = (KeyExtra *)palloc(sizeof(KeyExtra)); @@ -1082,7 +1096,7 @@ gin_compare_partial_jsonb_path_value(PG_FUNCTION_ARGS) } static Datum * -gin_extract_jsonb_path_value_internal(Jsonb *jb, int32 *nentries) +gin_extract_jsonb_path_value_internal(Jsonb *jb, int32 *nentries, bool lax) { int total = 2 * JB_ROOT_COUNT(jb); JsonbIterator *it; @@ -1123,6 +1137,8 @@ gin_extract_jsonb_path_value_internal(Jsonb *jb, int32 *nentries) if (v.val.array.rawScalar) break; entries[i++] = PointerGetDatum(make_gin_key(&v, stack->hash)); + if (lax) + break; tmp = stack; stack = (PathHashStack *) palloc(sizeof(PathHashStack)); stack->parent = tmp; @@ -1150,7 +1166,7 @@ gin_extract_jsonb_path_value_internal(Jsonb *jb, int32 *nentries) entries[i++] = PointerGetDatum(make_gin_key(&v, stack->hash)); break; case WJB_END_ARRAY: - if (!stack->parent) + if (!stack->parent || lax) break; /* raw scalar array */ /* fall through */ case WJB_END_OBJECT: @@ -1175,25 +1191,49 @@ gin_extract_jsonb_path_value(PG_FUNCTION_ARGS) Jsonb *jb = PG_GETARG_JSONB_P(0); int32 *nentries = (int32 *) PG_GETARG_POINTER(1); - PG_RETURN_POINTER(gin_extract_jsonb_path_value_internal(jb, nentries)); + PG_RETURN_POINTER(gin_extract_jsonb_path_value_internal(jb, nentries, false)); } Datum -gin_debug_query_path_value(PG_FUNCTION_ARGS) +gin_extract_jsonb_laxpath_value(PG_FUNCTION_ARGS) +{ + Jsonb *jb = PG_GETARG_JSONB_P(0); + int32 *nentries = (int32 *) PG_GETARG_POINTER(1); + + PG_RETURN_POINTER(gin_extract_jsonb_path_value_internal(jb, nentries, true)); +} + +static Datum +gin_debug_query_path_value_internal(FunctionCallInfo fcinfo, bool lax) { JsQuery *jq; Entries e = {0}; + PathValueExtra extra; char *s; + extra.entries = &e; + extra.lax = lax; jq = PG_GETARG_JSQUERY(0); s = debugJsQuery(jq, make_path_value_entry_handler, - check_path_value_entry_handler, (Pointer)&e); + check_path_value_entry_handler, (Pointer) &extra); PG_RETURN_TEXT_P(cstring_to_text(s)); } Datum -gin_extract_jsonb_query_path_value(PG_FUNCTION_ARGS) +gin_debug_query_path_value(PG_FUNCTION_ARGS) +{ + PG_RETURN_DATUM(gin_debug_query_path_value_internal(fcinfo, false)); +} + +Datum +gin_debug_query_laxpath_value(PG_FUNCTION_ARGS) +{ + PG_RETURN_DATUM(gin_debug_query_path_value_internal(fcinfo, true)); +} + +static Datum +gin_extract_jsonb_query_path_value_internal(FunctionCallInfo fcinfo, bool lax) { Jsonb *jb; int32 *nentries = (int32 *) PG_GETARG_POINTER(1); @@ -1204,20 +1244,24 @@ gin_extract_jsonb_query_path_value(PG_FUNCTION_ARGS) Datum *entries = NULL; int i; Entries e = {0}; + PathValueExtra extra; JsQuery *jq; ExtractedNode *root; + extra.entries = &e; + extra.lax = lax; + switch(strategy) { case JsonbContainsStrategyNumber: jb = PG_GETARG_JSONB_P(0); - entries = gin_extract_jsonb_path_value_internal(jb, nentries); + entries = gin_extract_jsonb_path_value_internal(jb, nentries, lax); break; case JsQueryMatchStrategyNumber: jq = PG_GETARG_JSQUERY(0); root = extractJsQuery(jq, make_path_value_entry_handler, - check_path_value_entry_handler, (Pointer)&e); + check_path_value_entry_handler, (Pointer) &extra); if (root) { *nentries = e.count; @@ -1246,6 +1290,18 @@ gin_extract_jsonb_query_path_value(PG_FUNCTION_ARGS) PG_RETURN_POINTER(entries); } +Datum +gin_extract_jsonb_query_path_value(PG_FUNCTION_ARGS) +{ + return gin_extract_jsonb_query_path_value_internal(fcinfo, false); +} + +Datum +gin_extract_jsonb_query_laxpath_value(PG_FUNCTION_ARGS) +{ + return gin_extract_jsonb_query_path_value_internal(fcinfo, true); +} + Datum gin_consistent_jsonb_path_value(PG_FUNCTION_ARGS) { diff --git a/jsquery--1.1.sql b/jsquery--1.1.sql index d10076a..47cafd9 100644 --- a/jsquery--1.1.sql +++ b/jsquery--1.1.sql @@ -293,6 +293,28 @@ CREATE OPERATOR CLASS jsonb_path_value_ops FUNCTION 6 gin_triconsistent_jsonb_path_value(internal, smallint, anyarray, integer, internal, internal, internal), STORAGE bytea; +CREATE OR REPLACE FUNCTION gin_extract_jsonb_laxpath_value(internal, internal, internal) + RETURNS internal + AS 'MODULE_PATHNAME' + LANGUAGE C STRICT IMMUTABLE; + +CREATE OR REPLACE FUNCTION gin_extract_jsonb_query_laxpath_value(anyarray, internal, smallint, internal, internal, internal, internal) + RETURNS internal + AS 'MODULE_PATHNAME' + LANGUAGE C STRICT IMMUTABLE; + +CREATE OPERATOR CLASS jsonb_laxpath_value_ops + FOR TYPE jsonb USING gin AS + OPERATOR 7 @>, + OPERATOR 14 @@ (jsonb, jsquery), + FUNCTION 1 gin_compare_jsonb_path_value(bytea, bytea), + FUNCTION 2 gin_extract_jsonb_laxpath_value(internal, internal, internal), + FUNCTION 3 gin_extract_jsonb_query_laxpath_value(anyarray, internal, smallint, internal, internal, internal, internal), + FUNCTION 4 gin_consistent_jsonb_path_value(internal, smallint, anyarray, integer, internal, internal, internal, internal), + FUNCTION 5 gin_compare_partial_jsonb_path_value(bytea, bytea, smallint, internal), + FUNCTION 6 gin_triconsistent_jsonb_path_value(internal, smallint, anyarray, integer, internal, internal, internal), + STORAGE bytea; + CREATE OR REPLACE FUNCTION gin_debug_query_value_path(jsquery) RETURNS text AS 'MODULE_PATHNAME' @@ -302,3 +324,8 @@ CREATE OR REPLACE FUNCTION gin_debug_query_path_value(jsquery) RETURNS text AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; + +CREATE OR REPLACE FUNCTION gin_debug_query_laxpath_value(jsquery) + RETURNS text + AS 'MODULE_PATHNAME' + LANGUAGE C STRICT IMMUTABLE; diff --git a/sql/jsquery.sql b/sql/jsquery.sql index 26487eb..1efb033 100644 --- a/sql/jsquery.sql +++ b/sql/jsquery.sql @@ -421,6 +421,32 @@ SELECT gin_debug_query_path_value('$ = true'); SELECT gin_debug_query_path_value('$ . ? (review_votes > 10) . review_rating < 7'); SELECT gin_debug_query_path_value('similar_product_ids . ? (# = "B0002W4TL2") . $'); +SELECT gin_debug_query_laxpath_value('NOT NOT NOT x(y(NOT (a=1) and NOT (b=2)) OR NOT NOT (c=3)) and z = 5'); +SELECT gin_debug_query_laxpath_value('NOT #(x=1) and NOT *(y=1) and NOT %(z=1) '); +SELECT gin_debug_query_laxpath_value('#(NOT x=1) and *(NOT y=1) and %(NOT z=1) '); +SELECT gin_debug_query_laxpath_value('NOT #(NOT x=1) and NOT *(NOT y=1) and NOT %(NOT z=1) '); +SELECT gin_debug_query_laxpath_value('#(x = "a" and y > 0 and y < 1 and z > 0)'); +SELECT gin_debug_query_laxpath_value('#(x = "a" and y /*-- index */ >= 0 and y < 1 and z > 0)'); +SELECT gin_debug_query_laxpath_value('#(x /*-- noindex */ = "a" and y > 0 and y <= 1 and z /*-- index */ > 0)'); +SELECT gin_debug_query_laxpath_value('x = 1 and (y /*-- index */ > 0 and y < 1 OR z > 0)'); +SELECT gin_debug_query_laxpath_value('%.x = 1'); +SELECT gin_debug_query_laxpath_value('*.x = "b"'); +SELECT gin_debug_query_laxpath_value('x && [1,2,3]'); +SELECT gin_debug_query_laxpath_value('x @> [1,2,3]'); +SELECT gin_debug_query_laxpath_value('x <@ [1,2,3]'); +SELECT gin_debug_query_laxpath_value('x = *'); +SELECT gin_debug_query_laxpath_value('x is boolean'); +SELECT gin_debug_query_laxpath_value('x is string'); +SELECT gin_debug_query_laxpath_value('x is numeric'); +SELECT gin_debug_query_laxpath_value('x is array'); +SELECT gin_debug_query_laxpath_value('x is object'); +SELECT gin_debug_query_laxpath_value('#:(x=1) AND %:(y=1) AND *:(z=1)'); +SELECT gin_debug_query_laxpath_value('#:(NOT x=1) AND %:(NOT y=1) AND *:(NOT z=1)'); +SELECT gin_debug_query_laxpath_value('NOT #:(NOT x=1) AND NOT %:(NOT y=1) AND NOT *:(NOT z=1)'); +SELECT gin_debug_query_laxpath_value('$ = true'); +SELECT gin_debug_query_laxpath_value('$ . ? (review_votes > 10) . review_rating < 7'); +SELECT gin_debug_query_laxpath_value('similar_product_ids . ? (# = "B0002W4TL2") . $'); + SELECT gin_debug_query_value_path('NOT NOT NOT x(y(NOT (a=1) and NOT (b=2)) OR NOT NOT (c=3)) and z = 5'); SELECT gin_debug_query_value_path('NOT #(x=1) and NOT *(y=1) and NOT %(z=1) '); SELECT gin_debug_query_value_path('#(NOT x=1) and *(NOT y=1) and %(NOT z=1) '); @@ -623,4 +649,62 @@ select v from test_jsquery where v @@ 'array && [2,3]'::jsquery order by v; select v from test_jsquery where v @@ 'array @> [2,3]'::jsquery order by v; select v from test_jsquery where v @@ 'array = [2,3]'::jsquery order by v; +drop index t_idx; + +create index t_idx on test_jsquery using gin (v jsonb_laxpath_value_ops); +set enable_seqscan = off; + +explain (costs off) select count(*) from test_jsquery where v @@ 'review_helpful_votes > 0'::jsquery; + +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 0'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 19'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes < 19'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes >= 19'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes <= 19'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes = 19'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16'::jsquery AND + v @@ 'review_helpful_votes < 20'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16 and review_helpful_votes < 20'::jsquery; +select count(*) from test_jsquery where v @@ 'review_helpful_votes ($ > 16 and $ < 20)'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"]'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids(# = "0440180295") '::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids.#($ = "0440180295") '::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"] and product_sales_rank > 300000'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids <@ ["B00000DG0U", "B00004SQXU", "B0001XAM18", "B00000FDBU", "B00000FDBV", "B000002H2H", "B000002H6C", "B000002H5E", "B000002H97", "B000002HMH"]'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids @> ["B000002H2H", "B000002H6C"]'::jsquery; +select count(*) from test_jsquery where v @@ 'customer_id = null'::jsquery; +select count(*) from test_jsquery where v @@ 'review_votes = true'::jsquery; +select count(*) from test_jsquery where v @@ 'product_group = false'::jsquery; +select count(*) from test_jsquery where v @@ 't = *'::jsquery; +select count(*) from test_jsquery where v @@ 't is boolean'::jsquery; +select count(*) from test_jsquery where v @@ 't is string'::jsquery; +select count(*) from test_jsquery where v @@ 't is numeric'::jsquery; +select count(*) from test_jsquery where v @@ 't is array'::jsquery; +select count(*) from test_jsquery where v @@ 't is object'::jsquery; +select count(*) from test_jsquery where v @@ '$ is boolean'::jsquery; +select count(*) from test_jsquery where v @@ '$ is string'::jsquery; +select count(*) from test_jsquery where v @@ '$ is numeric'::jsquery; +select count(*) from test_jsquery where v @@ '$ is array'::jsquery; +select count(*) from test_jsquery where v @@ '$ is object'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is numeric'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is string'::jsquery; +select count(*) from test_jsquery where v @@ 'NOT similar_product_ids.#: (NOT $ = "0440180295")'::jsquery; +select count(*) from test_jsquery where v @@ '$ > 2'::jsquery; +select count(*) from test_jsquery where v @@ '$ = false'::jsquery; +select count(*) from test_jsquery where v @@ 't'::jsquery; +select count(*) from test_jsquery where v @@ '$'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids.#'::jsquery; +select count(*) from test_jsquery where v @@ '$ . ? (review_votes > 10) . review_rating < 7'::jsquery; +select count(*) from test_jsquery where v @@ 'similar_product_ids . ? (# = "B0002W4TL2") . $'::jsquery; + +explain (costs off) select v from test_jsquery where v @@ 'array <@ [2,3]'::jsquery order by v; +explain (costs off) select v from test_jsquery where v @@ 'array && [2,3]'::jsquery order by v; +explain (costs off) select v from test_jsquery where v @@ 'array @> [2,3]'::jsquery order by v; +explain (costs off) select v from test_jsquery where v @@ 'array = [2,3]'::jsquery order by v; + +select v from test_jsquery where v @@ 'array <@ [2,3]'::jsquery order by v; +select v from test_jsquery where v @@ 'array && [2,3]'::jsquery order by v; +select v from test_jsquery where v @@ 'array @> [2,3]'::jsquery order by v; +select v from test_jsquery where v @@ 'array = [2,3]'::jsquery order by v; + RESET enable_seqscan; From 40e6d81a2514f7b228114f1bc0be2af31e19f81a Mon Sep 17 00:00:00 2001 From: Nikita Glukhov Date: Mon, 15 May 2017 23:35:22 +0300 Subject: [PATCH 09/21] Add jsonpath @? support --- Makefile | 2 + include/utils/jsonpath.h | 1 + jsonb_gin_ops.c | 48 ++++- jsquery--1.1.sql | 24 +++ jsquery.h | 5 + jsquery_extract.c | 440 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 510 insertions(+), 10 deletions(-) create mode 100644 include/utils/jsonpath.h diff --git a/Makefile b/Makefile index b11a9b1..540bee1 100644 --- a/Makefile +++ b/Makefile @@ -30,6 +30,8 @@ ifdef USE_ASSERT_CHECKING override CFLAGS += -DUSE_ASSERT_CHECKING endif +override CPPFLAGS += $(CPPFLAGS) -I$(srcdir)/include + jsquery_gram.o: jsquery_scan.c jsquery_gram.c: BISONFLAGS += -d diff --git a/include/utils/jsonpath.h b/include/utils/jsonpath.h new file mode 100644 index 0000000..98bd3d1 --- /dev/null +++ b/include/utils/jsonpath.h @@ -0,0 +1 @@ +#define NO_JSONPATH 1 diff --git a/jsonb_gin_ops.c b/jsonb_gin_ops.c index 237c32b..de402ab 100644 --- a/jsonb_gin_ops.c +++ b/jsonb_gin_ops.c @@ -21,6 +21,7 @@ #include "miscadmin.h" #include "utils/builtins.h" #include "utils/jsonb.h" +#include "utils/jsonpath.h" #include "jsquery.h" @@ -59,6 +60,7 @@ typedef struct #define BLOOM_BITS 2 #define JsonbNestedContainsStrategyNumber 13 #define JsQueryMatchStrategyNumber 14 +#define JsonpathExistsStrategyNumber 15 typedef struct { @@ -584,7 +586,8 @@ gin_compare_partial_jsonb_value_path(PG_FUNCTION_ARGS) StrategyNumber strategy = PG_GETARG_UINT16(2); int32 result; - if (strategy == JsQueryMatchStrategyNumber) + if (strategy == JsQueryMatchStrategyNumber || + strategy == JsonpathExistsStrategyNumber) { KeyExtra *extra = (KeyExtra *)PG_GETARG_POINTER(3); ExtractedNode *node = extra->node; @@ -780,7 +783,6 @@ gin_extract_jsonb_query_value_path(PG_FUNCTION_ARGS) int i, n; uint32 *bloom; Entries e = {0}; - JsQuery *jq; ExtractedNode *root; switch(strategy) @@ -805,9 +807,20 @@ gin_extract_jsonb_query_value_path(PG_FUNCTION_ARGS) break; case JsQueryMatchStrategyNumber: - jq = PG_GETARG_JSQUERY(0); - root = extractJsQuery(jq, make_value_path_entry_handler, - check_value_path_entry_handler, (Pointer)&e); +#ifndef NO_JSONPATH + case JsonpathExistsStrategyNumber: + if (strategy == JsonpathExistsStrategyNumber) + root = extractJsonPath(PG_GETARG_JSONPATH_P(0), + make_value_path_entry_handler, + check_value_path_entry_handler, + (Pointer)&e); + else +#endif + root = extractJsQuery(PG_GETARG_JSQUERY(0), + make_value_path_entry_handler, + check_value_path_entry_handler, + (Pointer)&e); + if (root) { *nentries = e.count; @@ -864,6 +877,7 @@ gin_consistent_jsonb_value_path(PG_FUNCTION_ARGS) break; case JsQueryMatchStrategyNumber: + case JsonpathExistsStrategyNumber: if (nkeys == 0) res = true; else @@ -925,6 +939,7 @@ gin_triconsistent_jsonb_value_path(PG_FUNCTION_ARGS) break; case JsQueryMatchStrategyNumber: + case JsonpathExistsStrategyNumber: if (nkeys == 0) res = GIN_MAYBE; else @@ -1047,7 +1062,8 @@ gin_compare_partial_jsonb_path_value(PG_FUNCTION_ARGS) { result = (key->hash > partial_key->hash) ? 1 : -1; } - else if (strategy == JsQueryMatchStrategyNumber) + else if (strategy == JsQueryMatchStrategyNumber || + strategy == JsonpathExistsStrategyNumber) { KeyExtra *extra = (KeyExtra *)PG_GETARG_POINTER(3); ExtractedNode *node = extra->node; @@ -1245,7 +1261,6 @@ gin_extract_jsonb_query_path_value_internal(FunctionCallInfo fcinfo, bool lax) int i; Entries e = {0}; PathValueExtra extra; - JsQuery *jq; ExtractedNode *root; extra.entries = &e; @@ -1259,9 +1274,20 @@ gin_extract_jsonb_query_path_value_internal(FunctionCallInfo fcinfo, bool lax) break; case JsQueryMatchStrategyNumber: - jq = PG_GETARG_JSQUERY(0); - root = extractJsQuery(jq, make_path_value_entry_handler, - check_path_value_entry_handler, (Pointer) &extra); +#ifndef NO_JSONPATH + case JsonpathExistsStrategyNumber: + if (strategy == JsonpathExistsStrategyNumber) + root = extractJsonPath(PG_GETARG_JSONPATH_P(0), + make_path_value_entry_handler, + check_path_value_entry_handler, + (Pointer) &extra); + else +#endif + root = extractJsQuery(PG_GETARG_JSQUERY(0), + make_path_value_entry_handler, + check_path_value_entry_handler, + (Pointer) &extra); + if (root) { *nentries = e.count; @@ -1329,6 +1355,7 @@ gin_consistent_jsonb_path_value(PG_FUNCTION_ARGS) break; case JsQueryMatchStrategyNumber: + case JsonpathExistsStrategyNumber: if (nkeys == 0) res = true; else @@ -1390,6 +1417,7 @@ gin_triconsistent_jsonb_path_value(PG_FUNCTION_ARGS) break; case JsQueryMatchStrategyNumber: + case JsonpathExistsStrategyNumber: if (nkeys == 0) res = GIN_MAYBE; else diff --git a/jsquery--1.1.sql b/jsquery--1.1.sql index 47cafd9..849f162 100644 --- a/jsquery--1.1.sql +++ b/jsquery--1.1.sql @@ -329,3 +329,27 @@ CREATE OR REPLACE FUNCTION gin_debug_query_laxpath_value(jsquery) RETURNS text AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; + +-- add support for operator @? (jsonb, jsonpath) if type jsonpath exists in catalog +DO LANGUAGE plpgsql +$$ +BEGIN + PERFORM + FROM + pg_type t, + pg_namespace ns + WHERE + t.typname = 'jsonpath' AND + t.typnamespace = ns.oid AND + ns.nspname = 'pg_catalog'; + + IF FOUND THEN + ALTER OPERATOR FAMILY jsonb_path_value_ops USING gin + ADD OPERATOR 15 @? (jsonb, jsonpath); + ALTER OPERATOR FAMILY jsonb_laxpath_value_ops USING gin + ADD OPERATOR 15 @? (jsonb, jsonpath); + ALTER OPERATOR FAMILY jsonb_value_path_ops USING gin + ADD OPERATOR 15 @? (jsonb, jsonpath); + END IF; +END +$$; diff --git a/jsquery.h b/jsquery.h index 9563944..354ddfa 100644 --- a/jsquery.h +++ b/jsquery.h @@ -19,6 +19,7 @@ #include "fmgr.h" #include "utils/numeric.h" #include "utils/jsonb.h" +#include "utils/jsonpath.h" typedef struct { @@ -243,6 +244,10 @@ bool isLogicalNodeType(ExtractedNodeType type); ExtractedNode *extractJsQuery(JsQuery *jq, MakeEntryHandler makeHandler, CheckEntryHandler checkHandler, Pointer extra); +#ifndef NO_JSONPATH +ExtractedNode *extractJsonPath(JsonPath *jp, MakeEntryHandler makeHandler, + CheckEntryHandler checkHandler, Pointer extra); +#endif char *debugJsQuery(JsQuery *jq, MakeEntryHandler makeHandler, CheckEntryHandler checkHandler, Pointer extra); bool queryNeedRecheck(ExtractedNode *node); diff --git a/jsquery_extract.c b/jsquery_extract.c index 75758b8..b68cacc 100644 --- a/jsquery_extract.c +++ b/jsquery_extract.c @@ -17,6 +17,7 @@ #include "access/gin.h" #include "utils/builtins.h" #include "utils/jsonb.h" +#include "utils/jsonpath.h" #include "miscadmin.h" #include "jsquery.h" @@ -289,6 +290,420 @@ recursiveExtract(JsQueryItem *jsq, bool not, bool indirect, PathItem *path) return NULL; } +#ifndef NO_JSONPATH +static PathItem * +makePathItem(PathItemType type, PathItem *parent) +{ + PathItem *item = (PathItem *) palloc(sizeof(PathItem)); + + item->type = type; + item->parent = parent; + + return item; +} + +typedef struct ExtractedJsonPath +{ + PathItem *path; + bool indirect; + bool type; +} ExtractedJsonPath; + +static bool +recursiveExtractJsonPath(JsonPathItem *jpi, + bool lax, bool not, bool indirect, bool unwrap, + PathItem *parent, List **paths); + +static bool +recursiveExtractJsonPath2(JsonPathItem *jpi, + bool lax, bool not, bool indirect, bool unwrap, + PathItem *parent, List **paths) +{ + JsonPathItem next; + PathItem *path; + + check_stack_depth(); + + switch (jpi->type) + { + case jpiRoot: + Assert(!parent); + path = NULL; + break; + + case jpiCurrent: + path = parent; + break; + + case jpiKey: + path = makePathItem(iKey, parent); + path->s = jspGetString(jpi, &path->len); + break; + + case jpiAny: + /* case jpiAll: */ + if ((not && jpi->type == jpiAny) /*|| (!not && jpi->type == jpiAll)*/) + return false; + + path = makePathItem(iAny, parent); + indirect = true; + break; + + case jpiIndexArray: + path = makePathItem(iIndexArray, parent); + path->arrayIndex = -1; /* FIXME */ + indirect = true; + break; + + case jpiAnyArray: + /* case jpiAllArray: */ + if ((not && jpi->type == jpiAnyArray) /*|| (!not && jpi->type == jpiAllArray)*/) + return false; + + path = makePathItem(iAnyArray, parent); + indirect = true; + break; + + case jpiAnyKey: + /* case jpiAllKey: */ + if ((not && jpi->type == jpiAnyKey) /*|| (!not && jpi->type == jpiAllKey)*/) + return false; + + path = makePathItem(iAnyKey, parent); + indirect = true; + break; + + default: + /* elog(ERROR,"Wrong state: %d", jpi->type); */ + return false; + } + + if (!jspGetNext(jpi, &next) || + (next.type == jpiType && !jspHasNext(&next))) + { + ExtractedJsonPath *ejp = palloc(sizeof(*ejp)); + + ejp->path = path; + ejp->indirect = indirect; + ejp->type = jspHasNext(jpi) && next.type == jpiType; + + *paths = lappend(*paths, ejp); + + if (lax && unwrap && //!(path && path->type == iAnyArray) && + !(jspHasNext(jpi) && next.type == jpiType)) + { + ejp = palloc(sizeof(*ejp)); + ejp->path = makePathItem(iAnyArray, path); + ejp->indirect = indirect; + ejp->type = false; + + *paths = lappend(*paths, ejp); + } + + return true; + } + + return recursiveExtractJsonPath(&next, lax, not, indirect, unwrap, path, + paths); +} + +static bool +recursiveExtractJsonPath(JsonPathItem *jpi, + bool lax, bool not, bool indirect, bool unwrap, + PathItem *parent, List **paths) +{ + switch (jpi->type) + { + case jpiRoot: + case jpiAny: + break; + + case jpiAnyArray: + case jpiIndexArray: + if (lax) + { + /* try to skip [*] path item */ + JsonPathItem next; + + if (!jspGetNext(jpi, &next)) + { + ExtractedJsonPath *ejp = palloc(sizeof(*ejp)); + + ejp->path = parent; + ejp->indirect = indirect; + ejp->type = false; + + *paths = lappend(*paths, ejp); + } + else if (!recursiveExtractJsonPath2(&next, lax, not, indirect, unwrap, + parent, paths)) + return false; + } + + break; + + case jpiKey: + case jpiAnyKey: + /* add implicit [*] path item in lax mode */ + if (lax && + !recursiveExtractJsonPath2(jpi, lax, not, true, unwrap, + makePathItem(iAnyArray, parent), + paths)) + return false; + break; + + default: + break; + } + + return recursiveExtractJsonPath2(jpi, lax, not, indirect, unwrap, parent, + paths); +} + +static inline JsQueryItem * +jspConstToJsQueryItem(JsonPathItem *jpi) +{ + JsQueryItem *jqi = palloc(sizeof(*jqi)); + + switch (jpi->type) + { + case jpiNull: + jqi->type = jqiNull; + break; + + case jpiBool: + jqi->type = jqiBool; + jqi->value.data = jpi->content.value.data; + break; + + case jpiNumeric: + jqi->type = jqiNumeric; + jqi->value.data = jpi->content.value.data; + break; + + case jpiString: + jqi->type = jqiString; + jqi->value.data = jpi->content.value.data; + jqi->value.datalen = jpi->content.value.datalen; + break; + + default: + elog(ERROR, "invalid JsonPathItem literal type: %d", jpi->type); + return NULL; + } + + return jqi; +} + +static ExtractedNode * +recursiveExtractJsonPathExpr(JsonPathItem *jpi, bool lax, bool not) +{ + ExtractedNode *result; + JsonPathItem elem; + + check_stack_depth(); + + switch (jpi->type) + { + case jpiAnd: + case jpiOr: + { + ExtractedNode *leftNode; + ExtractedNode *rightNode; + ExtractedNodeType type; + bool indirect = false; + + type = ((jpi->type == jpiAnd) == not) ? eOr : eAnd; + + jspGetLeftArg(jpi, &elem); + leftNode = recursiveExtractJsonPathExpr(&elem, lax, not); + + jspGetRightArg(jpi, &elem); + rightNode = recursiveExtractJsonPathExpr(&elem, lax, not); + + if (!leftNode || !rightNode) + { + if (type == eOr || (!leftNode && !rightNode)) + return NULL; + + if (leftNode) + { + leftNode->indirect |= indirect; + return leftNode; + } + else + { + rightNode->indirect |= indirect; + return rightNode; + } + } + + result = palloc(sizeof(ExtractedNode)); + result->type = type; + result->path = NULL; + result->indirect = indirect; + result->args.items = palloc(2 * sizeof(ExtractedNode *)); + result->args.items[0] = leftNode; + result->args.items[1] = rightNode; + result->args.count = 2; + + return result; + } + + case jpiNot: + jspGetArg(jpi, &elem); + return recursiveExtractJsonPathExpr(&elem, lax, !not); + + case jpiEqual: + case jpiLess: + case jpiGreater: + case jpiLessOrEqual: + case jpiGreaterOrEqual: + { + bool equal = jpi->type == jpiEqual; + bool greater = jpi->type == jpiGreater || + jpi->type == jpiGreaterOrEqual; + bool inclusive = jpi->type == jpiLessOrEqual || + jpi->type == jpiGreaterOrEqual; + List *paths = NIL; + ListCell *lc; + JsQueryItem *bound; + JsonPathItem larg; + JsonPathItem rarg; + + if (not) + return NULL; + + jspGetLeftArg(jpi, &larg); + jspGetRightArg(jpi, &rarg); + + if (rarg.type == jpiNumeric || + (equal && + (rarg.type == jpiNull || + rarg.type == jpiBool || + rarg.type == jpiString))) + { + bound = jspConstToJsQueryItem(&rarg); + + if (!recursiveExtractJsonPath(&larg, lax, not, false/* FIXME */, true, NULL, + &paths)) + return NULL; + } + else if (larg.type == jpiNumeric || + (equal && + (larg.type == jpiNull || + larg.type == jpiBool || + larg.type == jpiString))) + { + bound = jspConstToJsQueryItem(&larg); + + if (!recursiveExtractJsonPath(&rarg, lax, not, false/* FIXME */, true, NULL, + &paths)) + return NULL; + + greater = !greater; + } + else + return NULL; + + result = NULL; + + foreach(lc, paths) + { + ExtractedJsonPath *ejp = lfirst(lc); + ExtractedNode *node = palloc(sizeof(ExtractedNode)); + + node->hint = jsqIndexDefault; + node->path = ejp->path; + node->indirect = ejp->indirect; + + if (equal) + { + if (ejp->type) + { + if (bound->type != jqiString) + return NULL; /* FIXME always false */ + + node->type = eIs; + + if (!strncmp("null", bound->value.data, + bound->value.datalen)) + node->isType = jbvNull; + else if (!strncmp("boolean", bound->value.data, + bound->value.datalen)) + node->isType = jbvBool; + else if (!strncmp("number", bound->value.data, + bound->value.datalen)) + node->isType = jbvNumeric; + else if (!strncmp("string", bound->value.data, + bound->value.datalen)) + node->isType = jbvString; + else if (!strncmp("object", bound->value.data, + bound->value.datalen)) + node->isType = jbvObject; + else if (!strncmp("array", bound->value.data, + bound->value.datalen)) + node->isType = jbvArray; + else + return NULL; /* FIXME always false */ + } + else + { + node->type = eExactValue; + node->exactValue = bound; + } + } + else + { + if (ejp->type) + return NULL; + + node->type = eInequality; + + if (greater) + { + node->bounds.leftInclusive = inclusive; + node->bounds.rightBound = NULL; + node->bounds.leftBound = bound; + } + else + { + node->bounds.rightInclusive = inclusive; + node->bounds.leftBound = NULL; + node->bounds.rightBound = bound; + } + } + + if (result) + { + ExtractedNode *orNode = palloc(sizeof(ExtractedNode)); + + orNode->type = eOr; + orNode->path = NULL; + orNode->indirect = false; + orNode->args.items = palloc(2 * sizeof(ExtractedNode *)); + orNode->args.items[0] = result; + orNode->args.items[1] = node; + orNode->args.count = 2; + + result = orNode; + } + else + result = node; + } + + return result; + } + + default: + /* elog(ERROR,"Wrong state: %d", jpi->type); */ + return NULL; + } + + return NULL; +} +#endif + /* * Make node for checking existence of path. */ @@ -834,6 +1249,31 @@ extractJsQuery(JsQuery *jq, MakeEntryHandler makeHandler, return root; } +#ifndef NO_JSONPATH +/* + * Turn jsonpath into tree of entries using user-provided handler. + */ +ExtractedNode * +extractJsonPath(JsonPath *jp, MakeEntryHandler makeHandler, + CheckEntryHandler checkHandler, Pointer extra) +{ + ExtractedNode *root; + JsonPathItem jsp; + bool lax = (jp->header & JSONPATH_LAX) != 0; + + jspInit(&jsp, jp); + root = recursiveExtractJsonPathExpr(&jsp, lax, false); + if (root) + { + flatternTree(root); + simplifyRecursive(root); + setSelectivityClass(root, checkHandler, extra); + root = makeEntries(root, makeHandler, extra); + } + return root; +} +#endif + /* * Evaluate previously extracted tree. */ From adece5640fbcb602f91228180947b6e4d6172a26 Mon Sep 17 00:00:00 2001 From: Nikita Glukhov Date: Thu, 18 May 2017 18:05:56 +0300 Subject: [PATCH 10/21] WIP: jsonpath exists, filters --- jsquery_extract.c | 158 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 125 insertions(+), 33 deletions(-) diff --git a/jsquery_extract.c b/jsquery_extract.c index b68cacc..e997fc4 100644 --- a/jsquery_extract.c +++ b/jsquery_extract.c @@ -305,6 +305,7 @@ makePathItem(PathItemType type, PathItem *parent) typedef struct ExtractedJsonPath { PathItem *path; + List *filters; bool indirect; bool type; } ExtractedJsonPath; @@ -312,12 +313,16 @@ typedef struct ExtractedJsonPath static bool recursiveExtractJsonPath(JsonPathItem *jpi, bool lax, bool not, bool indirect, bool unwrap, - PathItem *parent, List **paths); + PathItem *parent, List **paths, List *filters); + +static ExtractedNode * +recursiveExtractJsonPathExpr(JsonPathItem *jpi, bool lax, bool not, + PathItem *path); static bool recursiveExtractJsonPath2(JsonPathItem *jpi, bool lax, bool not, bool indirect, bool unwrap, - PathItem *parent, List **paths) + PathItem *parent, List **paths, List *filters) { JsonPathItem next; PathItem *path; @@ -373,6 +378,24 @@ recursiveExtractJsonPath2(JsonPathItem *jpi, indirect = true; break; + case jpiFilter: + { + JsonPathItem arg; + ExtractedNode *expr; + + if (not) + return false; + + jspGetArg(jpi, &arg); + expr = recursiveExtractJsonPathExpr(&arg, lax, not, parent); + + if (expr) + filters = lappend(list_copy(filters), expr); + + path = parent; + break; + } + default: /* elog(ERROR,"Wrong state: %d", jpi->type); */ return false; @@ -384,6 +407,7 @@ recursiveExtractJsonPath2(JsonPathItem *jpi, ExtractedJsonPath *ejp = palloc(sizeof(*ejp)); ejp->path = path; + ejp->filters = filters; ejp->indirect = indirect; ejp->type = jspHasNext(jpi) && next.type == jpiType; @@ -394,6 +418,7 @@ recursiveExtractJsonPath2(JsonPathItem *jpi, { ejp = palloc(sizeof(*ejp)); ejp->path = makePathItem(iAnyArray, path); + ejp->filters = filters; ejp->indirect = indirect; ejp->type = false; @@ -404,13 +429,13 @@ recursiveExtractJsonPath2(JsonPathItem *jpi, } return recursiveExtractJsonPath(&next, lax, not, indirect, unwrap, path, - paths); + paths, filters); } static bool recursiveExtractJsonPath(JsonPathItem *jpi, bool lax, bool not, bool indirect, bool unwrap, - PathItem *parent, List **paths) + PathItem *parent, List **paths, List *filters) { switch (jpi->type) { @@ -430,13 +455,14 @@ recursiveExtractJsonPath(JsonPathItem *jpi, ExtractedJsonPath *ejp = palloc(sizeof(*ejp)); ejp->path = parent; + ejp->filters = filters; ejp->indirect = indirect; ejp->type = false; *paths = lappend(*paths, ejp); } else if (!recursiveExtractJsonPath2(&next, lax, not, indirect, unwrap, - parent, paths)) + parent, paths, filters)) return false; } @@ -448,7 +474,7 @@ recursiveExtractJsonPath(JsonPathItem *jpi, if (lax && !recursiveExtractJsonPath2(jpi, lax, not, true, unwrap, makePathItem(iAnyArray, parent), - paths)) + paths, filters)) return false; break; @@ -457,7 +483,7 @@ recursiveExtractJsonPath(JsonPathItem *jpi, } return recursiveExtractJsonPath2(jpi, lax, not, indirect, unwrap, parent, - paths); + paths, filters); } static inline JsQueryItem * @@ -496,7 +522,77 @@ jspConstToJsQueryItem(JsonPathItem *jpi) } static ExtractedNode * -recursiveExtractJsonPathExpr(JsonPathItem *jpi, bool lax, bool not) +appendJsonPathExprNode(ExtractedNode *result, ExtractedNode *node, PathItem *path, + List *filters) +{ + ExtractedNode *orNode; + + if (filters) + { + ListCell *flc; + + foreach(flc, filters) + { + ExtractedNode *filter = lfirst(flc); + ExtractedNode *andNode = palloc(sizeof(ExtractedNode)); + + andNode->type = eAnd; + andNode->hint = jsqIndexDefault; + andNode->path = path; + andNode->indirect = false; + andNode->args.items = palloc(2 * sizeof(ExtractedNode *)); + andNode->args.items[0] = node; + andNode->args.items[1] = filter; + andNode->args.count = 2; + + node = andNode; + } + } + + if (!result) + return node; + + orNode = palloc(sizeof(ExtractedNode)); + + orNode->type = eOr; + orNode->hint = jsqIndexDefault; + orNode->path = path; + orNode->indirect = false; + orNode->args.items = palloc(2 * sizeof(ExtractedNode *)); + orNode->args.items[0] = result; + orNode->args.items[1] = node; + orNode->args.count = 2; + + return orNode; +} + +static ExtractedNode * +extractJsonPathExists(JsonPathItem *jpi, bool lax, PathItem *path) +{ + List *paths = NIL; + ListCell *lc; + ExtractedNode *result; + + if (!recursiveExtractJsonPath(jpi, lax, false, false /* FIXME */, false, + path, &paths, NIL)) + return NULL; + + result = NULL; + + foreach(lc, paths) + { + ExtractedJsonPath *ejp = lfirst(lc); + ExtractedNode *node = makeAnyNode(false, ejp->indirect, ejp->path); + + result = appendJsonPathExprNode(result, node, path, ejp->filters); + } + + return result; +} + +static ExtractedNode * +recursiveExtractJsonPathExpr(JsonPathItem *jpi, bool lax, bool not, + PathItem *path) { ExtractedNode *result; JsonPathItem elem; @@ -516,10 +612,10 @@ recursiveExtractJsonPathExpr(JsonPathItem *jpi, bool lax, bool not) type = ((jpi->type == jpiAnd) == not) ? eOr : eAnd; jspGetLeftArg(jpi, &elem); - leftNode = recursiveExtractJsonPathExpr(&elem, lax, not); + leftNode = recursiveExtractJsonPathExpr(&elem, lax, not, path); jspGetRightArg(jpi, &elem); - rightNode = recursiveExtractJsonPathExpr(&elem, lax, not); + rightNode = recursiveExtractJsonPathExpr(&elem, lax, not, path); if (!leftNode || !rightNode) { @@ -540,7 +636,7 @@ recursiveExtractJsonPathExpr(JsonPathItem *jpi, bool lax, bool not) result = palloc(sizeof(ExtractedNode)); result->type = type; - result->path = NULL; + result->path = path; result->indirect = indirect; result->args.items = palloc(2 * sizeof(ExtractedNode *)); result->args.items[0] = leftNode; @@ -552,7 +648,7 @@ recursiveExtractJsonPathExpr(JsonPathItem *jpi, bool lax, bool not) case jpiNot: jspGetArg(jpi, &elem); - return recursiveExtractJsonPathExpr(&elem, lax, !not); + return recursiveExtractJsonPathExpr(&elem, lax, !not, path); case jpiEqual: case jpiLess: @@ -585,8 +681,8 @@ recursiveExtractJsonPathExpr(JsonPathItem *jpi, bool lax, bool not) { bound = jspConstToJsQueryItem(&rarg); - if (!recursiveExtractJsonPath(&larg, lax, not, false/* FIXME */, true, NULL, - &paths)) + if (!recursiveExtractJsonPath(&larg, lax, not, false/* FIXME */, true, path, + &paths, NIL)) return NULL; } else if (larg.type == jpiNumeric || @@ -597,8 +693,8 @@ recursiveExtractJsonPathExpr(JsonPathItem *jpi, bool lax, bool not) { bound = jspConstToJsQueryItem(&larg); - if (!recursiveExtractJsonPath(&rarg, lax, not, false/* FIXME */, true, NULL, - &paths)) + if (!recursiveExtractJsonPath(&rarg, lax, not, false/* FIXME */, true, path, + &paths, NIL)) return NULL; greater = !greater; @@ -674,27 +770,23 @@ recursiveExtractJsonPathExpr(JsonPathItem *jpi, bool lax, bool not) } } - if (result) - { - ExtractedNode *orNode = palloc(sizeof(ExtractedNode)); - - orNode->type = eOr; - orNode->path = NULL; - orNode->indirect = false; - orNode->args.items = palloc(2 * sizeof(ExtractedNode *)); - orNode->args.items[0] = result; - orNode->args.items[1] = node; - orNode->args.count = 2; - - result = orNode; - } - else - result = node; + result = appendJsonPathExprNode(result, node, path, + ejp->filters); } return result; } + case jpiExists: + { + if (not) + return NULL; + + jspGetArg(jpi, &elem); + + return extractJsonPathExists(&elem, lax, path); + } + default: /* elog(ERROR,"Wrong state: %d", jpi->type); */ return NULL; @@ -1262,7 +1354,7 @@ extractJsonPath(JsonPath *jp, MakeEntryHandler makeHandler, bool lax = (jp->header & JSONPATH_LAX) != 0; jspInit(&jsp, jp); - root = recursiveExtractJsonPathExpr(&jsp, lax, false); + root = recursiveExtractJsonPathExpr(&jsp, lax, false, NULL); if (root) { flatternTree(root); From 60c0c719fccdf86f51d5110ace292e64f9382956 Mon Sep 17 00:00:00 2001 From: Nikita Glukhov Date: Wed, 18 Oct 2017 15:27:18 +0300 Subject: [PATCH 11/21] Add jsonpath @@ support --- jsonb_gin_ops.c | 19 +++++++++++++++---- jsquery--1.1.sql | 8 +++++++- jsquery.h | 3 ++- jsquery_extract.c | 8 +++++--- 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/jsonb_gin_ops.c b/jsonb_gin_ops.c index de402ab..e88d7ad 100644 --- a/jsonb_gin_ops.c +++ b/jsonb_gin_ops.c @@ -61,6 +61,7 @@ typedef struct #define JsonbNestedContainsStrategyNumber 13 #define JsQueryMatchStrategyNumber 14 #define JsonpathExistsStrategyNumber 15 +#define JsonpathMatchStrategyNumber 16 typedef struct { @@ -587,7 +588,8 @@ gin_compare_partial_jsonb_value_path(PG_FUNCTION_ARGS) int32 result; if (strategy == JsQueryMatchStrategyNumber || - strategy == JsonpathExistsStrategyNumber) + strategy == JsonpathExistsStrategyNumber || + strategy == JsonpathMatchStrategyNumber) { KeyExtra *extra = (KeyExtra *)PG_GETARG_POINTER(3); ExtractedNode *node = extra->node; @@ -809,8 +811,10 @@ gin_extract_jsonb_query_value_path(PG_FUNCTION_ARGS) case JsQueryMatchStrategyNumber: #ifndef NO_JSONPATH case JsonpathExistsStrategyNumber: - if (strategy == JsonpathExistsStrategyNumber) + case JsonpathMatchStrategyNumber: + if (strategy != JsQueryMatchStrategyNumber) root = extractJsonPath(PG_GETARG_JSONPATH_P(0), + strategy == JsonpathExistsStrategyNumber, make_value_path_entry_handler, check_value_path_entry_handler, (Pointer)&e); @@ -878,6 +882,7 @@ gin_consistent_jsonb_value_path(PG_FUNCTION_ARGS) case JsQueryMatchStrategyNumber: case JsonpathExistsStrategyNumber: + case JsonpathMatchStrategyNumber: if (nkeys == 0) res = true; else @@ -940,6 +945,7 @@ gin_triconsistent_jsonb_value_path(PG_FUNCTION_ARGS) case JsQueryMatchStrategyNumber: case JsonpathExistsStrategyNumber: + case JsonpathMatchStrategyNumber: if (nkeys == 0) res = GIN_MAYBE; else @@ -1063,7 +1069,8 @@ gin_compare_partial_jsonb_path_value(PG_FUNCTION_ARGS) result = (key->hash > partial_key->hash) ? 1 : -1; } else if (strategy == JsQueryMatchStrategyNumber || - strategy == JsonpathExistsStrategyNumber) + strategy == JsonpathExistsStrategyNumber || + strategy == JsonpathMatchStrategyNumber) { KeyExtra *extra = (KeyExtra *)PG_GETARG_POINTER(3); ExtractedNode *node = extra->node; @@ -1276,8 +1283,10 @@ gin_extract_jsonb_query_path_value_internal(FunctionCallInfo fcinfo, bool lax) case JsQueryMatchStrategyNumber: #ifndef NO_JSONPATH case JsonpathExistsStrategyNumber: - if (strategy == JsonpathExistsStrategyNumber) + case JsonpathMatchStrategyNumber: + if (strategy != JsQueryMatchStrategyNumber) root = extractJsonPath(PG_GETARG_JSONPATH_P(0), + strategy == JsonpathExistsStrategyNumber, make_path_value_entry_handler, check_path_value_entry_handler, (Pointer) &extra); @@ -1356,6 +1365,7 @@ gin_consistent_jsonb_path_value(PG_FUNCTION_ARGS) case JsQueryMatchStrategyNumber: case JsonpathExistsStrategyNumber: + case JsonpathMatchStrategyNumber: if (nkeys == 0) res = true; else @@ -1418,6 +1428,7 @@ gin_triconsistent_jsonb_path_value(PG_FUNCTION_ARGS) case JsQueryMatchStrategyNumber: case JsonpathExistsStrategyNumber: + case JsonpathMatchStrategyNumber: if (nkeys == 0) res = GIN_MAYBE; else diff --git a/jsquery--1.1.sql b/jsquery--1.1.sql index 849f162..338da38 100644 --- a/jsquery--1.1.sql +++ b/jsquery--1.1.sql @@ -330,7 +330,7 @@ CREATE OR REPLACE FUNCTION gin_debug_query_laxpath_value(jsquery) AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; --- add support for operator @? (jsonb, jsonpath) if type jsonpath exists in catalog +-- add support for operators @?, @@ (jsonb, jsonpath) if type jsonpath exists in catalog DO LANGUAGE plpgsql $$ BEGIN @@ -346,10 +346,16 @@ BEGIN IF FOUND THEN ALTER OPERATOR FAMILY jsonb_path_value_ops USING gin ADD OPERATOR 15 @? (jsonb, jsonpath); + ALTER OPERATOR FAMILY jsonb_path_value_ops USING gin + ADD OPERATOR 16 @@ (jsonb, jsonpath); ALTER OPERATOR FAMILY jsonb_laxpath_value_ops USING gin ADD OPERATOR 15 @? (jsonb, jsonpath); + ALTER OPERATOR FAMILY jsonb_laxpath_value_ops USING gin + ADD OPERATOR 16 @@ (jsonb, jsonpath); ALTER OPERATOR FAMILY jsonb_value_path_ops USING gin ADD OPERATOR 15 @? (jsonb, jsonpath); + ALTER OPERATOR FAMILY jsonb_value_path_ops USING gin + ADD OPERATOR 16 @@ (jsonb, jsonpath); END IF; END $$; diff --git a/jsquery.h b/jsquery.h index 354ddfa..118c906 100644 --- a/jsquery.h +++ b/jsquery.h @@ -245,7 +245,8 @@ bool isLogicalNodeType(ExtractedNodeType type); ExtractedNode *extractJsQuery(JsQuery *jq, MakeEntryHandler makeHandler, CheckEntryHandler checkHandler, Pointer extra); #ifndef NO_JSONPATH -ExtractedNode *extractJsonPath(JsonPath *jp, MakeEntryHandler makeHandler, +ExtractedNode *extractJsonPath(JsonPath *jp, bool exists, + MakeEntryHandler makeHandler, CheckEntryHandler checkHandler, Pointer extra); #endif char *debugJsQuery(JsQuery *jq, MakeEntryHandler makeHandler, diff --git a/jsquery_extract.c b/jsquery_extract.c index e997fc4..e6f3f1d 100644 --- a/jsquery_extract.c +++ b/jsquery_extract.c @@ -1346,15 +1346,17 @@ extractJsQuery(JsQuery *jq, MakeEntryHandler makeHandler, * Turn jsonpath into tree of entries using user-provided handler. */ ExtractedNode * -extractJsonPath(JsonPath *jp, MakeEntryHandler makeHandler, +extractJsonPath(JsonPath *jp, bool exists, MakeEntryHandler makeHandler, CheckEntryHandler checkHandler, Pointer extra) { ExtractedNode *root; JsonPathItem jsp; - bool lax = (jp->header & JSONPATH_LAX) != 0; + bool lax = (jp->header & JSONPATH_LAX) != 0; jspInit(&jsp, jp); - root = recursiveExtractJsonPathExpr(&jsp, lax, false, NULL); + root = exists + ? extractJsonPathExists(&jsp, lax, NULL) + : recursiveExtractJsonPathExpr(&jsp, lax, false, NULL); if (root) { flatternTree(root); From d063a07bd33b99d3af141acbf8197353dcd976a3 Mon Sep 17 00:00:00 2001 From: Nikita Glukhov Date: Tue, 19 Mar 2019 17:13:34 +0300 Subject: [PATCH 12/21] Don't generate all possible lax paths for jsonb_value_path_ops --- jsonb_gin_ops.c | 2 ++ jsquery.h | 2 +- jsquery_extract.c | 5 +++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/jsonb_gin_ops.c b/jsonb_gin_ops.c index e88d7ad..09bb621 100644 --- a/jsonb_gin_ops.c +++ b/jsonb_gin_ops.c @@ -815,6 +815,7 @@ gin_extract_jsonb_query_value_path(PG_FUNCTION_ARGS) if (strategy != JsQueryMatchStrategyNumber) root = extractJsonPath(PG_GETARG_JSONPATH_P(0), strategy == JsonpathExistsStrategyNumber, + false, make_value_path_entry_handler, check_value_path_entry_handler, (Pointer)&e); @@ -1287,6 +1288,7 @@ gin_extract_jsonb_query_path_value_internal(FunctionCallInfo fcinfo, bool lax) if (strategy != JsQueryMatchStrategyNumber) root = extractJsonPath(PG_GETARG_JSONPATH_P(0), strategy == JsonpathExistsStrategyNumber, + !lax, make_path_value_entry_handler, check_path_value_entry_handler, (Pointer) &extra); diff --git a/jsquery.h b/jsquery.h index 118c906..d585fd8 100644 --- a/jsquery.h +++ b/jsquery.h @@ -245,7 +245,7 @@ bool isLogicalNodeType(ExtractedNodeType type); ExtractedNode *extractJsQuery(JsQuery *jq, MakeEntryHandler makeHandler, CheckEntryHandler checkHandler, Pointer extra); #ifndef NO_JSONPATH -ExtractedNode *extractJsonPath(JsonPath *jp, bool exists, +ExtractedNode *extractJsonPath(JsonPath *jp, bool exists, bool arrayPathItems, MakeEntryHandler makeHandler, CheckEntryHandler checkHandler, Pointer extra); #endif diff --git a/jsquery_extract.c b/jsquery_extract.c index e6f3f1d..8a86ce6 100644 --- a/jsquery_extract.c +++ b/jsquery_extract.c @@ -1346,12 +1346,13 @@ extractJsQuery(JsQuery *jq, MakeEntryHandler makeHandler, * Turn jsonpath into tree of entries using user-provided handler. */ ExtractedNode * -extractJsonPath(JsonPath *jp, bool exists, MakeEntryHandler makeHandler, +extractJsonPath(JsonPath *jp, bool exists, bool arrayPathItems, + MakeEntryHandler makeHandler, CheckEntryHandler checkHandler, Pointer extra) { ExtractedNode *root; JsonPathItem jsp; - bool lax = (jp->header & JSONPATH_LAX) != 0; + bool lax = (jp->header & JSONPATH_LAX) != 0 && arrayPathItems; jspInit(&jsp, jp); root = exists From 85c7e1c822175ebe7de05ef78eb6c3bc63b7a43d Mon Sep 17 00:00:00 2001 From: Nikita Glukhov Date: Fri, 19 May 2017 13:55:53 +0300 Subject: [PATCH 13/21] Add user functions for jsonpath queries debug --- expected/jsquery.out | 1615 ++++++++++++++++++++++++++++++++++++++++++ jsonb_gin_ops.c | 72 +- jsquery--1.1.sql | 15 + jsquery.h | 3 +- jsquery_extract.c | 5 +- sql/jsquery.sql | 118 +++ 6 files changed, 1819 insertions(+), 9 deletions(-) diff --git a/expected/jsquery.out b/expected/jsquery.out index 8553091..710bde7 100644 --- a/expected/jsquery.out +++ b/expected/jsquery.out @@ -2617,6 +2617,1621 @@ SELECT gin_debug_query_value_path('similar_product_ids . ? (# = "B0002W4TL2") . (1 row) +-- -extract jsonpath entries for index scan +SELECT gin_debug_jsonpath_value_path('lax $'); + gin_debug_jsonpath_value_path +------------------------------- + NULL + + +(1 row) + +SELECT gin_debug_jsonpath_value_path('strict $'); + gin_debug_jsonpath_value_path +------------------------------- + NULL + + +(1 row) + +SELECT gin_debug_jsonpath_value_path('lax $.a'); + gin_debug_jsonpath_value_path +------------------------------- + NULL + + +(1 row) + +SELECT gin_debug_jsonpath_value_path('strict $.a'); + gin_debug_jsonpath_value_path +------------------------------- + NULL + + +(1 row) + +SELECT gin_debug_jsonpath_value_path('lax exists($)'); + gin_debug_jsonpath_value_path +------------------------------- + $ = * , entry 0 + + +(1 row) + +SELECT gin_debug_jsonpath_value_path('strict exists($)'); + gin_debug_jsonpath_value_path +------------------------------- + $ = * , entry 0 + + +(1 row) + +SELECT gin_debug_jsonpath_value_path('lax exists($.a)'); + gin_debug_jsonpath_value_path +------------------------------- + a = * , entry 0 + + +(1 row) + +SELECT gin_debug_jsonpath_value_path('strict exists($.a)'); + gin_debug_jsonpath_value_path +------------------------------- + a = * , entry 0 + + +(1 row) + +SELECT gin_debug_jsonpath_value_path('lax exists($.a.b)'); + gin_debug_jsonpath_value_path +------------------------------- + a.b = * , entry 0 + + +(1 row) + +SELECT gin_debug_jsonpath_value_path('strict exists($.a.b)'); + gin_debug_jsonpath_value_path +------------------------------- + a.b = * , entry 0 + + +(1 row) + +SELECT gin_debug_jsonpath_value_path('lax exists($ ? (@.b == 3).c.d)'); + gin_debug_jsonpath_value_path +------------------------------- + b = 3 , entry 0 + + +(1 row) + +SELECT gin_debug_jsonpath_value_path('strict exists($ ? (@.b == 3).c.d)'); + gin_debug_jsonpath_value_path +------------------------------- + b = 3 , entry 0 + + +(1 row) + +SELECT gin_debug_jsonpath_value_path('lax exists($.a ? (@.b == 3).c.d ? (@.e == "aa" || @ == 1))'); + gin_debug_jsonpath_value_path +------------------------------- + AND + + OR + + a.c.d.e = "aa" , entry 0 + + a.c.d = 1 , entry 1 + + a.b = 3 , entry 2 + + +(1 row) + +SELECT gin_debug_jsonpath_value_path('strict exists($.a ? (@.b == 3).c.d ? (@.e == "aa" || @ == 1))'); + gin_debug_jsonpath_value_path +------------------------------- + AND + + a.b = 3 , entry 0 + + OR + + a.c.d.e = "aa" , entry 1 + + a.c.d = 1 , entry 2 + + +(1 row) + +SELECT gin_debug_jsonpath_value_path('lax $.a ? (@.b == "foo").c == 1'); + gin_debug_jsonpath_value_path +------------------------------- + AND + + a.b = "foo" , entry 0 + + a.c = 1 , entry 1 + + +(1 row) + +SELECT gin_debug_jsonpath_value_path('strict $.a ? (@.b == "foo").c == 1'); + gin_debug_jsonpath_value_path +------------------------------- + AND + + a.b = "foo" , entry 0 + + a.c = 1 , entry 1 + + +(1 row) + +SELECT gin_debug_jsonpath_value_path('lax $.a ? (@.b == "foo") == 1'); + gin_debug_jsonpath_value_path +------------------------------- + AND + + a = 1 , entry 0 + + a.b = "foo" , entry 1 + + +(1 row) + +SELECT gin_debug_jsonpath_value_path('strict $.a ? (@.b == "foo") == 1'); + gin_debug_jsonpath_value_path +------------------------------- + AND + + a = 1 , entry 0 + + a.b = "foo" , entry 1 + + +(1 row) + +SELECT gin_debug_jsonpath_value_path('lax $.a ? (@.b == "foo")'); + gin_debug_jsonpath_value_path +------------------------------- + NULL + + +(1 row) + +SELECT gin_debug_jsonpath_value_path('strict $.a ? (@.b == "foo")'); + gin_debug_jsonpath_value_path +------------------------------- + NULL + + +(1 row) + +SELECT gin_debug_jsonpath_value_path('lax exists($.a ? (@.b == "foo"))'); + gin_debug_jsonpath_value_path +------------------------------- + a.b = "foo" , entry 0 + + +(1 row) + +SELECT gin_debug_jsonpath_value_path('strict exists($.a ? (@.b == "foo"))'); + gin_debug_jsonpath_value_path +------------------------------- + a.b = "foo" , entry 0 + + +(1 row) + +SELECT gin_debug_jsonpath_value_path('lax exists($.a ? (@.b == "foo").c)'); + gin_debug_jsonpath_value_path +------------------------------- + a.b = "foo" , entry 0 + + +(1 row) + +SELECT gin_debug_jsonpath_value_path('strict exists($.a ? (@.b == "foo").c)'); + gin_debug_jsonpath_value_path +------------------------------- + a.b = "foo" , entry 0 + + +(1 row) + +select gin_debug_jsonpath_value_path('lax exists($.a.b ? (@.c.d == 1))'); + gin_debug_jsonpath_value_path +------------------------------- + a.b.c.d = 1 , entry 0 + + +(1 row) + +select gin_debug_jsonpath_value_path('strict exists($.a.b ? (@.c.d == 1))'); + gin_debug_jsonpath_value_path +------------------------------- + a.b.c.d = 1 , entry 0 + + +(1 row) + +select gin_debug_jsonpath_value_path('$.a > 1'); + gin_debug_jsonpath_value_path +------------------------------- + a > 1 , entry 0 + + +(1 row) + +SELECT gin_debug_jsonpath_path_value('lax $'); + gin_debug_jsonpath_path_value +------------------------------- + NULL + + +(1 row) + +SELECT gin_debug_jsonpath_path_value('strict $'); + gin_debug_jsonpath_path_value +------------------------------- + NULL + + +(1 row) + +SELECT gin_debug_jsonpath_path_value('lax $.a'); + gin_debug_jsonpath_path_value +------------------------------- + NULL + + +(1 row) + +SELECT gin_debug_jsonpath_path_value('strict $.a'); + gin_debug_jsonpath_path_value +------------------------------- + NULL + + +(1 row) + +SELECT gin_debug_jsonpath_path_value('lax exists($)'); + gin_debug_jsonpath_path_value +------------------------------- + $ = * , entry 0 + + +(1 row) + +SELECT gin_debug_jsonpath_path_value('strict exists($)'); + gin_debug_jsonpath_path_value +------------------------------- + $ = * , entry 0 + + +(1 row) + +SELECT gin_debug_jsonpath_path_value('lax exists($.a)'); + gin_debug_jsonpath_path_value +------------------------------- + OR + + #.a = * , entry 0 + + a = * , entry 1 + + +(1 row) + +SELECT gin_debug_jsonpath_path_value('strict exists($.a)'); + gin_debug_jsonpath_path_value +------------------------------- + a = * , entry 0 + + +(1 row) + +SELECT gin_debug_jsonpath_path_value('lax exists($.a.b)'); + gin_debug_jsonpath_path_value +------------------------------- + OR + + #.a.#.b = * , entry 0 + + #.a.b = * , entry 1 + + a.#.b = * , entry 2 + + a.b = * , entry 3 + + +(1 row) + +SELECT gin_debug_jsonpath_path_value('strict exists($.a.b)'); + gin_debug_jsonpath_path_value +------------------------------- + a.b = * , entry 0 + + +(1 row) + +SELECT gin_debug_jsonpath_path_value('lax exists($ ? (@.b == 3).c.d)'); + gin_debug_jsonpath_path_value +------------------------------- + OR + + OR + + #.#.b = 3 , entry 12 + + #.#.b.# = 3 , entry 13 + + #.b = 3 , entry 14 + + #.b.# = 3 , entry 15 + + OR + + #.#.b = 3 , entry 12 + + #.#.b.# = 3 , entry 13 + + #.b = 3 , entry 14 + + #.b.# = 3 , entry 15 + + OR + + #.#.b = 3 , entry 12 + + #.#.b.# = 3 , entry 13 + + #.b = 3 , entry 14 + + #.b.# = 3 , entry 15 + + OR + + #.#.b = 3 , entry 12 + + #.#.b.# = 3 , entry 13 + + #.b = 3 , entry 14 + + #.b.# = 3 , entry 15 + + OR + + #.b = 3 , entry 28 + + #.b.# = 3 , entry 29 + + b = 3 , entry 30 + + b.# = 3 , entry 31 + + OR + + #.b = 3 , entry 28 + + #.b.# = 3 , entry 29 + + b = 3 , entry 30 + + b.# = 3 , entry 31 + + OR + + #.b = 3 , entry 28 + + #.b.# = 3 , entry 29 + + b = 3 , entry 30 + + b.# = 3 , entry 31 + + OR + + #.b = 3 , entry 28 + + #.b.# = 3 , entry 29 + + b = 3 , entry 30 + + b.# = 3 , entry 31 + + +(1 row) + +SELECT gin_debug_jsonpath_path_value('strict exists($ ? (@.b == 3).c.d)'); + gin_debug_jsonpath_path_value +------------------------------- + b = 3 , entry 0 + + +(1 row) + +SELECT gin_debug_jsonpath_path_value('lax exists($.a ? (@.b == 3).c.d ? (@.e == "aa" || @ == 1))'); + gin_debug_jsonpath_path_value +----------------------------------------------- + OR + + AND + + OR + + #.a.#.#.b = 3 , entry 70 + + #.a.#.#.b.# = 3 , entry 71 + + #.a.#.b = 3 , entry 72 + + #.a.#.b.# = 3 , entry 73 + + OR + + #.a.#.#.c.#.d.#.#.e = "aa" , entry 4 + + #.a.#.#.c.#.d.#.#.e.# = "aa" , entry 5 + + #.a.#.#.c.#.d.#.e = "aa" , entry 6 + + #.a.#.#.c.#.d.#.e.# = "aa" , entry 7 + + #.a.#.#.c.#.d.# = 1 , entry 8 + + #.a.#.#.c.#.d.#.# = 1 , entry 9 + + AND + + OR + + #.a.#.#.b = 3 , entry 70 + + #.a.#.#.b.# = 3 , entry 71 + + #.a.#.b = 3 , entry 72 + + #.a.#.b.# = 3 , entry 73 + + OR + + #.a.#.#.c.#.d.#.e = "aa" , entry 14 + + #.a.#.#.c.#.d.#.e.# = "aa" , entry 15 + + #.a.#.#.c.#.d.e = "aa" , entry 16 + + #.a.#.#.c.#.d.e.# = "aa" , entry 17 + + #.a.#.#.c.#.d = 1 , entry 18 + + #.a.#.#.c.#.d.# = 1 , entry 19 + + AND + + OR + + #.a.#.#.b = 3 , entry 70 + + #.a.#.#.b.# = 3 , entry 71 + + #.a.#.b = 3 , entry 72 + + #.a.#.b.# = 3 , entry 73 + + OR + + #.a.#.#.c.d.#.#.e = "aa" , entry 24 + + #.a.#.#.c.d.#.#.e.# = "aa" , entry 25 + + #.a.#.#.c.d.#.e = "aa" , entry 26 + + #.a.#.#.c.d.#.e.# = "aa" , entry 27 + + #.a.#.#.c.d.# = 1 , entry 28 + + #.a.#.#.c.d.#.# = 1 , entry 29 + + AND + + OR + + #.a.#.#.b = 3 , entry 70 + + #.a.#.#.b.# = 3 , entry 71 + + #.a.#.b = 3 , entry 72 + + #.a.#.b.# = 3 , entry 73 + + OR + + #.a.#.#.c.d.#.e = "aa" , entry 34 + + #.a.#.#.c.d.#.e.# = "aa" , entry 35 + + #.a.#.#.c.d.e = "aa" , entry 36 + + #.a.#.#.c.d.e.# = "aa" , entry 37 + + #.a.#.#.c.d = 1 , entry 38 + + #.a.#.#.c.d.# = 1 , entry 39 + + AND + + OR + + #.a.#.#.b = 3 , entry 70 + + #.a.#.#.b.# = 3 , entry 71 + + #.a.#.b = 3 , entry 72 + + #.a.#.b.# = 3 , entry 73 + + OR + + #.a.#.c.#.d.#.#.e = "aa" , entry 44 + + #.a.#.c.#.d.#.#.e.# = "aa" , entry 45 + + #.a.#.c.#.d.#.e = "aa" , entry 46 + + #.a.#.c.#.d.#.e.# = "aa" , entry 47 + + #.a.#.c.#.d.# = 1 , entry 48 + + #.a.#.c.#.d.#.# = 1 , entry 49 + + AND + + OR + + #.a.#.#.b = 3 , entry 70 + + #.a.#.#.b.# = 3 , entry 71 + + #.a.#.b = 3 , entry 72 + + #.a.#.b.# = 3 , entry 73 + + OR + + #.a.#.c.#.d.#.e = "aa" , entry 54 + + #.a.#.c.#.d.#.e.# = "aa" , entry 55 + + #.a.#.c.#.d.e = "aa" , entry 56 + + #.a.#.c.#.d.e.# = "aa" , entry 57 + + #.a.#.c.#.d = 1 , entry 58 + + #.a.#.c.#.d.# = 1 , entry 59 + + AND + + OR + + #.a.#.#.b = 3 , entry 70 + + #.a.#.#.b.# = 3 , entry 71 + + #.a.#.b = 3 , entry 72 + + #.a.#.b.# = 3 , entry 73 + + OR + + #.a.#.c.d.#.#.e = "aa" , entry 64 + + #.a.#.c.d.#.#.e.# = "aa" , entry 65 + + #.a.#.c.d.#.e = "aa" , entry 66 + + #.a.#.c.d.#.e.# = "aa" , entry 67 + + #.a.#.c.d.# = 1 , entry 68 + + #.a.#.c.d.#.# = 1 , entry 69 + + AND + + OR + + #.a.#.#.b = 3 , entry 70 + + #.a.#.#.b.# = 3 , entry 71 + + #.a.#.b = 3 , entry 72 + + #.a.#.b.# = 3 , entry 73 + + OR + + #.a.#.c.d.#.e = "aa" , entry 74 + + #.a.#.c.d.#.e.# = "aa" , entry 75 + + #.a.#.c.d.e = "aa" , entry 76 + + #.a.#.c.d.e.# = "aa" , entry 77 + + #.a.#.c.d = 1 , entry 78 + + #.a.#.c.d.# = 1 , entry 79 + + AND + + OR + + #.a.#.b = 3 , entry 150 + + #.a.#.b.# = 3 , entry 151 + + #.a.b = 3 , entry 152 + + #.a.b.# = 3 , entry 153 + + OR + + #.a.#.c.#.d.#.#.e = "aa" , entry 84 + + #.a.#.c.#.d.#.#.e.# = "aa" , entry 85 + + #.a.#.c.#.d.#.e = "aa" , entry 86 + + #.a.#.c.#.d.#.e.# = "aa" , entry 87 + + #.a.#.c.#.d.# = 1 , entry 88 + + #.a.#.c.#.d.#.# = 1 , entry 89 + + AND + + OR + + #.a.#.b = 3 , entry 150 + + #.a.#.b.# = 3 , entry 151 + + #.a.b = 3 , entry 152 + + #.a.b.# = 3 , entry 153 + + OR + + #.a.#.c.#.d.#.e = "aa" , entry 94 + + #.a.#.c.#.d.#.e.# = "aa" , entry 95 + + #.a.#.c.#.d.e = "aa" , entry 96 + + #.a.#.c.#.d.e.# = "aa" , entry 97 + + #.a.#.c.#.d = 1 , entry 98 + + #.a.#.c.#.d.# = 1 , entry 99 + + AND + + OR + + #.a.#.b = 3 , entry 150 + + #.a.#.b.# = 3 , entry 151 + + #.a.b = 3 , entry 152 + + #.a.b.# = 3 , entry 153 + + OR + + #.a.#.c.d.#.#.e = "aa" , entry 104 + + #.a.#.c.d.#.#.e.# = "aa" , entry 105 + + #.a.#.c.d.#.e = "aa" , entry 106 + + #.a.#.c.d.#.e.# = "aa" , entry 107 + + #.a.#.c.d.# = 1 , entry 108 + + #.a.#.c.d.#.# = 1 , entry 109 + + AND + + OR + + #.a.#.b = 3 , entry 150 + + #.a.#.b.# = 3 , entry 151 + + #.a.b = 3 , entry 152 + + #.a.b.# = 3 , entry 153 + + OR + + #.a.#.c.d.#.e = "aa" , entry 114 + + #.a.#.c.d.#.e.# = "aa" , entry 115 + + #.a.#.c.d.e = "aa" , entry 116 + + #.a.#.c.d.e.# = "aa" , entry 117 + + #.a.#.c.d = 1 , entry 118 + + #.a.#.c.d.# = 1 , entry 119 + + AND + + OR + + #.a.#.b = 3 , entry 150 + + #.a.#.b.# = 3 , entry 151 + + #.a.b = 3 , entry 152 + + #.a.b.# = 3 , entry 153 + + OR + + #.a.c.#.d.#.#.e = "aa" , entry 124 + + #.a.c.#.d.#.#.e.# = "aa" , entry 125 + + #.a.c.#.d.#.e = "aa" , entry 126 + + #.a.c.#.d.#.e.# = "aa" , entry 127 + + #.a.c.#.d.# = 1 , entry 128 + + #.a.c.#.d.#.# = 1 , entry 129 + + AND + + OR + + #.a.#.b = 3 , entry 150 + + #.a.#.b.# = 3 , entry 151 + + #.a.b = 3 , entry 152 + + #.a.b.# = 3 , entry 153 + + OR + + #.a.c.#.d.#.e = "aa" , entry 134 + + #.a.c.#.d.#.e.# = "aa" , entry 135 + + #.a.c.#.d.e = "aa" , entry 136 + + #.a.c.#.d.e.# = "aa" , entry 137 + + #.a.c.#.d = 1 , entry 138 + + #.a.c.#.d.# = 1 , entry 139 + + AND + + OR + + #.a.#.b = 3 , entry 150 + + #.a.#.b.# = 3 , entry 151 + + #.a.b = 3 , entry 152 + + #.a.b.# = 3 , entry 153 + + OR + + #.a.c.d.#.#.e = "aa" , entry 144 + + #.a.c.d.#.#.e.# = "aa" , entry 145 + + #.a.c.d.#.e = "aa" , entry 146 + + #.a.c.d.#.e.# = "aa" , entry 147 + + #.a.c.d.# = 1 , entry 148 + + #.a.c.d.#.# = 1 , entry 149 + + AND + + OR + + #.a.#.b = 3 , entry 150 + + #.a.#.b.# = 3 , entry 151 + + #.a.b = 3 , entry 152 + + #.a.b.# = 3 , entry 153 + + OR + + #.a.c.d.#.e = "aa" , entry 154 + + #.a.c.d.#.e.# = "aa" , entry 155 + + #.a.c.d.e = "aa" , entry 156 + + #.a.c.d.e.# = "aa" , entry 157 + + #.a.c.d = 1 , entry 158 + + #.a.c.d.# = 1 , entry 159 + + AND + + OR + + a.#.#.b = 3 , entry 230 + + a.#.#.b.# = 3 , entry 231 + + a.#.b = 3 , entry 232 + + a.#.b.# = 3 , entry 233 + + OR + + a.#.#.c.#.d.#.#.e = "aa" , entry 164 + + a.#.#.c.#.d.#.#.e.# = "aa" , entry 165 + + a.#.#.c.#.d.#.e = "aa" , entry 166 + + a.#.#.c.#.d.#.e.# = "aa" , entry 167 + + a.#.#.c.#.d.# = 1 , entry 168 + + a.#.#.c.#.d.#.# = 1 , entry 169 + + AND + + OR + + a.#.#.b = 3 , entry 230 + + a.#.#.b.# = 3 , entry 231 + + a.#.b = 3 , entry 232 + + a.#.b.# = 3 , entry 233 + + OR + + a.#.#.c.#.d.#.e = "aa" , entry 174 + + a.#.#.c.#.d.#.e.# = "aa" , entry 175 + + a.#.#.c.#.d.e = "aa" , entry 176 + + a.#.#.c.#.d.e.# = "aa" , entry 177 + + a.#.#.c.#.d = 1 , entry 178 + + a.#.#.c.#.d.# = 1 , entry 179 + + AND + + OR + + a.#.#.b = 3 , entry 230 + + a.#.#.b.# = 3 , entry 231 + + a.#.b = 3 , entry 232 + + a.#.b.# = 3 , entry 233 + + OR + + a.#.#.c.d.#.#.e = "aa" , entry 184 + + a.#.#.c.d.#.#.e.# = "aa" , entry 185 + + a.#.#.c.d.#.e = "aa" , entry 186 + + a.#.#.c.d.#.e.# = "aa" , entry 187 + + a.#.#.c.d.# = 1 , entry 188 + + a.#.#.c.d.#.# = 1 , entry 189 + + AND + + OR + + a.#.#.b = 3 , entry 230 + + a.#.#.b.# = 3 , entry 231 + + a.#.b = 3 , entry 232 + + a.#.b.# = 3 , entry 233 + + OR + + a.#.#.c.d.#.e = "aa" , entry 194 + + a.#.#.c.d.#.e.# = "aa" , entry 195 + + a.#.#.c.d.e = "aa" , entry 196 + + a.#.#.c.d.e.# = "aa" , entry 197 + + a.#.#.c.d = 1 , entry 198 + + a.#.#.c.d.# = 1 , entry 199 + + AND + + OR + + a.#.#.b = 3 , entry 230 + + a.#.#.b.# = 3 , entry 231 + + a.#.b = 3 , entry 232 + + a.#.b.# = 3 , entry 233 + + OR + + a.#.c.#.d.#.#.e = "aa" , entry 204 + + a.#.c.#.d.#.#.e.# = "aa" , entry 205 + + a.#.c.#.d.#.e = "aa" , entry 206 + + a.#.c.#.d.#.e.# = "aa" , entry 207 + + a.#.c.#.d.# = 1 , entry 208 + + a.#.c.#.d.#.# = 1 , entry 209 + + AND + + OR + + a.#.#.b = 3 , entry 230 + + a.#.#.b.# = 3 , entry 231 + + a.#.b = 3 , entry 232 + + a.#.b.# = 3 , entry 233 + + OR + + a.#.c.#.d.#.e = "aa" , entry 214 + + a.#.c.#.d.#.e.# = "aa" , entry 215 + + a.#.c.#.d.e = "aa" , entry 216 + + a.#.c.#.d.e.# = "aa" , entry 217 + + a.#.c.#.d = 1 , entry 218 + + a.#.c.#.d.# = 1 , entry 219 + + AND + + OR + + a.#.#.b = 3 , entry 230 + + a.#.#.b.# = 3 , entry 231 + + a.#.b = 3 , entry 232 + + a.#.b.# = 3 , entry 233 + + OR + + a.#.c.d.#.#.e = "aa" , entry 224 + + a.#.c.d.#.#.e.# = "aa" , entry 225 + + a.#.c.d.#.e = "aa" , entry 226 + + a.#.c.d.#.e.# = "aa" , entry 227 + + a.#.c.d.# = 1 , entry 228 + + a.#.c.d.#.# = 1 , entry 229 + + AND + + OR + + a.#.#.b = 3 , entry 230 + + a.#.#.b.# = 3 , entry 231 + + a.#.b = 3 , entry 232 + + a.#.b.# = 3 , entry 233 + + OR + + a.#.c.d.#.e = "aa" , entry 234 + + a.#.c.d.#.e.# = "aa" , entry 235 + + a.#.c.d.e = "aa" , entry 236 + + a.#.c.d.e.# = "aa" , entry 237 + + a.#.c.d = 1 , entry 238 + + a.#.c.d.# = 1 , entry 239 + + AND + + OR + + a.#.b = 3 , entry 310 + + a.#.b.# = 3 , entry 311 + + a.b = 3 , entry 312 + + a.b.# = 3 , entry 313 + + OR + + a.#.c.#.d.#.#.e = "aa" , entry 244 + + a.#.c.#.d.#.#.e.# = "aa" , entry 245 + + a.#.c.#.d.#.e = "aa" , entry 246 + + a.#.c.#.d.#.e.# = "aa" , entry 247 + + a.#.c.#.d.# = 1 , entry 248 + + a.#.c.#.d.#.# = 1 , entry 249 + + AND + + OR + + a.#.b = 3 , entry 310 + + a.#.b.# = 3 , entry 311 + + a.b = 3 , entry 312 + + a.b.# = 3 , entry 313 + + OR + + a.#.c.#.d.#.e = "aa" , entry 254 + + a.#.c.#.d.#.e.# = "aa" , entry 255 + + a.#.c.#.d.e = "aa" , entry 256 + + a.#.c.#.d.e.# = "aa" , entry 257 + + a.#.c.#.d = 1 , entry 258 + + a.#.c.#.d.# = 1 , entry 259 + + AND + + OR + + a.#.b = 3 , entry 310 + + a.#.b.# = 3 , entry 311 + + a.b = 3 , entry 312 + + a.b.# = 3 , entry 313 + + OR + + a.#.c.d.#.#.e = "aa" , entry 264 + + a.#.c.d.#.#.e.# = "aa" , entry 265 + + a.#.c.d.#.e = "aa" , entry 266 + + a.#.c.d.#.e.# = "aa" , entry 267 + + a.#.c.d.# = 1 , entry 268 + + a.#.c.d.#.# = 1 , entry 269 + + AND + + OR + + a.#.b = 3 , entry 310 + + a.#.b.# = 3 , entry 311 + + a.b = 3 , entry 312 + + a.b.# = 3 , entry 313 + + OR + + a.#.c.d.#.e = "aa" , entry 274 + + a.#.c.d.#.e.# = "aa" , entry 275 + + a.#.c.d.e = "aa" , entry 276 + + a.#.c.d.e.# = "aa" , entry 277 + + a.#.c.d = 1 , entry 278 + + a.#.c.d.# = 1 , entry 279 + + AND + + OR + + a.#.b = 3 , entry 310 + + a.#.b.# = 3 , entry 311 + + a.b = 3 , entry 312 + + a.b.# = 3 , entry 313 + + OR + + a.c.#.d.#.#.e = "aa" , entry 284 + + a.c.#.d.#.#.e.# = "aa" , entry 285 + + a.c.#.d.#.e = "aa" , entry 286 + + a.c.#.d.#.e.# = "aa" , entry 287 + + a.c.#.d.# = 1 , entry 288 + + a.c.#.d.#.# = 1 , entry 289 + + AND + + OR + + a.#.b = 3 , entry 310 + + a.#.b.# = 3 , entry 311 + + a.b = 3 , entry 312 + + a.b.# = 3 , entry 313 + + OR + + a.c.#.d.#.e = "aa" , entry 294 + + a.c.#.d.#.e.# = "aa" , entry 295 + + a.c.#.d.e = "aa" , entry 296 + + a.c.#.d.e.# = "aa" , entry 297 + + a.c.#.d = 1 , entry 298 + + a.c.#.d.# = 1 , entry 299 + + AND + + OR + + a.#.b = 3 , entry 310 + + a.#.b.# = 3 , entry 311 + + a.b = 3 , entry 312 + + a.b.# = 3 , entry 313 + + OR + + a.c.d.#.#.e = "aa" , entry 304 + + a.c.d.#.#.e.# = "aa" , entry 305 + + a.c.d.#.e = "aa" , entry 306 + + a.c.d.#.e.# = "aa" , entry 307 + + a.c.d.# = 1 , entry 308 + + a.c.d.#.# = 1 , entry 309 + + AND + + OR + + a.#.b = 3 , entry 310 + + a.#.b.# = 3 , entry 311 + + a.b = 3 , entry 312 + + a.b.# = 3 , entry 313 + + OR + + a.c.d.#.e = "aa" , entry 314 + + a.c.d.#.e.# = "aa" , entry 315 + + a.c.d.e = "aa" , entry 316 + + a.c.d.e.# = "aa" , entry 317 + + a.c.d = 1 , entry 318 + + a.c.d.# = 1 , entry 319 + + +(1 row) + +SELECT gin_debug_jsonpath_path_value('strict exists($.a ? (@.b == 3).c.d ? (@.e == "aa" || @ == 1))'); + gin_debug_jsonpath_path_value +------------------------------- + AND + + a.b = 3 , entry 0 + + OR + + a.c.d.e = "aa" , entry 1 + + a.c.d = 1 , entry 2 + + +(1 row) + +SELECT gin_debug_jsonpath_path_value('lax $.a ? (@.b == "foo").c == 1'); + gin_debug_jsonpath_path_value +--------------------------------------- + OR + + AND + + OR + + #.a.#.#.b = "foo" , entry 15 + + #.a.#.#.b.# = "foo" , entry 16 + + #.a.#.b = "foo" , entry 17 + + #.a.#.b.# = "foo" , entry 18 + + #.a.#.#.c = 1 , entry 4 + + AND + + OR + + #.a.#.#.b = "foo" , entry 15 + + #.a.#.#.b.# = "foo" , entry 16 + + #.a.#.b = "foo" , entry 17 + + #.a.#.b.# = "foo" , entry 18 + + #.a.#.#.c.# = 1 , entry 9 + + AND + + OR + + #.a.#.#.b = "foo" , entry 15 + + #.a.#.#.b.# = "foo" , entry 16 + + #.a.#.b = "foo" , entry 17 + + #.a.#.b.# = "foo" , entry 18 + + #.a.#.c = 1 , entry 14 + + AND + + OR + + #.a.#.#.b = "foo" , entry 15 + + #.a.#.#.b.# = "foo" , entry 16 + + #.a.#.b = "foo" , entry 17 + + #.a.#.b.# = "foo" , entry 18 + + #.a.#.c.# = 1 , entry 19 + + AND + + OR + + #.a.#.b = "foo" , entry 35 + + #.a.#.b.# = "foo" , entry 36 + + #.a.b = "foo" , entry 37 + + #.a.b.# = "foo" , entry 38 + + #.a.#.c = 1 , entry 24 + + AND + + OR + + #.a.#.b = "foo" , entry 35 + + #.a.#.b.# = "foo" , entry 36 + + #.a.b = "foo" , entry 37 + + #.a.b.# = "foo" , entry 38 + + #.a.#.c.# = 1 , entry 29 + + AND + + OR + + #.a.#.b = "foo" , entry 35 + + #.a.#.b.# = "foo" , entry 36 + + #.a.b = "foo" , entry 37 + + #.a.b.# = "foo" , entry 38 + + #.a.c = 1 , entry 34 + + AND + + OR + + #.a.#.b = "foo" , entry 35 + + #.a.#.b.# = "foo" , entry 36 + + #.a.b = "foo" , entry 37 + + #.a.b.# = "foo" , entry 38 + + #.a.c.# = 1 , entry 39 + + AND + + OR + + a.#.#.b = "foo" , entry 55 + + a.#.#.b.# = "foo" , entry 56 + + a.#.b = "foo" , entry 57 + + a.#.b.# = "foo" , entry 58 + + a.#.#.c = 1 , entry 44 + + AND + + OR + + a.#.#.b = "foo" , entry 55 + + a.#.#.b.# = "foo" , entry 56 + + a.#.b = "foo" , entry 57 + + a.#.b.# = "foo" , entry 58 + + a.#.#.c.# = 1 , entry 49 + + AND + + OR + + a.#.#.b = "foo" , entry 55 + + a.#.#.b.# = "foo" , entry 56 + + a.#.b = "foo" , entry 57 + + a.#.b.# = "foo" , entry 58 + + a.#.c = 1 , entry 54 + + AND + + OR + + a.#.#.b = "foo" , entry 55 + + a.#.#.b.# = "foo" , entry 56 + + a.#.b = "foo" , entry 57 + + a.#.b.# = "foo" , entry 58 + + a.#.c.# = 1 , entry 59 + + AND + + OR + + a.#.b = "foo" , entry 75 + + a.#.b.# = "foo" , entry 76 + + a.b = "foo" , entry 77 + + a.b.# = "foo" , entry 78 + + a.#.c = 1 , entry 64 + + AND + + OR + + a.#.b = "foo" , entry 75 + + a.#.b.# = "foo" , entry 76 + + a.b = "foo" , entry 77 + + a.b.# = "foo" , entry 78 + + a.#.c.# = 1 , entry 69 + + AND + + a.c = 1 , entry 70 + + OR + + a.#.b = "foo" , entry 75 + + a.#.b.# = "foo" , entry 76 + + a.b = "foo" , entry 77 + + a.b.# = "foo" , entry 78 + + AND + + OR + + a.#.b = "foo" , entry 75 + + a.#.b.# = "foo" , entry 76 + + a.b = "foo" , entry 77 + + a.b.# = "foo" , entry 78 + + a.c.# = 1 , entry 79 + + +(1 row) + +SELECT gin_debug_jsonpath_path_value('strict $.a ? (@.b == "foo").c == 1'); + gin_debug_jsonpath_path_value +------------------------------- + AND + + a.b = "foo" , entry 0 + + a.c = 1 , entry 1 + + +(1 row) + +SELECT gin_debug_jsonpath_path_value('lax $.a ? (@.b == "foo") == 1'); + gin_debug_jsonpath_path_value +-------------------------------------- + OR + + AND + + OR + + #.a.#.#.b = "foo" , entry 5 + + #.a.#.#.b.# = "foo" , entry 6 + + #.a.#.b = "foo" , entry 7 + + #.a.#.b.# = "foo" , entry 8 + + #.a.# = 1 , entry 4 + + AND + + OR + + #.a.#.#.b = "foo" , entry 5 + + #.a.#.#.b.# = "foo" , entry 6 + + #.a.#.b = "foo" , entry 7 + + #.a.#.b.# = "foo" , entry 8 + + #.a.#.# = 1 , entry 9 + + AND + + OR + + #.a.#.b = "foo" , entry 15 + + #.a.#.b.# = "foo" , entry 16 + + #.a.b = "foo" , entry 17 + + #.a.b.# = "foo" , entry 18 + + #.a = 1 , entry 14 + + AND + + OR + + #.a.#.b = "foo" , entry 15 + + #.a.#.b.# = "foo" , entry 16 + + #.a.b = "foo" , entry 17 + + #.a.b.# = "foo" , entry 18 + + #.a.# = 1 , entry 19 + + AND + + OR + + a.#.#.b = "foo" , entry 25 + + a.#.#.b.# = "foo" , entry 26 + + a.#.b = "foo" , entry 27 + + a.#.b.# = "foo" , entry 28 + + a.# = 1 , entry 24 + + AND + + OR + + a.#.#.b = "foo" , entry 25 + + a.#.#.b.# = "foo" , entry 26 + + a.#.b = "foo" , entry 27 + + a.#.b.# = "foo" , entry 28 + + a.#.# = 1 , entry 29 + + AND + + a = 1 , entry 30 + + OR + + a.#.b = "foo" , entry 35 + + a.#.b.# = "foo" , entry 36 + + a.b = "foo" , entry 37 + + a.b.# = "foo" , entry 38 + + AND + + OR + + a.#.b = "foo" , entry 35 + + a.#.b.# = "foo" , entry 36 + + a.b = "foo" , entry 37 + + a.b.# = "foo" , entry 38 + + a.# = 1 , entry 39 + + +(1 row) + +SELECT gin_debug_jsonpath_path_value('strict $.a ? (@.b == "foo") == 1'); + gin_debug_jsonpath_path_value +------------------------------- + AND + + a = 1 , entry 0 + + a.b = "foo" , entry 1 + + +(1 row) + +SELECT gin_debug_jsonpath_path_value('lax $.a ? (@.b == "foo")'); + gin_debug_jsonpath_path_value +------------------------------- + NULL + + +(1 row) + +SELECT gin_debug_jsonpath_path_value('strict $.a ? (@.b == "foo")'); + gin_debug_jsonpath_path_value +------------------------------- + NULL + + +(1 row) + +SELECT gin_debug_jsonpath_path_value('lax exists($.a ? (@.b == "foo"))'); + gin_debug_jsonpath_path_value +------------------------------------ + OR + + OR + + #.a.#.#.b = "foo" , entry 0 + + #.a.#.#.b.# = "foo" , entry 1 + + #.a.#.b = "foo" , entry 2 + + #.a.#.b.# = "foo" , entry 3 + + OR + + #.a.#.b = "foo" , entry 4 + + #.a.#.b.# = "foo" , entry 5 + + #.a.b = "foo" , entry 6 + + #.a.b.# = "foo" , entry 7 + + OR + + a.#.#.b = "foo" , entry 8 + + a.#.#.b.# = "foo" , entry 9 + + a.#.b = "foo" , entry 10 + + a.#.b.# = "foo" , entry 11 + + OR + + a.#.b = "foo" , entry 12 + + a.#.b.# = "foo" , entry 13 + + a.b = "foo" , entry 14 + + a.b.# = "foo" , entry 15 + + +(1 row) + +SELECT gin_debug_jsonpath_path_value('strict exists($.a ? (@.b == "foo"))'); + gin_debug_jsonpath_path_value +------------------------------- + a.b = "foo" , entry 0 + + +(1 row) + +SELECT gin_debug_jsonpath_path_value('lax exists($.a ? (@.b == "foo").c)'); + gin_debug_jsonpath_path_value +------------------------------------ + OR + + OR + + #.a.#.#.b = "foo" , entry 4 + + #.a.#.#.b.# = "foo" , entry 5 + + #.a.#.b = "foo" , entry 6 + + #.a.#.b.# = "foo" , entry 7 + + OR + + #.a.#.#.b = "foo" , entry 4 + + #.a.#.#.b.# = "foo" , entry 5 + + #.a.#.b = "foo" , entry 6 + + #.a.#.b.# = "foo" , entry 7 + + OR + + #.a.#.b = "foo" , entry 12 + + #.a.#.b.# = "foo" , entry 13 + + #.a.b = "foo" , entry 14 + + #.a.b.# = "foo" , entry 15 + + OR + + #.a.#.b = "foo" , entry 12 + + #.a.#.b.# = "foo" , entry 13 + + #.a.b = "foo" , entry 14 + + #.a.b.# = "foo" , entry 15 + + OR + + a.#.#.b = "foo" , entry 20 + + a.#.#.b.# = "foo" , entry 21 + + a.#.b = "foo" , entry 22 + + a.#.b.# = "foo" , entry 23 + + OR + + a.#.#.b = "foo" , entry 20 + + a.#.#.b.# = "foo" , entry 21 + + a.#.b = "foo" , entry 22 + + a.#.b.# = "foo" , entry 23 + + OR + + a.#.b = "foo" , entry 28 + + a.#.b.# = "foo" , entry 29 + + a.b = "foo" , entry 30 + + a.b.# = "foo" , entry 31 + + OR + + a.#.b = "foo" , entry 28 + + a.#.b.# = "foo" , entry 29 + + a.b = "foo" , entry 30 + + a.b.# = "foo" , entry 31 + + +(1 row) + +SELECT gin_debug_jsonpath_path_value('strict exists($.a ? (@.b == "foo").c)'); + gin_debug_jsonpath_path_value +------------------------------- + a.b = "foo" , entry 0 + + +(1 row) + +select gin_debug_jsonpath_path_value('strict exists($.a.b ? (@.c.d == 1))'); + gin_debug_jsonpath_path_value +------------------------------- + a.b.c.d = 1 , entry 0 + + +(1 row) + +select gin_debug_jsonpath_path_value('lax exists($.a.b ? (@.c.d == 1))'); + gin_debug_jsonpath_path_value +---------------------------------------- + OR + + OR + + #.a.#.b.#.#.c.#.d = 1 , entry 0 + + #.a.#.b.#.#.c.#.d.# = 1 , entry 1 + + #.a.#.b.#.#.c.d = 1 , entry 2 + + #.a.#.b.#.#.c.d.# = 1 , entry 3 + + #.a.#.b.#.c.#.d = 1 , entry 4 + + #.a.#.b.#.c.#.d.# = 1 , entry 5 + + #.a.#.b.#.c.d = 1 , entry 6 + + #.a.#.b.#.c.d.# = 1 , entry 7 + + OR + + #.a.#.b.#.c.#.d = 1 , entry 8 + + #.a.#.b.#.c.#.d.# = 1 , entry 9 + + #.a.#.b.#.c.d = 1 , entry 10 + + #.a.#.b.#.c.d.# = 1 , entry 11 + + #.a.#.b.c.#.d = 1 , entry 12 + + #.a.#.b.c.#.d.# = 1 , entry 13 + + #.a.#.b.c.d = 1 , entry 14 + + #.a.#.b.c.d.# = 1 , entry 15 + + OR + + #.a.b.#.#.c.#.d = 1 , entry 16 + + #.a.b.#.#.c.#.d.# = 1 , entry 17 + + #.a.b.#.#.c.d = 1 , entry 18 + + #.a.b.#.#.c.d.# = 1 , entry 19 + + #.a.b.#.c.#.d = 1 , entry 20 + + #.a.b.#.c.#.d.# = 1 , entry 21 + + #.a.b.#.c.d = 1 , entry 22 + + #.a.b.#.c.d.# = 1 , entry 23 + + OR + + #.a.b.#.c.#.d = 1 , entry 24 + + #.a.b.#.c.#.d.# = 1 , entry 25 + + #.a.b.#.c.d = 1 , entry 26 + + #.a.b.#.c.d.# = 1 , entry 27 + + #.a.b.c.#.d = 1 , entry 28 + + #.a.b.c.#.d.# = 1 , entry 29 + + #.a.b.c.d = 1 , entry 30 + + #.a.b.c.d.# = 1 , entry 31 + + OR + + a.#.b.#.#.c.#.d = 1 , entry 32 + + a.#.b.#.#.c.#.d.# = 1 , entry 33 + + a.#.b.#.#.c.d = 1 , entry 34 + + a.#.b.#.#.c.d.# = 1 , entry 35 + + a.#.b.#.c.#.d = 1 , entry 36 + + a.#.b.#.c.#.d.# = 1 , entry 37 + + a.#.b.#.c.d = 1 , entry 38 + + a.#.b.#.c.d.# = 1 , entry 39 + + OR + + a.#.b.#.c.#.d = 1 , entry 40 + + a.#.b.#.c.#.d.# = 1 , entry 41 + + a.#.b.#.c.d = 1 , entry 42 + + a.#.b.#.c.d.# = 1 , entry 43 + + a.#.b.c.#.d = 1 , entry 44 + + a.#.b.c.#.d.# = 1 , entry 45 + + a.#.b.c.d = 1 , entry 46 + + a.#.b.c.d.# = 1 , entry 47 + + OR + + a.b.#.#.c.#.d = 1 , entry 48 + + a.b.#.#.c.#.d.# = 1 , entry 49 + + a.b.#.#.c.d = 1 , entry 50 + + a.b.#.#.c.d.# = 1 , entry 51 + + a.b.#.c.#.d = 1 , entry 52 + + a.b.#.c.#.d.# = 1 , entry 53 + + a.b.#.c.d = 1 , entry 54 + + a.b.#.c.d.# = 1 , entry 55 + + OR + + a.b.#.c.#.d = 1 , entry 56 + + a.b.#.c.#.d.# = 1 , entry 57 + + a.b.#.c.d = 1 , entry 58 + + a.b.#.c.d.# = 1 , entry 59 + + a.b.c.#.d = 1 , entry 60 + + a.b.c.#.d.# = 1 , entry 61 + + a.b.c.d = 1 , entry 62 + + a.b.c.d.# = 1 , entry 63 + + +(1 row) + +select gin_debug_jsonpath_path_value('strict $.a.b == 1'); + gin_debug_jsonpath_path_value +------------------------------- + a.b = 1 , entry 0 + + +(1 row) + +select gin_debug_jsonpath_path_value('lax $.a.b == 1'); + gin_debug_jsonpath_path_value +------------------------------- + OR + + #.a.#.b = 1 , entry 0 + + #.a.#.b.# = 1 , entry 1 + + #.a.b = 1 , entry 2 + + #.a.b.# = 1 , entry 3 + + a.#.b = 1 , entry 4 + + a.#.b.# = 1 , entry 5 + + a.b = 1 , entry 6 + + a.b.# = 1 , entry 7 + + +(1 row) + +select gin_debug_jsonpath_path_value('strict $.a.b[*] == 1'); + gin_debug_jsonpath_path_value +------------------------------- + a.b.# = 1 , entry 0 + + +(1 row) + +select gin_debug_jsonpath_path_value('lax $.a.b[*] == 1'); + gin_debug_jsonpath_path_value +------------------------------- + OR + + #.a.#.b = 1 , entry 0 + + #.a.#.b.# = 1 , entry 1 + + #.a.#.b.#.# = 1 , entry 2 + + #.a.b = 1 , entry 3 + + #.a.b.# = 1 , entry 4 + + #.a.b.#.# = 1 , entry 5 + + a.#.b = 1 , entry 6 + + a.#.b.# = 1 , entry 7 + + a.#.b.#.# = 1 , entry 8 + + a.b = 1 , entry 9 + + a.b.# = 1 , entry 10 + + a.b.#.# = 1 , entry 11 + + +(1 row) + +select gin_debug_jsonpath_path_value('strict $.a[*].b[*] == 1'); + gin_debug_jsonpath_path_value +------------------------------- + a.#.b.# = 1 , entry 0 + + +(1 row) + +select gin_debug_jsonpath_path_value('lax $.a[*].b[*] == 1'); + gin_debug_jsonpath_path_value +-------------------------------- + OR + + #.a.b = 1 , entry 0 + + #.a.b.# = 1 , entry 1 + + #.a.b.#.# = 1 , entry 2 + + #.a.#.#.b = 1 , entry 3 + + #.a.#.#.b.# = 1 , entry 4 + + #.a.#.#.b.#.# = 1 , entry 5 + + #.a.#.b = 1 , entry 6 + + #.a.#.b.# = 1 , entry 7 + + #.a.#.b.#.# = 1 , entry 8 + + a.b = 1 , entry 9 + + a.b.# = 1 , entry 10 + + a.b.#.# = 1 , entry 11 + + a.#.#.b = 1 , entry 12 + + a.#.#.b.# = 1 , entry 13 + + a.#.#.b.#.# = 1 , entry 14 + + a.#.b = 1 , entry 15 + + a.#.b.# = 1 , entry 16 + + a.#.b.#.# = 1 , entry 17 + + +(1 row) + +select gin_debug_jsonpath_path_value('strict $.a.b[*][*] == 1'); + gin_debug_jsonpath_path_value +------------------------------- + a.b.#.# = 1 , entry 0 + + +(1 row) + +select gin_debug_jsonpath_path_value('lax $.a.b[*][*] == 1'); + gin_debug_jsonpath_path_value +-------------------------------- + OR + + #.a.#.b = 1 , entry 0 + + #.a.#.b.# = 1 , entry 1 + + #.a.#.b.#.# = 1 , entry 2 + + #.a.#.b.# = 1 , entry 3 + + #.a.#.b.#.# = 1 , entry 4 + + #.a.#.b.#.#.# = 1 , entry 5 + + #.a.b = 1 , entry 6 + + #.a.b.# = 1 , entry 7 + + #.a.b.#.# = 1 , entry 8 + + #.a.b.# = 1 , entry 9 + + #.a.b.#.# = 1 , entry 10 + + #.a.b.#.#.# = 1 , entry 11 + + a.#.b = 1 , entry 12 + + a.#.b.# = 1 , entry 13 + + a.#.b.#.# = 1 , entry 14 + + a.#.b.# = 1 , entry 15 + + a.#.b.#.# = 1 , entry 16 + + a.#.b.#.#.# = 1 , entry 17 + + a.b = 1 , entry 18 + + a.b.# = 1 , entry 19 + + a.b.#.# = 1 , entry 20 + + a.b.# = 1 , entry 21 + + a.b.#.# = 1 , entry 22 + + a.b.#.#.# = 1 , entry 23 + + +(1 row) + +select gin_debug_jsonpath_path_value('strict $.a[*].b[*] == 1'); + gin_debug_jsonpath_path_value +------------------------------- + a.#.b.# = 1 , entry 0 + + +(1 row) + +select gin_debug_jsonpath_path_value('lax $.a[*].b[*] == 1'); + gin_debug_jsonpath_path_value +-------------------------------- + OR + + #.a.b = 1 , entry 0 + + #.a.b.# = 1 , entry 1 + + #.a.b.#.# = 1 , entry 2 + + #.a.#.#.b = 1 , entry 3 + + #.a.#.#.b.# = 1 , entry 4 + + #.a.#.#.b.#.# = 1 , entry 5 + + #.a.#.b = 1 , entry 6 + + #.a.#.b.# = 1 , entry 7 + + #.a.#.b.#.# = 1 , entry 8 + + a.b = 1 , entry 9 + + a.b.# = 1 , entry 10 + + a.b.#.# = 1 , entry 11 + + a.#.#.b = 1 , entry 12 + + a.#.#.b.# = 1 , entry 13 + + a.#.#.b.#.# = 1 , entry 14 + + a.#.b = 1 , entry 15 + + a.#.b.# = 1 , entry 16 + + a.#.b.#.# = 1 , entry 17 + + +(1 row) + +SELECT gin_debug_jsonpath_path_value('lax $.a > 1 && $.a < 2 && $.b >= 3 && $.c <= "aaa"'); + gin_debug_jsonpath_path_value +------------------------------- + AND + + OR + + #.a > 1 , entry 0 + + #.a.# > 1 , entry 1 + + a > 1 , entry 2 + + a.# > 1 , entry 3 + + OR + + #.a < 2 , entry 4 + + #.a.# < 2 , entry 5 + + a < 2 , entry 6 + + a.# < 2 , entry 7 + + OR + + #.b >= 3 , entry 8 + + #.b.# >= 3 , entry 9 + + b >= 3 , entry 10 + + b.# >= 3 , entry 11 + + +(1 row) + +SELECT gin_debug_jsonpath_path_value('strict $.a > 1 && $.a < 2 && $.b >= 3 && $.c <= "aaa"'); + gin_debug_jsonpath_path_value +------------------------------- + a > 1 , < 2 , entry 0 + + +(1 row) + +SELECT gin_debug_jsonpath_laxpath_value('lax $'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + NULL + + +(1 row) + +SELECT gin_debug_jsonpath_laxpath_value('strict $'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + NULL + + +(1 row) + +SELECT gin_debug_jsonpath_laxpath_value('lax $.a'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + NULL + + +(1 row) + +SELECT gin_debug_jsonpath_laxpath_value('strict $.a'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + NULL + + +(1 row) + +SELECT gin_debug_jsonpath_laxpath_value('lax exists($)'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + $ = * , entry 0 + + +(1 row) + +SELECT gin_debug_jsonpath_laxpath_value('strict exists($)'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + $ = * , entry 0 + + +(1 row) + +SELECT gin_debug_jsonpath_laxpath_value('lax exists($.a)'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + a = * , entry 0 + + +(1 row) + +SELECT gin_debug_jsonpath_laxpath_value('strict exists($.a)'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + a = * , entry 0 + + +(1 row) + +SELECT gin_debug_jsonpath_laxpath_value('lax exists($.a.b)'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + a.b = * , entry 0 + + +(1 row) + +SELECT gin_debug_jsonpath_laxpath_value('strict exists($.a.b)'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + a.b = * , entry 0 + + +(1 row) + +SELECT gin_debug_jsonpath_laxpath_value('lax exists($ ? (@.b == 3).c.d)'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + b = 3 , entry 0 + + +(1 row) + +SELECT gin_debug_jsonpath_laxpath_value('strict exists($ ? (@.b == 3).c.d)'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + b = 3 , entry 0 + + +(1 row) + +SELECT gin_debug_jsonpath_laxpath_value('lax exists($.a ? (@.b == 3).c.d ? (@.e == "aa" || @ == 1))'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + AND + + OR + + a.c.d.e = "aa" , entry 0 + + a.c.d = 1 , entry 1 + + a.b = 3 , entry 2 + + +(1 row) + +SELECT gin_debug_jsonpath_laxpath_value('strict exists($.a ? (@.b == 3).c.d ? (@.e == "aa" || @ == 1))'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + AND + + a.b = 3 , entry 0 + + OR + + a.c.d.e = "aa" , entry 1 + + a.c.d = 1 , entry 2 + + +(1 row) + +SELECT gin_debug_jsonpath_laxpath_value('lax $.a ? (@.b == "foo").c == 1'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + AND + + a.b = "foo" , entry 0 + + a.c = 1 , entry 1 + + +(1 row) + +SELECT gin_debug_jsonpath_laxpath_value('strict $.a ? (@.b == "foo").c == 1'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + AND + + a.b = "foo" , entry 0 + + a.c = 1 , entry 1 + + +(1 row) + +SELECT gin_debug_jsonpath_laxpath_value('lax $.a ? (@.b == "foo") == 1'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + AND + + a = 1 , entry 0 + + a.b = "foo" , entry 1 + + +(1 row) + +SELECT gin_debug_jsonpath_laxpath_value('strict $.a ? (@.b == "foo") == 1'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + AND + + a = 1 , entry 0 + + a.b = "foo" , entry 1 + + +(1 row) + +SELECT gin_debug_jsonpath_laxpath_value('lax $.a ? (@.b == "foo")'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + NULL + + +(1 row) + +SELECT gin_debug_jsonpath_laxpath_value('strict $.a ? (@.b == "foo")'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + NULL + + +(1 row) + +SELECT gin_debug_jsonpath_laxpath_value('lax exists($.a ? (@.b == "foo"))'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + a.b = "foo" , entry 0 + + +(1 row) + +SELECT gin_debug_jsonpath_laxpath_value('strict exists($.a ? (@.b == "foo"))'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + a.b = "foo" , entry 0 + + +(1 row) + +SELECT gin_debug_jsonpath_laxpath_value('lax exists($.a ? (@.b == "foo").c)'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + a.b = "foo" , entry 0 + + +(1 row) + +SELECT gin_debug_jsonpath_laxpath_value('strict exists($.a ? (@.b == "foo").c)'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + a.b = "foo" , entry 0 + + +(1 row) + +select gin_debug_jsonpath_laxpath_value('exists($.a.b ? (@.c.d == 1))'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + a.b.c.d = 1 , entry 0 + + +(1 row) + +select gin_debug_jsonpath_laxpath_value('strict $.a.b == 1'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + a.b = 1 , entry 0 + + +(1 row) + +select gin_debug_jsonpath_laxpath_value('lax $.a.b == 1'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + a.b = 1 , entry 0 + + +(1 row) + +select gin_debug_jsonpath_laxpath_value('strict $.a.b[*] == 1'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + a.b.# = 1 , entry 0 + + +(1 row) + +select gin_debug_jsonpath_laxpath_value('lax $.a.b[*] == 1'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + a.b.# = 1 , entry 0 + + +(1 row) + +select gin_debug_jsonpath_laxpath_value('strict $.a[*].b[*] == 1'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + a.#.b.# = 1 , entry 0 + + +(1 row) + +select gin_debug_jsonpath_laxpath_value('lax $.a[*].b[*] == 1'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + a.#.b.# = 1 , entry 0 + + +(1 row) + +select gin_debug_jsonpath_laxpath_value('strict $.a.b[*][*] == 1'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + a.b.#.# = 1 , entry 0 + + +(1 row) + +select gin_debug_jsonpath_laxpath_value('lax $.a.b[*][*] == 1'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + a.b.#.# = 1 , entry 0 + + +(1 row) + +select gin_debug_jsonpath_laxpath_value('strict $.a[*].b[*] == 1'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + a.#.b.# = 1 , entry 0 + + +(1 row) + +select gin_debug_jsonpath_laxpath_value('lax $.a[*].b[*] == 1'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + a.#.b.# = 1 , entry 0 + + +(1 row) + +SELECT gin_debug_jsonpath_laxpath_value('lax $.a > 1 && $.a < 2 && $.b >= 3 && $.c <= "aaa"'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + AND + + a > 1 , entry 0 + + a < 2 , entry 1 + + b >= 3 , entry 2 + + +(1 row) + +SELECT gin_debug_jsonpath_laxpath_value('strict $.a > 1 && $.a < 2 && $.b >= 3 && $.c <= "aaa"'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + a > 1 , < 2 , entry 0 + + +(1 row) + ---table and index select count(*) from test_jsquery where (v->>'review_helpful_votes')::int4 > 0; count diff --git a/jsonb_gin_ops.c b/jsonb_gin_ops.c index 09bb621..189c4c8 100644 --- a/jsonb_gin_ops.c +++ b/jsonb_gin_ops.c @@ -104,6 +104,7 @@ PG_FUNCTION_INFO_V1(gin_extract_jsonb_query_value_path); PG_FUNCTION_INFO_V1(gin_consistent_jsonb_value_path); PG_FUNCTION_INFO_V1(gin_triconsistent_jsonb_value_path); PG_FUNCTION_INFO_V1(gin_debug_query_value_path); +PG_FUNCTION_INFO_V1(gin_debug_jsonpath_value_path); Datum gin_compare_jsonb_value_path(PG_FUNCTION_ARGS); Datum gin_compare_partial_jsonb_value_path(PG_FUNCTION_ARGS); @@ -112,6 +113,7 @@ Datum gin_extract_jsonb_query_value_path(PG_FUNCTION_ARGS); Datum gin_consistent_jsonb_value_path(PG_FUNCTION_ARGS); Datum gin_triconsistent_jsonb_value_path(PG_FUNCTION_ARGS); Datum gin_debug_query_value_path(PG_FUNCTION_ARGS); +Datum gin_debug_jsonpath_value_path(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(gin_compare_jsonb_path_value); PG_FUNCTION_INFO_V1(gin_compare_partial_jsonb_path_value); @@ -123,6 +125,8 @@ PG_FUNCTION_INFO_V1(gin_consistent_jsonb_path_value); PG_FUNCTION_INFO_V1(gin_triconsistent_jsonb_path_value); PG_FUNCTION_INFO_V1(gin_debug_query_path_value); PG_FUNCTION_INFO_V1(gin_debug_query_laxpath_value); +PG_FUNCTION_INFO_V1(gin_debug_jsonpath_path_value); +PG_FUNCTION_INFO_V1(gin_debug_jsonpath_laxpath_value); Datum gin_compare_jsonb_path_value(PG_FUNCTION_ARGS); Datum gin_compare_partial_jsonb_path_value(PG_FUNCTION_ARGS); @@ -134,6 +138,8 @@ Datum gin_consistent_jsonb_path_value(PG_FUNCTION_ARGS); Datum gin_triconsistent_jsonb_path_value(PG_FUNCTION_ARGS); Datum gin_debug_query_path_value(PG_FUNCTION_ARGS); Datum gin_debug_query_laxpath_value(PG_FUNCTION_ARGS); +Datum gin_debug_jsonpath_path_value(PG_FUNCTION_ARGS); +Datum gin_debug_jsonpath_laxpath_value(PG_FUNCTION_ARGS); static int add_entry(Entries *e, Datum key, Pointer extra, bool pmatch) @@ -764,13 +770,34 @@ gin_debug_query_value_path(PG_FUNCTION_ARGS) { JsQuery *jq; Entries e = {0}; + ExtractedNode *root; char *s; jq = PG_GETARG_JSQUERY(0); - s = debugJsQuery(jq, make_value_path_entry_handler, + root = extractJsQuery(jq, make_value_path_entry_handler, check_value_path_entry_handler, (Pointer)&e); + s = debugExtractedQuery(root); + + PG_RETURN_TEXT_P(cstring_to_text(s)); +} + +#ifndef NO_JSONPATH +Datum +gin_debug_jsonpath_value_path(PG_FUNCTION_ARGS) +{ + JsonPath *jp; + Entries e = {0}; + ExtractedNode *root; + char *s; + + jp = PG_GETARG_JSONPATH_P(0); + root = extractJsonPath(jp, false, false, make_value_path_entry_handler, + check_value_path_entry_handler, (Pointer)&e); + s = debugExtractedQuery(root); + PG_RETURN_TEXT_P(cstring_to_text(s)); } +#endif Datum gin_extract_jsonb_query_value_path(PG_FUNCTION_ARGS) @@ -1233,14 +1260,17 @@ gin_debug_query_path_value_internal(FunctionCallInfo fcinfo, bool lax) JsQuery *jq; Entries e = {0}; PathValueExtra extra; + ExtractedNode *root; char *s; extra.entries = &e; extra.lax = lax; jq = PG_GETARG_JSQUERY(0); - s = debugJsQuery(jq, make_path_value_entry_handler, - check_path_value_entry_handler, (Pointer) &extra); + root = extractJsQuery(jq, make_path_value_entry_handler, + check_path_value_entry_handler, (Pointer) &extra); + s = debugExtractedQuery(root); + PG_RETURN_TEXT_P(cstring_to_text(s)); } @@ -1256,6 +1286,42 @@ gin_debug_query_laxpath_value(PG_FUNCTION_ARGS) PG_RETURN_DATUM(gin_debug_query_path_value_internal(fcinfo, true)); } +#ifndef NO_JSONPATH +static Datum +gin_debug_jsonpath_path_value_internal(FunctionCallInfo fcinfo, bool lax) +{ + JsonPath *jp; + Entries e = {0}; + PathValueExtra extra; + ExtractedNode *root; + char *s; + + extra.entries = &e; + extra.lax = lax; + + jp = PG_GETARG_JSONPATH_P(0); + root = extractJsonPath(jp, false, !lax, + make_path_value_entry_handler, + check_path_value_entry_handler, + (Pointer) &extra); + s = debugExtractedQuery(root); + + PG_RETURN_TEXT_P(cstring_to_text(s)); +} + +Datum +gin_debug_jsonpath_path_value(PG_FUNCTION_ARGS) +{ + PG_RETURN_DATUM(gin_debug_jsonpath_path_value_internal(fcinfo, false)); +} + +Datum +gin_debug_jsonpath_laxpath_value(PG_FUNCTION_ARGS) +{ + PG_RETURN_DATUM(gin_debug_jsonpath_path_value_internal(fcinfo, true)); +} +#endif + static Datum gin_extract_jsonb_query_path_value_internal(FunctionCallInfo fcinfo, bool lax) { diff --git a/jsquery--1.1.sql b/jsquery--1.1.sql index 338da38..22d0c16 100644 --- a/jsquery--1.1.sql +++ b/jsquery--1.1.sql @@ -356,6 +356,21 @@ BEGIN ADD OPERATOR 15 @? (jsonb, jsonpath); ALTER OPERATOR FAMILY jsonb_value_path_ops USING gin ADD OPERATOR 16 @@ (jsonb, jsonpath); + + CREATE OR REPLACE FUNCTION gin_debug_jsonpath_value_path(jsonpath) + RETURNS text + AS 'MODULE_PATHNAME' + LANGUAGE C STRICT IMMUTABLE; + + CREATE OR REPLACE FUNCTION gin_debug_jsonpath_path_value(jsonpath) + RETURNS text + AS 'MODULE_PATHNAME' + LANGUAGE C STRICT IMMUTABLE; + + CREATE OR REPLACE FUNCTION gin_debug_jsonpath_laxpath_value(jsonpath) + RETURNS text + AS 'MODULE_PATHNAME' + LANGUAGE C STRICT IMMUTABLE; END IF; END $$; diff --git a/jsquery.h b/jsquery.h index d585fd8..8705481 100644 --- a/jsquery.h +++ b/jsquery.h @@ -249,8 +249,7 @@ ExtractedNode *extractJsonPath(JsonPath *jp, bool exists, bool arrayPathItems, MakeEntryHandler makeHandler, CheckEntryHandler checkHandler, Pointer extra); #endif -char *debugJsQuery(JsQuery *jq, MakeEntryHandler makeHandler, - CheckEntryHandler checkHandler, Pointer extra); +char *debugExtractedQuery(ExtractedNode *root); bool queryNeedRecheck(ExtractedNode *node); bool execRecursive(ExtractedNode *node, bool *check); bool execRecursiveTristate(ExtractedNode *node, GinTernaryValue *check); diff --git a/jsquery_extract.c b/jsquery_extract.c index 8a86ce6..c267584 100644 --- a/jsquery_extract.c +++ b/jsquery_extract.c @@ -1591,13 +1591,10 @@ debugRecursive(StringInfo buf, ExtractedNode *node, int shift) * Debug print of query processing. */ char * -debugJsQuery(JsQuery *jq, MakeEntryHandler makeHandler, - CheckEntryHandler checkHandler, Pointer extra) +debugExtractedQuery(ExtractedNode *root) { - ExtractedNode *root; StringInfoData buf; - root = extractJsQuery(jq, makeHandler, checkHandler, extra); if (!root) return "NULL\n"; diff --git a/sql/jsquery.sql b/sql/jsquery.sql index 1efb033..a752922 100644 --- a/sql/jsquery.sql +++ b/sql/jsquery.sql @@ -477,6 +477,124 @@ SELECT gin_debug_query_value_path('$ = true'); SELECT gin_debug_query_value_path('$ . ? (review_votes > 10) . review_rating < 7'); SELECT gin_debug_query_value_path('similar_product_ids . ? (# = "B0002W4TL2") . $'); +-- -extract jsonpath entries for index scan + +SELECT gin_debug_jsonpath_value_path('lax $'); +SELECT gin_debug_jsonpath_value_path('strict $'); +SELECT gin_debug_jsonpath_value_path('lax $.a'); +SELECT gin_debug_jsonpath_value_path('strict $.a'); +SELECT gin_debug_jsonpath_value_path('lax exists($)'); +SELECT gin_debug_jsonpath_value_path('strict exists($)'); +SELECT gin_debug_jsonpath_value_path('lax exists($.a)'); +SELECT gin_debug_jsonpath_value_path('strict exists($.a)'); +SELECT gin_debug_jsonpath_value_path('lax exists($.a.b)'); +SELECT gin_debug_jsonpath_value_path('strict exists($.a.b)'); + +SELECT gin_debug_jsonpath_value_path('lax exists($ ? (@.b == 3).c.d)'); +SELECT gin_debug_jsonpath_value_path('strict exists($ ? (@.b == 3).c.d)'); +SELECT gin_debug_jsonpath_value_path('lax exists($.a ? (@.b == 3).c.d ? (@.e == "aa" || @ == 1))'); +SELECT gin_debug_jsonpath_value_path('strict exists($.a ? (@.b == 3).c.d ? (@.e == "aa" || @ == 1))'); + +SELECT gin_debug_jsonpath_value_path('lax $.a ? (@.b == "foo").c == 1'); +SELECT gin_debug_jsonpath_value_path('strict $.a ? (@.b == "foo").c == 1'); +SELECT gin_debug_jsonpath_value_path('lax $.a ? (@.b == "foo") == 1'); +SELECT gin_debug_jsonpath_value_path('strict $.a ? (@.b == "foo") == 1'); +SELECT gin_debug_jsonpath_value_path('lax $.a ? (@.b == "foo")'); +SELECT gin_debug_jsonpath_value_path('strict $.a ? (@.b == "foo")'); +SELECT gin_debug_jsonpath_value_path('lax exists($.a ? (@.b == "foo"))'); +SELECT gin_debug_jsonpath_value_path('strict exists($.a ? (@.b == "foo"))'); +SELECT gin_debug_jsonpath_value_path('lax exists($.a ? (@.b == "foo").c)'); +SELECT gin_debug_jsonpath_value_path('strict exists($.a ? (@.b == "foo").c)'); +select gin_debug_jsonpath_value_path('lax exists($.a.b ? (@.c.d == 1))'); +select gin_debug_jsonpath_value_path('strict exists($.a.b ? (@.c.d == 1))'); + +select gin_debug_jsonpath_value_path('$.a > 1'); + +SELECT gin_debug_jsonpath_path_value('lax $'); +SELECT gin_debug_jsonpath_path_value('strict $'); +SELECT gin_debug_jsonpath_path_value('lax $.a'); +SELECT gin_debug_jsonpath_path_value('strict $.a'); +SELECT gin_debug_jsonpath_path_value('lax exists($)'); +SELECT gin_debug_jsonpath_path_value('strict exists($)'); +SELECT gin_debug_jsonpath_path_value('lax exists($.a)'); +SELECT gin_debug_jsonpath_path_value('strict exists($.a)'); +SELECT gin_debug_jsonpath_path_value('lax exists($.a.b)'); +SELECT gin_debug_jsonpath_path_value('strict exists($.a.b)'); + +SELECT gin_debug_jsonpath_path_value('lax exists($ ? (@.b == 3).c.d)'); +SELECT gin_debug_jsonpath_path_value('strict exists($ ? (@.b == 3).c.d)'); +SELECT gin_debug_jsonpath_path_value('lax exists($.a ? (@.b == 3).c.d ? (@.e == "aa" || @ == 1))'); +SELECT gin_debug_jsonpath_path_value('strict exists($.a ? (@.b == 3).c.d ? (@.e == "aa" || @ == 1))'); + +SELECT gin_debug_jsonpath_path_value('lax $.a ? (@.b == "foo").c == 1'); +SELECT gin_debug_jsonpath_path_value('strict $.a ? (@.b == "foo").c == 1'); +SELECT gin_debug_jsonpath_path_value('lax $.a ? (@.b == "foo") == 1'); +SELECT gin_debug_jsonpath_path_value('strict $.a ? (@.b == "foo") == 1'); +SELECT gin_debug_jsonpath_path_value('lax $.a ? (@.b == "foo")'); +SELECT gin_debug_jsonpath_path_value('strict $.a ? (@.b == "foo")'); +SELECT gin_debug_jsonpath_path_value('lax exists($.a ? (@.b == "foo"))'); +SELECT gin_debug_jsonpath_path_value('strict exists($.a ? (@.b == "foo"))'); +SELECT gin_debug_jsonpath_path_value('lax exists($.a ? (@.b == "foo").c)'); +SELECT gin_debug_jsonpath_path_value('strict exists($.a ? (@.b == "foo").c)'); +select gin_debug_jsonpath_path_value('strict exists($.a.b ? (@.c.d == 1))'); +select gin_debug_jsonpath_path_value('lax exists($.a.b ? (@.c.d == 1))'); + +select gin_debug_jsonpath_path_value('strict $.a.b == 1'); +select gin_debug_jsonpath_path_value('lax $.a.b == 1'); +select gin_debug_jsonpath_path_value('strict $.a.b[*] == 1'); +select gin_debug_jsonpath_path_value('lax $.a.b[*] == 1'); +select gin_debug_jsonpath_path_value('strict $.a[*].b[*] == 1'); +select gin_debug_jsonpath_path_value('lax $.a[*].b[*] == 1'); +select gin_debug_jsonpath_path_value('strict $.a.b[*][*] == 1'); +select gin_debug_jsonpath_path_value('lax $.a.b[*][*] == 1'); +select gin_debug_jsonpath_path_value('strict $.a[*].b[*] == 1'); +select gin_debug_jsonpath_path_value('lax $.a[*].b[*] == 1'); + +SELECT gin_debug_jsonpath_path_value('lax $.a > 1 && $.a < 2 && $.b >= 3 && $.c <= "aaa"'); +SELECT gin_debug_jsonpath_path_value('strict $.a > 1 && $.a < 2 && $.b >= 3 && $.c <= "aaa"'); + +SELECT gin_debug_jsonpath_laxpath_value('lax $'); +SELECT gin_debug_jsonpath_laxpath_value('strict $'); +SELECT gin_debug_jsonpath_laxpath_value('lax $.a'); +SELECT gin_debug_jsonpath_laxpath_value('strict $.a'); +SELECT gin_debug_jsonpath_laxpath_value('lax exists($)'); +SELECT gin_debug_jsonpath_laxpath_value('strict exists($)'); +SELECT gin_debug_jsonpath_laxpath_value('lax exists($.a)'); +SELECT gin_debug_jsonpath_laxpath_value('strict exists($.a)'); +SELECT gin_debug_jsonpath_laxpath_value('lax exists($.a.b)'); +SELECT gin_debug_jsonpath_laxpath_value('strict exists($.a.b)'); + +SELECT gin_debug_jsonpath_laxpath_value('lax exists($ ? (@.b == 3).c.d)'); +SELECT gin_debug_jsonpath_laxpath_value('strict exists($ ? (@.b == 3).c.d)'); +SELECT gin_debug_jsonpath_laxpath_value('lax exists($.a ? (@.b == 3).c.d ? (@.e == "aa" || @ == 1))'); +SELECT gin_debug_jsonpath_laxpath_value('strict exists($.a ? (@.b == 3).c.d ? (@.e == "aa" || @ == 1))'); + +SELECT gin_debug_jsonpath_laxpath_value('lax $.a ? (@.b == "foo").c == 1'); +SELECT gin_debug_jsonpath_laxpath_value('strict $.a ? (@.b == "foo").c == 1'); +SELECT gin_debug_jsonpath_laxpath_value('lax $.a ? (@.b == "foo") == 1'); +SELECT gin_debug_jsonpath_laxpath_value('strict $.a ? (@.b == "foo") == 1'); +SELECT gin_debug_jsonpath_laxpath_value('lax $.a ? (@.b == "foo")'); +SELECT gin_debug_jsonpath_laxpath_value('strict $.a ? (@.b == "foo")'); +SELECT gin_debug_jsonpath_laxpath_value('lax exists($.a ? (@.b == "foo"))'); +SELECT gin_debug_jsonpath_laxpath_value('strict exists($.a ? (@.b == "foo"))'); +SELECT gin_debug_jsonpath_laxpath_value('lax exists($.a ? (@.b == "foo").c)'); +SELECT gin_debug_jsonpath_laxpath_value('strict exists($.a ? (@.b == "foo").c)'); +select gin_debug_jsonpath_laxpath_value('exists($.a.b ? (@.c.d == 1))'); + +select gin_debug_jsonpath_laxpath_value('strict $.a.b == 1'); +select gin_debug_jsonpath_laxpath_value('lax $.a.b == 1'); +select gin_debug_jsonpath_laxpath_value('strict $.a.b[*] == 1'); +select gin_debug_jsonpath_laxpath_value('lax $.a.b[*] == 1'); +select gin_debug_jsonpath_laxpath_value('strict $.a[*].b[*] == 1'); +select gin_debug_jsonpath_laxpath_value('lax $.a[*].b[*] == 1'); +select gin_debug_jsonpath_laxpath_value('strict $.a.b[*][*] == 1'); +select gin_debug_jsonpath_laxpath_value('lax $.a.b[*][*] == 1'); +select gin_debug_jsonpath_laxpath_value('strict $.a[*].b[*] == 1'); +select gin_debug_jsonpath_laxpath_value('lax $.a[*].b[*] == 1'); + +SELECT gin_debug_jsonpath_laxpath_value('lax $.a > 1 && $.a < 2 && $.b >= 3 && $.c <= "aaa"'); +SELECT gin_debug_jsonpath_laxpath_value('strict $.a > 1 && $.a < 2 && $.b >= 3 && $.c <= "aaa"'); + ---table and index select count(*) from test_jsquery where (v->>'review_helpful_votes')::int4 > 0; From b2248ec22a2235b4275d811a2752e305f46ee936 Mon Sep 17 00:00:00 2001 From: Nikita Glukhov Date: Tue, 16 Apr 2019 17:55:02 +0300 Subject: [PATCH 14/21] Add optimization flags to debug functions --- jsonb_gin_ops.c | 109 +++++++++++++++++++++++++++++----------------- jsquery--1.1.sql | 12 ++--- jsquery.h | 12 ++++- jsquery_extract.c | 56 ++++++++++++++---------- 4 files changed, 120 insertions(+), 69 deletions(-) diff --git a/jsonb_gin_ops.c b/jsonb_gin_ops.c index 189c4c8..8f0683e 100644 --- a/jsonb_gin_ops.c +++ b/jsonb_gin_ops.c @@ -765,37 +765,76 @@ gin_extract_jsonb_value_path(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gin_extract_jsonb_value_path_internal(jb, nentries, NULL)); } -Datum -gin_debug_query_value_path(PG_FUNCTION_ARGS) +static text * +gin_debug_query_internal(FunctionCallInfo fcinfo, + MakeEntryHandler makeHandler, + CheckEntryHandler checkHandler, Pointer extra) { - JsQuery *jq; - Entries e = {0}; + JsQuery *jq = PG_GETARG_JSQUERY(0); ExtractedNode *root; char *s; + int optimize = 0; + + if (PG_GETARG_BOOL(1)) + optimize |= optFlatten; + if (PG_GETARG_BOOL(2)) + optimize |= optSimplify; + if (PG_GETARG_BOOL(3)) + optimize |= optSelectivity; - jq = PG_GETARG_JSQUERY(0); - root = extractJsQuery(jq, make_value_path_entry_handler, - check_value_path_entry_handler, (Pointer)&e); + root = extractJsQuery(jq, optimize, makeHandler, checkHandler, extra); s = debugExtractedQuery(root); - PG_RETURN_TEXT_P(cstring_to_text(s)); + return cstring_to_text(s); } -#ifndef NO_JSONPATH Datum -gin_debug_jsonpath_value_path(PG_FUNCTION_ARGS) +gin_debug_query_value_path(PG_FUNCTION_ARGS) { - JsonPath *jp; Entries e = {0}; + + PG_RETURN_TEXT_P(gin_debug_query_internal(fcinfo, + make_value_path_entry_handler, + check_value_path_entry_handler, + (Pointer) &e)); +} + +#ifndef NO_JSONPATH +static text * +gin_debug_jsonpath_internal(FunctionCallInfo fcinfo, + bool arrayPathItems, + MakeEntryHandler makeHandler, + CheckEntryHandler checkHandler, Pointer extra) +{ + JsonPath *jp = PG_GETARG_JSONPATH_P(0); ExtractedNode *root; char *s; - - jp = PG_GETARG_JSONPATH_P(0); - root = extractJsonPath(jp, false, false, make_value_path_entry_handler, - check_value_path_entry_handler, (Pointer)&e); + int optimize = 0; + bool exists = PG_GETARG_BOOL(1); + + if (PG_GETARG_BOOL(2)) + optimize |= optFlatten; + if (PG_GETARG_BOOL(3)) + optimize |= optSimplify; + if (PG_GETARG_BOOL(4)) + optimize |= optSelectivity; + + root = extractJsonPath(jp, exists, arrayPathItems, optimize, + makeHandler, checkHandler, extra); s = debugExtractedQuery(root); - PG_RETURN_TEXT_P(cstring_to_text(s)); + return cstring_to_text(s); +} + +Datum +gin_debug_jsonpath_value_path(PG_FUNCTION_ARGS) +{ + Entries e = {0}; + + PG_RETURN_TEXT_P(gin_debug_jsonpath_internal(fcinfo, false, + make_value_path_entry_handler, + check_value_path_entry_handler, + (Pointer) &e)); } #endif @@ -843,12 +882,14 @@ gin_extract_jsonb_query_value_path(PG_FUNCTION_ARGS) root = extractJsonPath(PG_GETARG_JSONPATH_P(0), strategy == JsonpathExistsStrategyNumber, false, + optAll, make_value_path_entry_handler, check_value_path_entry_handler, (Pointer)&e); else #endif root = extractJsQuery(PG_GETARG_JSQUERY(0), + optAll, make_value_path_entry_handler, check_value_path_entry_handler, (Pointer)&e); @@ -1257,21 +1298,16 @@ gin_extract_jsonb_laxpath_value(PG_FUNCTION_ARGS) static Datum gin_debug_query_path_value_internal(FunctionCallInfo fcinfo, bool lax) { - JsQuery *jq; Entries e = {0}; PathValueExtra extra; - ExtractedNode *root; - char *s; extra.entries = &e; extra.lax = lax; - jq = PG_GETARG_JSQUERY(0); - root = extractJsQuery(jq, make_path_value_entry_handler, - check_path_value_entry_handler, (Pointer) &extra); - s = debugExtractedQuery(root); - - PG_RETURN_TEXT_P(cstring_to_text(s)); + PG_RETURN_TEXT_P(gin_debug_query_internal(fcinfo, + make_path_value_entry_handler, + check_path_value_entry_handler, + (Pointer) &extra)); } Datum @@ -1287,38 +1323,31 @@ gin_debug_query_laxpath_value(PG_FUNCTION_ARGS) } #ifndef NO_JSONPATH -static Datum +static text * gin_debug_jsonpath_path_value_internal(FunctionCallInfo fcinfo, bool lax) { - JsonPath *jp; Entries e = {0}; PathValueExtra extra; - ExtractedNode *root; - char *s; extra.entries = &e; extra.lax = lax; - jp = PG_GETARG_JSONPATH_P(0); - root = extractJsonPath(jp, false, !lax, - make_path_value_entry_handler, - check_path_value_entry_handler, - (Pointer) &extra); - s = debugExtractedQuery(root); - - PG_RETURN_TEXT_P(cstring_to_text(s)); + return gin_debug_jsonpath_internal(fcinfo, !lax, + make_path_value_entry_handler, + check_path_value_entry_handler, + (Pointer) &extra); } Datum gin_debug_jsonpath_path_value(PG_FUNCTION_ARGS) { - PG_RETURN_DATUM(gin_debug_jsonpath_path_value_internal(fcinfo, false)); + PG_RETURN_TEXT_P(gin_debug_jsonpath_path_value_internal(fcinfo, false)); } Datum gin_debug_jsonpath_laxpath_value(PG_FUNCTION_ARGS) { - PG_RETURN_DATUM(gin_debug_jsonpath_path_value_internal(fcinfo, true)); + PG_RETURN_TEXT_P(gin_debug_jsonpath_path_value_internal(fcinfo, true)); } #endif @@ -1355,12 +1384,14 @@ gin_extract_jsonb_query_path_value_internal(FunctionCallInfo fcinfo, bool lax) root = extractJsonPath(PG_GETARG_JSONPATH_P(0), strategy == JsonpathExistsStrategyNumber, !lax, + optAll, make_path_value_entry_handler, check_path_value_entry_handler, (Pointer) &extra); else #endif root = extractJsQuery(PG_GETARG_JSQUERY(0), + optAll, make_path_value_entry_handler, check_path_value_entry_handler, (Pointer) &extra); diff --git a/jsquery--1.1.sql b/jsquery--1.1.sql index 22d0c16..fcd9c30 100644 --- a/jsquery--1.1.sql +++ b/jsquery--1.1.sql @@ -315,17 +315,17 @@ CREATE OPERATOR CLASS jsonb_laxpath_value_ops FUNCTION 6 gin_triconsistent_jsonb_path_value(internal, smallint, anyarray, integer, internal, internal, internal), STORAGE bytea; -CREATE OR REPLACE FUNCTION gin_debug_query_value_path(jsquery) +CREATE OR REPLACE FUNCTION gin_debug_query_value_path(jsquery, flatten bool = true, simplify bool = true, selectivity bool = true) RETURNS text AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_debug_query_path_value(jsquery) +CREATE OR REPLACE FUNCTION gin_debug_query_path_value(jsquery, flatten bool = true, simplify bool = true, selectivity bool = true) RETURNS text AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_debug_query_laxpath_value(jsquery) +CREATE OR REPLACE FUNCTION gin_debug_query_laxpath_value(jsquery, flatten bool = true, simplify bool = true, selectivity bool = true) RETURNS text AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -357,17 +357,17 @@ BEGIN ALTER OPERATOR FAMILY jsonb_value_path_ops USING gin ADD OPERATOR 16 @@ (jsonb, jsonpath); - CREATE OR REPLACE FUNCTION gin_debug_jsonpath_value_path(jsonpath) + CREATE OR REPLACE FUNCTION gin_debug_jsonpath_value_path(jsonpath, is_exists bool = false, flatten bool = true, simplify bool = true, selectivity bool = true) RETURNS text AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; - CREATE OR REPLACE FUNCTION gin_debug_jsonpath_path_value(jsonpath) + CREATE OR REPLACE FUNCTION gin_debug_jsonpath_path_value(jsonpath, is_exists bool = false, flatten bool = true, simplify bool = true, selectivity bool = true) RETURNS text AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; - CREATE OR REPLACE FUNCTION gin_debug_jsonpath_laxpath_value(jsonpath) + CREATE OR REPLACE FUNCTION gin_debug_jsonpath_laxpath_value(jsonpath, is_exists bool = false, flatten bool = true, simplify bool = true, selectivity bool = true) RETURNS text AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; diff --git a/jsquery.h b/jsquery.h index 8705481..d44f82e 100644 --- a/jsquery.h +++ b/jsquery.h @@ -242,10 +242,20 @@ typedef int (*MakeEntryHandler)(ExtractedNode *node, Pointer extra); typedef bool (*CheckEntryHandler)(ExtractedNode *node, Pointer extra); bool isLogicalNodeType(ExtractedNodeType type); -ExtractedNode *extractJsQuery(JsQuery *jq, MakeEntryHandler makeHandler, +typedef enum +{ + optFlatten = 0x01, + optSimplify = 0x02, + optSelectivity = 0x04, + optAll = 0x07 +} JsQueryOptFlags; + +ExtractedNode *extractJsQuery(JsQuery *jq, int optimize, + MakeEntryHandler makeHandler, CheckEntryHandler checkHandler, Pointer extra); #ifndef NO_JSONPATH ExtractedNode *extractJsonPath(JsonPath *jp, bool exists, bool arrayPathItems, + int optimize, MakeEntryHandler makeHandler, CheckEntryHandler checkHandler, Pointer extra); #endif diff --git a/jsquery_extract.c b/jsquery_extract.c index c267584..747c697 100644 --- a/jsquery_extract.c +++ b/jsquery_extract.c @@ -33,7 +33,7 @@ static int compareJsQueryItem(JsQueryItem *v1, JsQueryItem *v2); static void processGroup(ExtractedNode *node, int start, int end); static void simplifyRecursive(ExtractedNode *node); static SelectivityClass getScalarSelectivityClass(ExtractedNode *node); -static ExtractedNode *makeEntries(ExtractedNode *node, MakeEntryHandler handler, Pointer extra); +static ExtractedNode *makeEntries(ExtractedNode *node, bool skipNonSelective, MakeEntryHandler handler, Pointer extra); static void setSelectivityClass(ExtractedNode *node, CheckEntryHandler checkHandler, Pointer extra); static void debugPath(StringInfo buf, PathItem *path); static void debugValue(StringInfo buf, JsQueryItem *v); @@ -1204,7 +1204,8 @@ getScalarSelectivityClass(ExtractedNode *node) * Make entries for all leaf tree nodes using user-provided handler. */ static ExtractedNode * -makeEntries(ExtractedNode *node, MakeEntryHandler handler, Pointer extra) +makeEntries(ExtractedNode *node, bool skipNonSelective, + MakeEntryHandler handler, Pointer extra) { if (node->type == eAnd || node->type == eOr) { @@ -1216,11 +1217,12 @@ makeEntries(ExtractedNode *node, MakeEntryHandler handler, Pointer extra) if (!child) continue; /* Skip non-selective AND children */ - if (child->sClass > node->sClass && + if (skipNonSelective && + child->sClass > node->sClass && node->type == eAnd && !child->forceIndex) continue; - child = makeEntries(child, handler, extra); + child = makeEntries(child, skipNonSelective, handler, extra); if (child) { node->args.items[j] = child; @@ -1319,26 +1321,40 @@ setSelectivityClass(ExtractedNode *node, CheckEntryHandler checkHandler, } } +static ExtractedNode * +emitExtractedQuery(ExtractedNode *root, int optimize, + MakeEntryHandler makeHandler, + CheckEntryHandler checkHandler, + Pointer extra) +{ + if (!root) + return NULL; + + if (optimize & optFlatten) + flatternTree(root); + + if (optimize & optSimplify) + simplifyRecursive(root); + + setSelectivityClass(root, checkHandler, extra); + + return makeEntries(root, (optimize & optSelectivity) != 0, makeHandler, extra); +} + /* * Turn jsquery into tree of entries using user-provided handler. */ ExtractedNode * -extractJsQuery(JsQuery *jq, MakeEntryHandler makeHandler, - CheckEntryHandler checkHandler, Pointer extra) +extractJsQuery(JsQuery *jq, int optimize, MakeEntryHandler makeHandler, + CheckEntryHandler checkHandler, Pointer extra) { ExtractedNode *root; JsQueryItem jsq; jsqInit(&jsq, jq); root = recursiveExtract(&jsq, false, false, NULL); - if (root) - { - flatternTree(root); - simplifyRecursive(root); - setSelectivityClass(root, checkHandler, extra); - root = makeEntries(root, makeHandler, extra); - } - return root; + + return emitExtractedQuery(root, optimize, makeHandler, checkHandler, extra); } #ifndef NO_JSONPATH @@ -1346,7 +1362,7 @@ extractJsQuery(JsQuery *jq, MakeEntryHandler makeHandler, * Turn jsonpath into tree of entries using user-provided handler. */ ExtractedNode * -extractJsonPath(JsonPath *jp, bool exists, bool arrayPathItems, +extractJsonPath(JsonPath *jp, bool exists, bool arrayPathItems, int optimize, MakeEntryHandler makeHandler, CheckEntryHandler checkHandler, Pointer extra) { @@ -1358,14 +1374,8 @@ extractJsonPath(JsonPath *jp, bool exists, bool arrayPathItems, root = exists ? extractJsonPathExists(&jsp, lax, NULL) : recursiveExtractJsonPathExpr(&jsp, lax, false, NULL); - if (root) - { - flatternTree(root); - simplifyRecursive(root); - setSelectivityClass(root, checkHandler, extra); - root = makeEntries(root, makeHandler, extra); - } - return root; + + return emitExtractedQuery(root, optimize, makeHandler, checkHandler, extra); } #endif From 6c7578a2807efdd37ec20718ef9144182fa47bb8 Mon Sep 17 00:00:00 2001 From: Nikita Glukhov Date: Thu, 18 Apr 2019 19:00:52 +0300 Subject: [PATCH 15/21] Rename extractJsonPath() => extractJsonPathQuery() --- jsonb_gin_ops.c | 32 ++++++++++++++++---------------- jsquery.h | 7 +++---- jsquery_extract.c | 6 +++--- 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/jsonb_gin_ops.c b/jsonb_gin_ops.c index 8f0683e..45b4753 100644 --- a/jsonb_gin_ops.c +++ b/jsonb_gin_ops.c @@ -819,8 +819,8 @@ gin_debug_jsonpath_internal(FunctionCallInfo fcinfo, if (PG_GETARG_BOOL(4)) optimize |= optSelectivity; - root = extractJsonPath(jp, exists, arrayPathItems, optimize, - makeHandler, checkHandler, extra); + root = extractJsonPathQuery(jp, exists, arrayPathItems, optimize, + makeHandler, checkHandler, extra); s = debugExtractedQuery(root); return cstring_to_text(s); @@ -879,13 +879,13 @@ gin_extract_jsonb_query_value_path(PG_FUNCTION_ARGS) case JsonpathExistsStrategyNumber: case JsonpathMatchStrategyNumber: if (strategy != JsQueryMatchStrategyNumber) - root = extractJsonPath(PG_GETARG_JSONPATH_P(0), - strategy == JsonpathExistsStrategyNumber, - false, - optAll, - make_value_path_entry_handler, - check_value_path_entry_handler, - (Pointer)&e); + root = extractJsonPathQuery(PG_GETARG_JSONPATH_P(0), + strategy == JsonpathExistsStrategyNumber, + false, + optAll, + make_value_path_entry_handler, + check_value_path_entry_handler, + (Pointer) &e); else #endif root = extractJsQuery(PG_GETARG_JSQUERY(0), @@ -1381,13 +1381,13 @@ gin_extract_jsonb_query_path_value_internal(FunctionCallInfo fcinfo, bool lax) case JsonpathExistsStrategyNumber: case JsonpathMatchStrategyNumber: if (strategy != JsQueryMatchStrategyNumber) - root = extractJsonPath(PG_GETARG_JSONPATH_P(0), - strategy == JsonpathExistsStrategyNumber, - !lax, - optAll, - make_path_value_entry_handler, - check_path_value_entry_handler, - (Pointer) &extra); + root = extractJsonPathQuery(PG_GETARG_JSONPATH_P(0), + strategy == JsonpathExistsStrategyNumber, + !lax, + optAll, + make_path_value_entry_handler, + check_path_value_entry_handler, + (Pointer) &extra); else #endif root = extractJsQuery(PG_GETARG_JSQUERY(0), diff --git a/jsquery.h b/jsquery.h index d44f82e..0a6a955 100644 --- a/jsquery.h +++ b/jsquery.h @@ -254,10 +254,9 @@ ExtractedNode *extractJsQuery(JsQuery *jq, int optimize, MakeEntryHandler makeHandler, CheckEntryHandler checkHandler, Pointer extra); #ifndef NO_JSONPATH -ExtractedNode *extractJsonPath(JsonPath *jp, bool exists, bool arrayPathItems, - int optimize, - MakeEntryHandler makeHandler, - CheckEntryHandler checkHandler, Pointer extra); +ExtractedNode *extractJsonPathQuery(JsonPath *jp, bool exists, bool arrayPathItems, + int optimize, MakeEntryHandler makeHandler, + CheckEntryHandler checkHandler, Pointer extra); #endif char *debugExtractedQuery(ExtractedNode *root); bool queryNeedRecheck(ExtractedNode *node); diff --git a/jsquery_extract.c b/jsquery_extract.c index 747c697..f676ae4 100644 --- a/jsquery_extract.c +++ b/jsquery_extract.c @@ -1362,9 +1362,9 @@ extractJsQuery(JsQuery *jq, int optimize, MakeEntryHandler makeHandler, * Turn jsonpath into tree of entries using user-provided handler. */ ExtractedNode * -extractJsonPath(JsonPath *jp, bool exists, bool arrayPathItems, int optimize, - MakeEntryHandler makeHandler, - CheckEntryHandler checkHandler, Pointer extra) +extractJsonPathQuery(JsonPath *jp, bool exists, bool arrayPathItems, int optimize, + MakeEntryHandler makeHandler, + CheckEntryHandler checkHandler, Pointer extra) { ExtractedNode *root; JsonPathItem jsp; From 3116deb5899076f12377191bcbd544aa5ba7c5a2 Mon Sep 17 00:00:00 2001 From: Nikita Glukhov Date: Thu, 18 Apr 2019 19:10:26 +0300 Subject: [PATCH 16/21] Fix indirect jsonpath expressions --- jsquery_extract.c | 320 +++++++++++++++++++++++----------------------- 1 file changed, 161 insertions(+), 159 deletions(-) diff --git a/jsquery_extract.c b/jsquery_extract.c index f676ae4..c31cbc0 100644 --- a/jsquery_extract.c +++ b/jsquery_extract.c @@ -24,6 +24,9 @@ static ExtractedNode *recursiveExtract(JsQueryItem *jsq, bool not, bool indirect, PathItem *path); static ExtractedNode *makeAnyNode(bool not, bool indirect, PathItem *path); +static ExtractedNode *makeBinaryNode(ExtractedNodeType type, PathItem *path, + bool indirect, ExtractedNode *leftNode, ExtractedNode *rightNode); +static ExtractedNode *makeEmptyArrayNode(PathItem *path, JsQueryHint hint); static int coundChildren(ExtractedNode *node, ExtractedNodeType type, bool first, bool *found); static void fillChildren(ExtractedNode *node, ExtractedNodeType type, bool first, ExtractedNode **items, int *i); static void flatternTree(ExtractedNode *node); @@ -93,15 +96,7 @@ recursiveExtract(JsQueryItem *jsq, bool not, bool indirect, PathItem *path) } } - result = (ExtractedNode *)palloc(sizeof(ExtractedNode)); - result->type = type; - result->path = path; - result->indirect = indirect; - result->args.items = (ExtractedNode **)palloc(2 * sizeof(ExtractedNode *)); - result->args.items[0] = leftNode; - result->args.items[1] = rightNode; - result->args.count = 2; - return result; + return makeBinaryNode(type, path, indirect, leftNode, rightNode); case jqiNot: jsqGetArg(jsq, &elem); return recursiveExtract(&elem, !not, indirect, path); @@ -214,13 +209,7 @@ recursiveExtract(JsQueryItem *jsq, bool not, bool indirect, PathItem *path) if (jsq->type == jqiContained || (jsq->type == jqiEqual && elem.array.nelems == 0)) { - ExtractedNode *item = (ExtractedNode *)palloc(sizeof(ExtractedNode)); - - item->hint = e.hint; - item->type = eEmptyArray; - item->path = pathItem->parent; - item->indirect = false; - item->hint = jsq->hint; + ExtractedNode *item = makeEmptyArrayNode(pathItem->parent, jsq->hint); /* XXX e.hint */ result->args.items[result->args.count] = item; result->args.count++; @@ -310,19 +299,21 @@ typedef struct ExtractedJsonPath bool type; } ExtractedJsonPath; -static bool -recursiveExtractJsonPath(JsonPathItem *jpi, - bool lax, bool not, bool indirect, bool unwrap, - PathItem *parent, List **paths, List *filters); +typedef enum +{ + efLax = 0x01, + efArrays = 0x02, + efLaxWithArrays = efLax | efArrays +} JsonPathExtractionFlags; static ExtractedNode * -recursiveExtractJsonPathExpr(JsonPathItem *jpi, bool lax, bool not, - PathItem *path); +extractJsonPathExpr(JsonPathItem *jpi, int flags, bool not, bool indirect, + PathItem *path); static bool -recursiveExtractJsonPath2(JsonPathItem *jpi, - bool lax, bool not, bool indirect, bool unwrap, - PathItem *parent, List **paths, List *filters) +extractJsonPathRec(JsonPathItem *jpi, + int flags, bool indirect, bool unwrap, bool preunwrap, + PathItem *parent, List **paths, List *filters) { JsonPathItem next; PathItem *path; @@ -334,44 +325,78 @@ recursiveExtractJsonPath2(JsonPathItem *jpi, case jpiRoot: Assert(!parent); path = NULL; + indirect = parent != NULL; break; case jpiCurrent: path = parent; + indirect = false; break; case jpiKey: + if (flags == efLaxWithArrays && preunwrap && + !extractJsonPathRec(jpi, flags, true, unwrap, false, + makePathItem(iAnyArray, parent), + paths, filters)) + return false; + path = makePathItem(iKey, parent); path->s = jspGetString(jpi, &path->len); break; case jpiAny: - /* case jpiAll: */ - if ((not && jpi->type == jpiAny) /*|| (!not && jpi->type == jpiAll)*/) - return false; - path = makePathItem(iAny, parent); indirect = true; break; + case jpiAnyArray: case jpiIndexArray: - path = makePathItem(iIndexArray, parent); - path->arrayIndex = -1; /* FIXME */ - indirect = true; - break; + if (flags == efLaxWithArrays) + { + /* try to skip [*] path item */ + JsonPathItem next; - case jpiAnyArray: - /* case jpiAllArray: */ - if ((not && jpi->type == jpiAnyArray) /*|| (!not && jpi->type == jpiAllArray)*/) - return false; + if (!jspGetNext(jpi, &next)) + { + ExtractedJsonPath *ejp = palloc(sizeof(*ejp)); + + ejp->path = parent; + ejp->filters = list_copy(filters); + ejp->indirect = indirect; + ejp->type = false; + + *paths = lappend(*paths, ejp); + } + else if (!extractJsonPathRec(&next, flags, indirect, unwrap, + false, parent, paths, filters)) + return false; + } - path = makePathItem(iAnyArray, parent); indirect = true; +#if 0 + if (jpi->content.array.nelems == 1) + { + JsonPathItem from; + JsonPathItem to; + + if (!jspGetArraySubscript(jpi, &from, &to, 0) && + from->type == jpiNumeric) + { + path = makePathItem(iIndexArray, parent); + path->arrayIndex = -1; + break; + } + } +#endif + path = makePathItem(iAnyArray, parent); break; case jpiAnyKey: - /* case jpiAllKey: */ - if ((not && jpi->type == jpiAnyKey) /*|| (!not && jpi->type == jpiAllKey)*/) + /* add implicit [*] path item in lax mode */ + if (flags == efLaxWithArrays && preunwrap && + !extractJsonPathRec(jpi, flags, true, unwrap, false, + makePathItem(iAnyArray, parent), + paths, filters)) return false; path = makePathItem(iAnyKey, parent); @@ -383,11 +408,15 @@ recursiveExtractJsonPath2(JsonPathItem *jpi, JsonPathItem arg; ExtractedNode *expr; - if (not) + /* add implicit [*] path item in lax mode */ + if (flags == efLaxWithArrays && preunwrap && + !extractJsonPathRec(jpi, flags, true, unwrap, false, + makePathItem(iAnyArray, parent), + paths, filters)) return false; jspGetArg(jpi, &arg); - expr = recursiveExtractJsonPathExpr(&arg, lax, not, parent); + expr = extractJsonPathExpr(&arg, flags, false, false, parent); if (expr) filters = lappend(list_copy(filters), expr); @@ -407,19 +436,20 @@ recursiveExtractJsonPath2(JsonPathItem *jpi, ExtractedJsonPath *ejp = palloc(sizeof(*ejp)); ejp->path = path; - ejp->filters = filters; - ejp->indirect = indirect; + ejp->filters = list_copy(filters); + ejp->indirect = flags == efLax ? path != NULL : indirect; ejp->type = jspHasNext(jpi) && next.type == jpiType; *paths = lappend(*paths, ejp); - if (lax && unwrap && //!(path && path->type == iAnyArray) && + if (flags == efLaxWithArrays && unwrap && !(jspHasNext(jpi) && next.type == jpiType)) { ejp = palloc(sizeof(*ejp)); + ejp->path = makePathItem(iAnyArray, path); - ejp->filters = filters; - ejp->indirect = indirect; + ejp->filters = list_copy(filters); + ejp->indirect = true; ejp->type = false; *paths = lappend(*paths, ejp); @@ -428,62 +458,16 @@ recursiveExtractJsonPath2(JsonPathItem *jpi, return true; } - return recursiveExtractJsonPath(&next, lax, not, indirect, unwrap, path, - paths, filters); + return extractJsonPathRec(&next, flags, indirect, unwrap, true, path, paths, filters); } -static bool -recursiveExtractJsonPath(JsonPathItem *jpi, - bool lax, bool not, bool indirect, bool unwrap, - PathItem *parent, List **paths, List *filters) +static List * +extractJsonPath(JsonPathItem *jpi, int flags, bool unwrap, PathItem *parent) { - switch (jpi->type) - { - case jpiRoot: - case jpiAny: - break; - - case jpiAnyArray: - case jpiIndexArray: - if (lax) - { - /* try to skip [*] path item */ - JsonPathItem next; - - if (!jspGetNext(jpi, &next)) - { - ExtractedJsonPath *ejp = palloc(sizeof(*ejp)); - - ejp->path = parent; - ejp->filters = filters; - ejp->indirect = indirect; - ejp->type = false; - - *paths = lappend(*paths, ejp); - } - else if (!recursiveExtractJsonPath2(&next, lax, not, indirect, unwrap, - parent, paths, filters)) - return false; - } - - break; - - case jpiKey: - case jpiAnyKey: - /* add implicit [*] path item in lax mode */ - if (lax && - !recursiveExtractJsonPath2(jpi, lax, not, true, unwrap, - makePathItem(iAnyArray, parent), - paths, filters)) - return false; - break; - - default: - break; - } + List *paths = NIL; - return recursiveExtractJsonPath2(jpi, lax, not, indirect, unwrap, parent, - paths, filters); + return extractJsonPathRec(jpi, flags, false, unwrap, false, parent, + &paths, NIL) ? paths : NIL; } static inline JsQueryItem * @@ -567,14 +551,13 @@ appendJsonPathExprNode(ExtractedNode *result, ExtractedNode *node, PathItem *pat } static ExtractedNode * -extractJsonPathExists(JsonPathItem *jpi, bool lax, PathItem *path) +extractJsonPathExists(JsonPathItem *jpi, int flags, bool indirect, PathItem *path) { - List *paths = NIL; + List *paths; ListCell *lc; ExtractedNode *result; - if (!recursiveExtractJsonPath(jpi, lax, false, false /* FIXME */, false, - path, &paths, NIL)) + if (!(paths = extractJsonPath(jpi, flags, false, path))) return NULL; result = NULL; @@ -590,9 +573,22 @@ extractJsonPathExists(JsonPathItem *jpi, bool lax, PathItem *path) return result; } +static JsQueryItem * +extractJsonPathConst(JsonPathItem *jpi, bool equal) +{ + if (jpi->type == jpiNumeric || + (equal && + (jpi->type == jpiNull || + jpi->type == jpiBool || + jpi->type == jpiString))) + return jspConstToJsQueryItem(jpi); + + return NULL; +} + static ExtractedNode * -recursiveExtractJsonPathExpr(JsonPathItem *jpi, bool lax, bool not, - PathItem *path) +extractJsonPathExpr(JsonPathItem *jpi, int flags, bool not, bool indirect, + PathItem *path) { ExtractedNode *result; JsonPathItem elem; @@ -607,48 +603,36 @@ recursiveExtractJsonPathExpr(JsonPathItem *jpi, bool lax, bool not, ExtractedNode *leftNode; ExtractedNode *rightNode; ExtractedNodeType type; - bool indirect = false; type = ((jpi->type == jpiAnd) == not) ? eOr : eAnd; jspGetLeftArg(jpi, &elem); - leftNode = recursiveExtractJsonPathExpr(&elem, lax, not, path); + leftNode = extractJsonPathExpr(&elem, flags, not, indirect, path); jspGetRightArg(jpi, &elem); - rightNode = recursiveExtractJsonPathExpr(&elem, lax, not, path); + rightNode = extractJsonPathExpr(&elem, flags, not, indirect, path); - if (!leftNode || !rightNode) + if (leftNode && rightNode) + return makeBinaryNode(type, path, indirect, leftNode, rightNode); + else if (type == eOr) + return NULL; + else if (leftNode) { - if (type == eOr || (!leftNode && !rightNode)) - return NULL; - - if (leftNode) - { - leftNode->indirect |= indirect; - return leftNode; - } - else - { - rightNode->indirect |= indirect; - return rightNode; - } + leftNode->indirect |= indirect; + return leftNode; } - - result = palloc(sizeof(ExtractedNode)); - result->type = type; - result->path = path; - result->indirect = indirect; - result->args.items = palloc(2 * sizeof(ExtractedNode *)); - result->args.items[0] = leftNode; - result->args.items[1] = rightNode; - result->args.count = 2; - - return result; + else if (rightNode) + { + rightNode->indirect |= indirect; + return rightNode; + } + else + return NULL; } case jpiNot: jspGetArg(jpi, &elem); - return recursiveExtractJsonPathExpr(&elem, lax, !not, path); + return extractJsonPathExpr(&elem, flags, !not, indirect, path); case jpiEqual: case jpiLess: @@ -664,6 +648,7 @@ recursiveExtractJsonPathExpr(JsonPathItem *jpi, bool lax, bool not, List *paths = NIL; ListCell *lc; JsQueryItem *bound; + JsonPathItem *patharg; JsonPathItem larg; JsonPathItem rarg; @@ -673,35 +658,21 @@ recursiveExtractJsonPathExpr(JsonPathItem *jpi, bool lax, bool not, jspGetLeftArg(jpi, &larg); jspGetRightArg(jpi, &rarg); - if (rarg.type == jpiNumeric || - (equal && - (rarg.type == jpiNull || - rarg.type == jpiBool || - rarg.type == jpiString))) + if ((bound = extractJsonPathConst(&rarg, equal))) { - bound = jspConstToJsQueryItem(&rarg); - - if (!recursiveExtractJsonPath(&larg, lax, not, false/* FIXME */, true, path, - &paths, NIL)) - return NULL; + patharg = &larg; } - else if (larg.type == jpiNumeric || - (equal && - (larg.type == jpiNull || - larg.type == jpiBool || - larg.type == jpiString))) + else if ((bound = extractJsonPathConst(&larg, equal))) { - bound = jspConstToJsQueryItem(&larg); - - if (!recursiveExtractJsonPath(&rarg, lax, not, false/* FIXME */, true, path, - &paths, NIL)) - return NULL; - + patharg = &rarg; greater = !greater; } else return NULL; + if (!(paths = extractJsonPath(patharg, flags, true, path))) + return NULL; + result = NULL; foreach(lc, paths) @@ -784,7 +755,7 @@ recursiveExtractJsonPathExpr(JsonPathItem *jpi, bool lax, bool not, jspGetArg(jpi, &elem); - return extractJsonPathExists(&elem, lax, path); + return extractJsonPathExists(&elem, flags, indirect, path); } default: @@ -815,6 +786,36 @@ makeAnyNode(bool not, bool indirect, PathItem *path) return result; } +static ExtractedNode * +makeBinaryNode(ExtractedNodeType type, PathItem *path, bool indirect, + ExtractedNode *leftNode, ExtractedNode *rightNode) +{ + ExtractedNode *result = (ExtractedNode *) palloc(sizeof(ExtractedNode)); + + result->type = type; + result->path = path; + result->indirect = indirect; + result->args.items = (ExtractedNode **)palloc(2 * sizeof(ExtractedNode *)); + result->args.items[0] = leftNode; + result->args.items[1] = rightNode; + result->args.count = 2; + + return result; +} + +static ExtractedNode * +makeEmptyArrayNode(PathItem *path, JsQueryHint hint) +{ + ExtractedNode *result = (ExtractedNode *) palloc(sizeof(ExtractedNode)); + + result->type = eEmptyArray; + result->path = path; + result->indirect = false; + result->hint = hint; + + return result; +} + /* * Count number of children connected with nodes of same type. */ @@ -1368,12 +1369,13 @@ extractJsonPathQuery(JsonPath *jp, bool exists, bool arrayPathItems, int optimiz { ExtractedNode *root; JsonPathItem jsp; - bool lax = (jp->header & JSONPATH_LAX) != 0 && arrayPathItems; + bool lax = (jp->header & JSONPATH_LAX) != 0; + int flags = (lax ? efLax : 0) | (arrayPathItems ? efArrays : 0); jspInit(&jsp, jp); root = exists - ? extractJsonPathExists(&jsp, lax, NULL) - : recursiveExtractJsonPathExpr(&jsp, lax, false, NULL); + ? extractJsonPathExists(&jsp, flags, false, NULL) + : extractJsonPathExpr(&jsp, flags, false, false, NULL); return emitExtractedQuery(root, optimize, makeHandler, checkHandler, extra); } From e3d9a09b709661fc3b31830be6227edf0578110d Mon Sep 17 00:00:00 2001 From: Nikita Glukhov Date: Thu, 18 Apr 2019 19:44:41 +0300 Subject: [PATCH 17/21] Add tests for jsonpath queries --- expected/jsquery.out | 1092 ++++++++++++++++++++++++++++++++++++++---- sql/jsquery.sql | 163 +++++++ 2 files changed, 1150 insertions(+), 105 deletions(-) diff --git a/expected/jsquery.out b/expected/jsquery.out index 710bde7..60ed213 100644 --- a/expected/jsquery.out +++ b/expected/jsquery.out @@ -4864,8 +4864,919 @@ select v from test_jsquery where v @@ 'array = [2,3]'::jsquery order by v; {"array": [2, 3]} (1 row) +explain (costs off) select count(*) from test_jsquery where v @@ '$.review_helpful_votes > 0'::jsonpath; + QUERY PLAN +----------------------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on test_jsquery + Recheck Cond: (v @@ '($."review_helpful_votes" > 0)'::jsonpath) + -> Bitmap Index Scan on t_idx + Index Cond: (v @@ '($."review_helpful_votes" > 0)'::jsonpath) +(5 rows) + +explain (costs off) select count(*) from test_jsquery where v @? '$.review_helpful_votes ? (@ > 0)'::jsonpath; + QUERY PLAN +------------------------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on test_jsquery + Recheck Cond: (v @? '$."review_helpful_votes"?(@ > 0)'::jsonpath) + -> Bitmap Index Scan on t_idx + Index Cond: (v @? '$."review_helpful_votes"?(@ > 0)'::jsonpath) +(5 rows) + +select count(*) from test_jsquery where v @@ '$.review_helpful_votes > 0'::jsonpath; + count +------- + 654 +(1 row) + +select count(*) from test_jsquery where v @@ '$.review_helpful_votes > 19'::jsonpath; + count +------- + 13 +(1 row) + +select count(*) from test_jsquery where v @@ '$.review_helpful_votes < 19'::jsonpath; + count +------- + 985 +(1 row) + +select count(*) from test_jsquery where v @@ '$.review_helpful_votes >= 19'::jsonpath; + count +------- + 16 +(1 row) + +select count(*) from test_jsquery where v @@ '$.review_helpful_votes <= 19'::jsonpath; + count +------- + 988 +(1 row) + +select count(*) from test_jsquery where v @@ '$.review_helpful_votes == 19'::jsonpath; + count +------- + 3 +(1 row) + +select count(*) from test_jsquery where v @@ '$.review_helpful_votes > 16'::jsonpath AND + v @@ '$.review_helpful_votes < 20'::jsonpath; + count +------- + 8 +(1 row) + +select count(*) from test_jsquery where v @@ '$.review_helpful_votes > 16 && $.review_helpful_votes < 20'::jsonpath; + count +------- + 8 +(1 row) + +select count(*) from test_jsquery where v @? '$.review_helpful_votes ? (@ > 16 && @ < 20)'::jsonpath; + count +------- + 8 +(1 row) + +select count(*) from test_jsquery where v @@ '$.similar_product_ids[*] == "0440180295"'::jsonpath; + count +------- + 7 +(1 row) + +select count(*) from test_jsquery where v @? '$.similar_product_ids ? (@[*] == "0440180295")'::jsonpath; + count +------- + 7 +(1 row) + +select count(*) from test_jsquery where v @? '$.similar_product_ids[*] ? (@ == "0440180295")'::jsonpath; + count +------- + 7 +(1 row) + +select count(*) from test_jsquery where v @@ '$.similar_product_ids[*] == "0440180295" && $.product_sales_rank > 300000'::jsonpath; + count +------- + 4 +(1 row) + +--select count(*) from test_jsquery where v @@ 'similar_product_ids <@ ["B00000DG0U", "B00004SQXU", "B0001XAM18", "B00000FDBU", "B00000FDBV", "B000002H2H", "B000002H6C", "B000002H5E", "B000002H97", "B000002HMH"]'::jsquery; +select count(*) from test_jsquery where v @? 'strict $.similar_product_ids ? (@[*] == "B000002H2H" && @[*] == "B000002H6C")'::jsonpath; + count +------- + 3 +(1 row) + +select count(*) from test_jsquery where v @@ '$.customer_id == null'::jsonpath; + count +------- + 1 +(1 row) + +select count(*) from test_jsquery where v @@ '$.review_votes == true'::jsonpath; + count +------- + 1 +(1 row) + +select count(*) from test_jsquery where v @@ '$.product_group == false'::jsonpath; + count +------- + 1 +(1 row) + +select count(*) from test_jsquery where v @? '$.t'::jsonpath; + count +------- + 10 +(1 row) + +select count(*) from test_jsquery where v @@ '$.t.type() == "boolean"'::jsonpath; + count +------- + 2 +(1 row) + +select count(*) from test_jsquery where v @@ '$.t.type() == "string"'::jsonpath; + count +------- + 2 +(1 row) + +select count(*) from test_jsquery where v @@ '$.t.type() == "number"'::jsonpath; + count +------- + 2 +(1 row) + +select count(*) from test_jsquery where v @@ '$.t.type() == "array"'::jsonpath; + count +------- + 2 +(1 row) + +select count(*) from test_jsquery where v @@ '$.t.type() == "object"'::jsonpath; + count +------- + 2 +(1 row) + +select count(*) from test_jsquery where v @@ '$.type() == "boolean"'::jsonpath; + count +------- + 3 +(1 row) + +select count(*) from test_jsquery where v @@ '$.type() == "string"'::jsonpath; + count +------- + 4 +(1 row) + +select count(*) from test_jsquery where v @@ '$.type() == "number"'::jsonpath; + count +------- + 5 +(1 row) + +select count(*) from test_jsquery where v @@ '$.type() == "array"'::jsonpath; + count +------- + 3 +(1 row) + +select count(*) from test_jsquery where v @@ '$.type() == "object"'::jsonpath; + count +------- + 1018 +(1 row) + +--select count(*) from test_jsquery where v @@ 'similar_product_ids.#: ($ is numeric)'::jsonpath; +--select count(*) from test_jsquery where v @@ 'similar_product_ids.#: ($ is string)'::jsonpath; +--select count(*) from test_jsquery where v @@ 'NOT similar_product_ids.#: (NOT $ = "0440180295")'::jsonpath; +select count(*) from test_jsquery where v @@ 'strict $ > 2'::jsonpath; + count +------- + 3 +(1 row) + +select count(*) from test_jsquery where v @@ '$ == false'::jsonpath; + count +------- + 2 +(1 row) + +select count(*) from test_jsquery where v @? '$.t'::jsonpath; + count +------- + 10 +(1 row) + +select count(*) from test_jsquery where v @? '$'::jsonpath; + count +------- + 1036 +(1 row) + +select count(*) from test_jsquery where v @? '$.similar_product_ids[*]'::jsonpath; + count +------- + 950 +(1 row) + +select count(*) from test_jsquery where v @@ '$ ? (@.review_votes > 10) . review_rating < 7'::jsonpath; + count +------- + 79 +(1 row) + +select count(*) from test_jsquery where v @? '$.similar_product_ids ? (@[*] == "B0002W4TL2") '::jsonpath; + count +------- + 3 +(1 row) + +--explain (costs off) select v from test_jsquery where v @@ '$.array <@ [2,3]'::jsonpath order by v; +explain (costs off) select v from test_jsquery where v @? '$.array[*] ? (@ == 2 || @ == 3)'::jsonpath order by v; + QUERY PLAN +------------------------------------------------------------------------------ + Sort + Sort Key: v + -> Bitmap Heap Scan on test_jsquery + Recheck Cond: (v @? '$."array"[*]?(@ == 2 || @ == 3)'::jsonpath) + -> Bitmap Index Scan on t_idx + Index Cond: (v @? '$."array"[*]?(@ == 2 || @ == 3)'::jsonpath) +(6 rows) + +explain (costs off) select v from test_jsquery where v @@ '$.array[*] == 2 && $.array[*] == 3'::jsonpath order by v; + QUERY PLAN +--------------------------------------------------------------------------------------- + Sort + Sort Key: v + -> Bitmap Heap Scan on test_jsquery + Recheck Cond: (v @@ '($."array"[*] == 2 && $."array"[*] == 3)'::jsonpath) + -> Bitmap Index Scan on t_idx + Index Cond: (v @@ '($."array"[*] == 2 && $."array"[*] == 3)'::jsonpath) +(6 rows) + +explain (costs off) select v from test_jsquery where v @@ '$.array[0] == 2 && $.array[1] == 3 && $.array.size() == 2'::jsonpath order by v; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------ + Sort + Sort Key: v + -> Bitmap Heap Scan on test_jsquery + Recheck Cond: (v @@ '(($."array"[0] == 2 && $."array"[1] == 3) && $."array".size() == 2)'::jsonpath) + -> Bitmap Index Scan on t_idx + Index Cond: (v @@ '(($."array"[0] == 2 && $."array"[1] == 3) && $."array".size() == 2)'::jsonpath) +(6 rows) + +--select v from test_jsquery where v @@ 'array <@ [2,3]'::jsonpath order by v; +select v from test_jsquery where v @? '$.array[*] ? (@ == 2 || @ == 3)'::jsonpath order by v; + v +---------------------- + {"array": [2]} + {"array": [2, 3]} + {"array": [1, 2, 3]} + {"array": [2, 3, 4]} + {"array": [3, 4, 5]} +(5 rows) + +select v from test_jsquery where v @@ '$.array[*] == 2 && $.array[*] == 3'::jsonpath order by v; + v +---------------------- + {"array": [2, 3]} + {"array": [1, 2, 3]} + {"array": [2, 3, 4]} +(3 rows) + +select v from test_jsquery where v @@ '$.array[0] == 2 && $.array[1] == 3 && $.array.size() == 2'::jsonpath order by v; + v +------------------- + {"array": [2, 3]} +(1 row) + +drop index t_idx; +create index t_idx on test_jsquery using gin (v jsonb_path_value_ops); +set enable_seqscan = off; +explain (costs off) select count(*) from test_jsquery where v @@ 'review_helpful_votes > 0'::jsquery; + QUERY PLAN +------------------------------------------------------------------------ + Aggregate + -> Bitmap Heap Scan on test_jsquery + Recheck Cond: (v @@ '"review_helpful_votes" > 0'::jsquery) + -> Bitmap Index Scan on t_idx + Index Cond: (v @@ '"review_helpful_votes" > 0'::jsquery) +(5 rows) + +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 0'::jsquery; + count +------- + 654 +(1 row) + +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 19'::jsquery; + count +------- + 13 +(1 row) + +select count(*) from test_jsquery where v @@ 'review_helpful_votes < 19'::jsquery; + count +------- + 985 +(1 row) + +select count(*) from test_jsquery where v @@ 'review_helpful_votes >= 19'::jsquery; + count +------- + 16 +(1 row) + +select count(*) from test_jsquery where v @@ 'review_helpful_votes <= 19'::jsquery; + count +------- + 988 +(1 row) + +select count(*) from test_jsquery where v @@ 'review_helpful_votes = 19'::jsquery; + count +------- + 3 +(1 row) + +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16'::jsquery AND + v @@ 'review_helpful_votes < 20'::jsquery; + count +------- + 8 +(1 row) + +select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16 and review_helpful_votes < 20'::jsquery; + count +------- + 8 +(1 row) + +select count(*) from test_jsquery where v @@ 'review_helpful_votes ($ > 16 and $ < 20)'::jsquery; + count +------- + 8 +(1 row) + +select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"]'::jsquery; + count +------- + 7 +(1 row) + +select count(*) from test_jsquery where v @@ 'similar_product_ids(# = "0440180295") '::jsquery; + count +------- + 7 +(1 row) + +select count(*) from test_jsquery where v @@ 'similar_product_ids.#($ = "0440180295") '::jsquery; + count +------- + 7 +(1 row) + +select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"] and product_sales_rank > 300000'::jsquery; + count +------- + 4 +(1 row) + +select count(*) from test_jsquery where v @@ 'similar_product_ids <@ ["B00000DG0U", "B00004SQXU", "B0001XAM18", "B00000FDBU", "B00000FDBV", "B000002H2H", "B000002H6C", "B000002H5E", "B000002H97", "B000002HMH"]'::jsquery; + count +------- + 54 +(1 row) + +select count(*) from test_jsquery where v @@ 'similar_product_ids @> ["B000002H2H", "B000002H6C"]'::jsquery; + count +------- + 3 +(1 row) + +select count(*) from test_jsquery where v @@ 'customer_id = null'::jsquery; + count +------- + 1 +(1 row) + +select count(*) from test_jsquery where v @@ 'review_votes = true'::jsquery; + count +------- + 1 +(1 row) + +select count(*) from test_jsquery where v @@ 'product_group = false'::jsquery; + count +------- + 1 +(1 row) + +select count(*) from test_jsquery where v @@ 't = *'::jsquery; + count +------- + 10 +(1 row) + +select count(*) from test_jsquery where v @@ 't is boolean'::jsquery; + count +------- + 2 +(1 row) + +select count(*) from test_jsquery where v @@ 't is string'::jsquery; + count +------- + 2 +(1 row) + +select count(*) from test_jsquery where v @@ 't is numeric'::jsquery; + count +------- + 2 +(1 row) + +select count(*) from test_jsquery where v @@ 't is array'::jsquery; + count +------- + 2 +(1 row) + +select count(*) from test_jsquery where v @@ 't is object'::jsquery; + count +------- + 2 +(1 row) + +select count(*) from test_jsquery where v @@ '$ is boolean'::jsquery; + count +------- + 3 +(1 row) + +select count(*) from test_jsquery where v @@ '$ is string'::jsquery; + count +------- + 4 +(1 row) + +select count(*) from test_jsquery where v @@ '$ is numeric'::jsquery; + count +------- + 5 +(1 row) + +select count(*) from test_jsquery where v @@ '$ is array'::jsquery; + count +------- + 3 +(1 row) + +select count(*) from test_jsquery where v @@ '$ is object'::jsquery; + count +------- + 1018 +(1 row) + +select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is numeric'::jsquery; + count +------- + 51 +(1 row) + +select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is string'::jsquery; + count +------- + 1001 +(1 row) + +select count(*) from test_jsquery where v @@ 'NOT similar_product_ids.#: (NOT $ = "0440180295")'::jsquery; + count +------- + 42 +(1 row) + +select count(*) from test_jsquery where v @@ '$ > 2'::jsquery; + count +------- + 3 +(1 row) + +select count(*) from test_jsquery where v @@ '$ = false'::jsquery; + count +------- + 2 +(1 row) + +select count(*) from test_jsquery where v @@ 't'::jsquery; + count +------- + 10 +(1 row) + +select count(*) from test_jsquery where v @@ '$'::jsquery; + count +------- + 1036 +(1 row) + +select count(*) from test_jsquery where v @@ 'similar_product_ids.#'::jsquery; + count +------- + 950 +(1 row) + +select count(*) from test_jsquery where v @@ '$ . ? (review_votes > 10) . review_rating < 7'::jsquery; + count +------- + 79 +(1 row) + +select count(*) from test_jsquery where v @@ 'similar_product_ids . ? (# = "B0002W4TL2") . $'::jsquery; + count +------- + 3 +(1 row) + +explain (costs off) select v from test_jsquery where v @@ 'array <@ [2,3]'::jsquery order by v; + QUERY PLAN +--------------------------------------------------------------- + Sort + Sort Key: v + -> Bitmap Heap Scan on test_jsquery + Recheck Cond: (v @@ '"array" <@ [2, 3]'::jsquery) + -> Bitmap Index Scan on t_idx + Index Cond: (v @@ '"array" <@ [2, 3]'::jsquery) +(6 rows) + +explain (costs off) select v from test_jsquery where v @@ 'array && [2,3]'::jsquery order by v; + QUERY PLAN +--------------------------------------------------------------- + Sort + Sort Key: v + -> Bitmap Heap Scan on test_jsquery + Recheck Cond: (v @@ '"array" && [2, 3]'::jsquery) + -> Bitmap Index Scan on t_idx + Index Cond: (v @@ '"array" && [2, 3]'::jsquery) +(6 rows) + +explain (costs off) select v from test_jsquery where v @@ 'array @> [2,3]'::jsquery order by v; + QUERY PLAN +--------------------------------------------------------------- + Sort + Sort Key: v + -> Bitmap Heap Scan on test_jsquery + Recheck Cond: (v @@ '"array" @> [2, 3]'::jsquery) + -> Bitmap Index Scan on t_idx + Index Cond: (v @@ '"array" @> [2, 3]'::jsquery) +(6 rows) + +explain (costs off) select v from test_jsquery where v @@ 'array = [2,3]'::jsquery order by v; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: v + -> Bitmap Heap Scan on test_jsquery + Recheck Cond: (v @@ '"array" = [2, 3]'::jsquery) + -> Bitmap Index Scan on t_idx + Index Cond: (v @@ '"array" = [2, 3]'::jsquery) +(6 rows) + +select v from test_jsquery where v @@ 'array <@ [2,3]'::jsquery order by v; + v +------------------- + {"array": [2]} + {"array": [2, 3]} +(2 rows) + +select v from test_jsquery where v @@ 'array && [2,3]'::jsquery order by v; + v +---------------------- + {"array": [2]} + {"array": [2, 3]} + {"array": [1, 2, 3]} + {"array": [2, 3, 4]} + {"array": [3, 4, 5]} +(5 rows) + +select v from test_jsquery where v @@ 'array @> [2,3]'::jsquery order by v; + v +---------------------- + {"array": [2, 3]} + {"array": [1, 2, 3]} + {"array": [2, 3, 4]} +(3 rows) + +select v from test_jsquery where v @@ 'array = [2,3]'::jsquery order by v; + v +------------------- + {"array": [2, 3]} +(1 row) + +explain (costs off) select count(*) from test_jsquery where v @@ '$.review_helpful_votes > 0'::jsonpath; + QUERY PLAN +----------------------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on test_jsquery + Recheck Cond: (v @@ '($."review_helpful_votes" > 0)'::jsonpath) + -> Bitmap Index Scan on t_idx + Index Cond: (v @@ '($."review_helpful_votes" > 0)'::jsonpath) +(5 rows) + +explain (costs off) select count(*) from test_jsquery where v @? '$.review_helpful_votes ? (@ > 0)'::jsonpath; + QUERY PLAN +------------------------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on test_jsquery + Recheck Cond: (v @? '$."review_helpful_votes"?(@ > 0)'::jsonpath) + -> Bitmap Index Scan on t_idx + Index Cond: (v @? '$."review_helpful_votes"?(@ > 0)'::jsonpath) +(5 rows) + +select count(*) from test_jsquery where v @@ '$.review_helpful_votes > 0'::jsonpath; + count +------- + 654 +(1 row) + +select count(*) from test_jsquery where v @@ '$.review_helpful_votes > 19'::jsonpath; + count +------- + 13 +(1 row) + +select count(*) from test_jsquery where v @@ '$.review_helpful_votes < 19'::jsonpath; + count +------- + 985 +(1 row) + +select count(*) from test_jsquery where v @@ '$.review_helpful_votes >= 19'::jsonpath; + count +------- + 16 +(1 row) + +select count(*) from test_jsquery where v @@ '$.review_helpful_votes <= 19'::jsonpath; + count +------- + 988 +(1 row) + +select count(*) from test_jsquery where v @@ '$.review_helpful_votes == 19'::jsonpath; + count +------- + 3 +(1 row) + +select count(*) from test_jsquery where v @@ '$.review_helpful_votes > 16'::jsonpath AND + v @@ '$.review_helpful_votes < 20'::jsonpath; + count +------- + 8 +(1 row) + +select count(*) from test_jsquery where v @@ '$.review_helpful_votes > 16 && $.review_helpful_votes < 20'::jsonpath; + count +------- + 8 +(1 row) + +select count(*) from test_jsquery where v @? '$.review_helpful_votes ? (@ > 16 && @ < 20)'::jsonpath; + count +------- + 8 +(1 row) + +select count(*) from test_jsquery where v @@ '$.similar_product_ids[*] == "0440180295"'::jsonpath; + count +------- + 7 +(1 row) + +select count(*) from test_jsquery where v @? '$.similar_product_ids ? (@[*] == "0440180295")'::jsonpath; + count +------- + 7 +(1 row) + +select count(*) from test_jsquery where v @? '$.similar_product_ids[*] ? (@ == "0440180295")'::jsonpath; + count +------- + 7 +(1 row) + +select count(*) from test_jsquery where v @@ '$.similar_product_ids[*] == "0440180295" && $.product_sales_rank > 300000'::jsonpath; + count +------- + 4 +(1 row) + +--select count(*) from test_jsquery where v @@ 'similar_product_ids <@ ["B00000DG0U", "B00004SQXU", "B0001XAM18", "B00000FDBU", "B00000FDBV", "B000002H2H", "B000002H6C", "B000002H5E", "B000002H97", "B000002HMH"]'::jsquery; +select count(*) from test_jsquery where v @? 'strict $.similar_product_ids ? (@[*] == "B000002H2H" && @[*] == "B000002H6C")'::jsonpath; + count +------- + 3 +(1 row) + +select count(*) from test_jsquery where v @@ '$.customer_id == null'::jsonpath; + count +------- + 1 +(1 row) + +select count(*) from test_jsquery where v @@ '$.review_votes == true'::jsonpath; + count +------- + 1 +(1 row) + +select count(*) from test_jsquery where v @@ '$.product_group == false'::jsonpath; + count +------- + 1 +(1 row) + +select count(*) from test_jsquery where v @? '$.t'::jsonpath; + count +------- + 10 +(1 row) + +select count(*) from test_jsquery where v @@ '$.t.type() == "boolean"'::jsonpath; + count +------- + 2 +(1 row) + +select count(*) from test_jsquery where v @@ '$.t.type() == "string"'::jsonpath; + count +------- + 2 +(1 row) + +select count(*) from test_jsquery where v @@ '$.t.type() == "number"'::jsonpath; + count +------- + 2 +(1 row) + +select count(*) from test_jsquery where v @@ '$.t.type() == "array"'::jsonpath; + count +------- + 2 +(1 row) + +select count(*) from test_jsquery where v @@ '$.t.type() == "object"'::jsonpath; + count +------- + 2 +(1 row) + +select count(*) from test_jsquery where v @@ '$.type() == "boolean"'::jsonpath; + count +------- + 3 +(1 row) + +select count(*) from test_jsquery where v @@ '$.type() == "string"'::jsonpath; + count +------- + 4 +(1 row) + +select count(*) from test_jsquery where v @@ '$.type() == "number"'::jsonpath; + count +------- + 5 +(1 row) + +select count(*) from test_jsquery where v @@ '$.type() == "array"'::jsonpath; + count +------- + 3 +(1 row) + +select count(*) from test_jsquery where v @@ '$.type() == "object"'::jsonpath; + count +------- + 1018 +(1 row) + +--select count(*) from test_jsquery where v @@ 'similar_product_ids.#: ($ is numeric)'::jsonpath; +--select count(*) from test_jsquery where v @@ 'similar_product_ids.#: ($ is string)'::jsonpath; +--select count(*) from test_jsquery where v @@ 'NOT similar_product_ids.#: (NOT $ = "0440180295")'::jsonpath; +select count(*) from test_jsquery where v @@ 'strict $ > 2'::jsonpath; + count +------- + 3 +(1 row) + +select count(*) from test_jsquery where v @@ '$ == false'::jsonpath; + count +------- + 2 +(1 row) + +select count(*) from test_jsquery where v @? '$.t'::jsonpath; + count +------- + 10 +(1 row) + +select count(*) from test_jsquery where v @? '$'::jsonpath; + count +------- + 1036 +(1 row) + +select count(*) from test_jsquery where v @? '$.similar_product_ids[*]'::jsonpath; + count +------- + 950 +(1 row) + +select count(*) from test_jsquery where v @@ '$ ? (@.review_votes > 10) . review_rating < 7'::jsonpath; + count +------- + 79 +(1 row) + +select count(*) from test_jsquery where v @? '$.similar_product_ids ? (@[*] == "B0002W4TL2") '::jsonpath; + count +------- + 3 +(1 row) + +--explain (costs off) select v from test_jsquery where v @@ '$.array <@ [2,3]'::jsonpath order by v; +explain (costs off) select v from test_jsquery where v @? '$.array[*] ? (@ == 2 || @ == 3)'::jsonpath order by v; + QUERY PLAN +------------------------------------------------------------------------------ + Sort + Sort Key: v + -> Bitmap Heap Scan on test_jsquery + Recheck Cond: (v @? '$."array"[*]?(@ == 2 || @ == 3)'::jsonpath) + -> Bitmap Index Scan on t_idx + Index Cond: (v @? '$."array"[*]?(@ == 2 || @ == 3)'::jsonpath) +(6 rows) + +explain (costs off) select v from test_jsquery where v @@ '$.array[*] == 2 && $.array[*] == 3'::jsonpath order by v; + QUERY PLAN +--------------------------------------------------------------------------------------- + Sort + Sort Key: v + -> Bitmap Heap Scan on test_jsquery + Recheck Cond: (v @@ '($."array"[*] == 2 && $."array"[*] == 3)'::jsonpath) + -> Bitmap Index Scan on t_idx + Index Cond: (v @@ '($."array"[*] == 2 && $."array"[*] == 3)'::jsonpath) +(6 rows) + +explain (costs off) select v from test_jsquery where v @@ '$.array[0] == 2 && $.array[1] == 3 && $.array.size() == 2'::jsonpath order by v; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------ + Sort + Sort Key: v + -> Bitmap Heap Scan on test_jsquery + Recheck Cond: (v @@ '(($."array"[0] == 2 && $."array"[1] == 3) && $."array".size() == 2)'::jsonpath) + -> Bitmap Index Scan on t_idx + Index Cond: (v @@ '(($."array"[0] == 2 && $."array"[1] == 3) && $."array".size() == 2)'::jsonpath) +(6 rows) + +--select v from test_jsquery where v @@ 'array <@ [2,3]'::jsonpath order by v; +select v from test_jsquery where v @? '$.array[*] ? (@ == 2 || @ == 3)'::jsonpath order by v; + v +---------------------- + {"array": [2]} + {"array": [2, 3]} + {"array": [1, 2, 3]} + {"array": [2, 3, 4]} + {"array": [3, 4, 5]} +(5 rows) + +select v from test_jsquery where v @@ '$.array[*] == 2 && $.array[*] == 3'::jsonpath order by v; + v +---------------------- + {"array": [2, 3]} + {"array": [1, 2, 3]} + {"array": [2, 3, 4]} +(3 rows) + +select v from test_jsquery where v @@ '$.array[0] == 2 && $.array[1] == 3 && $.array.size() == 2'::jsonpath order by v; + v +------------------- + {"array": [2, 3]} +(1 row) + drop index t_idx; -create index t_idx on test_jsquery using gin (v jsonb_path_value_ops); +create index t_idx on test_jsquery using gin (v jsonb_laxpath_value_ops); set enable_seqscan = off; explain (costs off) select count(*) from test_jsquery where v @@ 'review_helpful_votes > 0'::jsquery; QUERY PLAN @@ -5187,306 +6098,277 @@ select v from test_jsquery where v @@ 'array = [2,3]'::jsquery order by v; {"array": [2, 3]} (1 row) -drop index t_idx; -create index t_idx on test_jsquery using gin (v jsonb_laxpath_value_ops); -set enable_seqscan = off; -explain (costs off) select count(*) from test_jsquery where v @@ 'review_helpful_votes > 0'::jsquery; - QUERY PLAN ------------------------------------------------------------------------- +explain (costs off) select count(*) from test_jsquery where v @@ '$.review_helpful_votes > 0'::jsonpath; + QUERY PLAN +----------------------------------------------------------------------------- Aggregate -> Bitmap Heap Scan on test_jsquery - Recheck Cond: (v @@ '"review_helpful_votes" > 0'::jsquery) + Recheck Cond: (v @@ '($."review_helpful_votes" > 0)'::jsonpath) -> Bitmap Index Scan on t_idx - Index Cond: (v @@ '"review_helpful_votes" > 0'::jsquery) + Index Cond: (v @@ '($."review_helpful_votes" > 0)'::jsonpath) (5 rows) -select count(*) from test_jsquery where v @@ 'review_helpful_votes > 0'::jsquery; +explain (costs off) select count(*) from test_jsquery where v @? '$.review_helpful_votes ? (@ > 0)'::jsonpath; + QUERY PLAN +------------------------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on test_jsquery + Recheck Cond: (v @? '$."review_helpful_votes"?(@ > 0)'::jsonpath) + -> Bitmap Index Scan on t_idx + Index Cond: (v @? '$."review_helpful_votes"?(@ > 0)'::jsonpath) +(5 rows) + +select count(*) from test_jsquery where v @@ '$.review_helpful_votes > 0'::jsonpath; count ------- 654 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes > 19'::jsquery; +select count(*) from test_jsquery where v @@ '$.review_helpful_votes > 19'::jsonpath; count ------- 13 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes < 19'::jsquery; +select count(*) from test_jsquery where v @@ '$.review_helpful_votes < 19'::jsonpath; count ------- 985 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes >= 19'::jsquery; +select count(*) from test_jsquery where v @@ '$.review_helpful_votes >= 19'::jsonpath; count ------- 16 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes <= 19'::jsquery; +select count(*) from test_jsquery where v @@ '$.review_helpful_votes <= 19'::jsonpath; count ------- 988 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes = 19'::jsquery; +select count(*) from test_jsquery where v @@ '$.review_helpful_votes == 19'::jsonpath; count ------- 3 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16'::jsquery AND - v @@ 'review_helpful_votes < 20'::jsquery; +select count(*) from test_jsquery where v @@ '$.review_helpful_votes > 16'::jsonpath AND + v @@ '$.review_helpful_votes < 20'::jsonpath; count ------- 8 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes > 16 and review_helpful_votes < 20'::jsquery; +select count(*) from test_jsquery where v @@ '$.review_helpful_votes > 16 && $.review_helpful_votes < 20'::jsonpath; count ------- 8 (1 row) -select count(*) from test_jsquery where v @@ 'review_helpful_votes ($ > 16 and $ < 20)'::jsquery; +select count(*) from test_jsquery where v @? '$.review_helpful_votes ? (@ > 16 && @ < 20)'::jsonpath; count ------- 8 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"]'::jsquery; +select count(*) from test_jsquery where v @@ '$.similar_product_ids[*] == "0440180295"'::jsonpath; count ------- 7 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids(# = "0440180295") '::jsquery; +select count(*) from test_jsquery where v @? '$.similar_product_ids ? (@[*] == "0440180295")'::jsonpath; count ------- 7 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids.#($ = "0440180295") '::jsquery; +select count(*) from test_jsquery where v @? '$.similar_product_ids[*] ? (@ == "0440180295")'::jsonpath; count ------- 7 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids && ["0440180295"] and product_sales_rank > 300000'::jsquery; +select count(*) from test_jsquery where v @@ '$.similar_product_ids[*] == "0440180295" && $.product_sales_rank > 300000'::jsonpath; count ------- 4 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids <@ ["B00000DG0U", "B00004SQXU", "B0001XAM18", "B00000FDBU", "B00000FDBV", "B000002H2H", "B000002H6C", "B000002H5E", "B000002H97", "B000002HMH"]'::jsquery; - count -------- - 54 -(1 row) - -select count(*) from test_jsquery where v @@ 'similar_product_ids @> ["B000002H2H", "B000002H6C"]'::jsquery; +--select count(*) from test_jsquery where v @@ 'similar_product_ids <@ ["B00000DG0U", "B00004SQXU", "B0001XAM18", "B00000FDBU", "B00000FDBV", "B000002H2H", "B000002H6C", "B000002H5E", "B000002H97", "B000002HMH"]'::jsquery; +select count(*) from test_jsquery where v @? 'strict $.similar_product_ids ? (@[*] == "B000002H2H" && @[*] == "B000002H6C")'::jsonpath; count ------- 3 (1 row) -select count(*) from test_jsquery where v @@ 'customer_id = null'::jsquery; +select count(*) from test_jsquery where v @@ '$.customer_id == null'::jsonpath; count ------- 1 (1 row) -select count(*) from test_jsquery where v @@ 'review_votes = true'::jsquery; +select count(*) from test_jsquery where v @@ '$.review_votes == true'::jsonpath; count ------- 1 (1 row) -select count(*) from test_jsquery where v @@ 'product_group = false'::jsquery; +select count(*) from test_jsquery where v @@ '$.product_group == false'::jsonpath; count ------- 1 (1 row) -select count(*) from test_jsquery where v @@ 't = *'::jsquery; +select count(*) from test_jsquery where v @? '$.t'::jsonpath; count ------- 10 (1 row) -select count(*) from test_jsquery where v @@ 't is boolean'::jsquery; +select count(*) from test_jsquery where v @@ '$.t.type() == "boolean"'::jsonpath; count ------- 2 (1 row) -select count(*) from test_jsquery where v @@ 't is string'::jsquery; +select count(*) from test_jsquery where v @@ '$.t.type() == "string"'::jsonpath; count ------- 2 (1 row) -select count(*) from test_jsquery where v @@ 't is numeric'::jsquery; +select count(*) from test_jsquery where v @@ '$.t.type() == "number"'::jsonpath; count ------- 2 (1 row) -select count(*) from test_jsquery where v @@ 't is array'::jsquery; +select count(*) from test_jsquery where v @@ '$.t.type() == "array"'::jsonpath; count ------- 2 (1 row) -select count(*) from test_jsquery where v @@ 't is object'::jsquery; +select count(*) from test_jsquery where v @@ '$.t.type() == "object"'::jsonpath; count ------- 2 (1 row) -select count(*) from test_jsquery where v @@ '$ is boolean'::jsquery; +select count(*) from test_jsquery where v @@ '$.type() == "boolean"'::jsonpath; count ------- 3 (1 row) -select count(*) from test_jsquery where v @@ '$ is string'::jsquery; +select count(*) from test_jsquery where v @@ '$.type() == "string"'::jsonpath; count ------- 4 (1 row) -select count(*) from test_jsquery where v @@ '$ is numeric'::jsquery; +select count(*) from test_jsquery where v @@ '$.type() == "number"'::jsonpath; count ------- 5 (1 row) -select count(*) from test_jsquery where v @@ '$ is array'::jsquery; +select count(*) from test_jsquery where v @@ '$.type() == "array"'::jsonpath; count ------- 3 (1 row) -select count(*) from test_jsquery where v @@ '$ is object'::jsquery; +select count(*) from test_jsquery where v @@ '$.type() == "object"'::jsonpath; count ------- 1018 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is numeric'::jsquery; - count -------- - 51 -(1 row) - -select count(*) from test_jsquery where v @@ 'similar_product_ids.#: is string'::jsquery; - count -------- - 1001 -(1 row) - -select count(*) from test_jsquery where v @@ 'NOT similar_product_ids.#: (NOT $ = "0440180295")'::jsquery; - count -------- - 42 -(1 row) - -select count(*) from test_jsquery where v @@ '$ > 2'::jsquery; +--select count(*) from test_jsquery where v @@ 'similar_product_ids.#: ($ is numeric)'::jsonpath; +--select count(*) from test_jsquery where v @@ 'similar_product_ids.#: ($ is string)'::jsonpath; +--select count(*) from test_jsquery where v @@ 'NOT similar_product_ids.#: (NOT $ = "0440180295")'::jsonpath; +select count(*) from test_jsquery where v @@ 'strict $ > 2'::jsonpath; count ------- 3 (1 row) -select count(*) from test_jsquery where v @@ '$ = false'::jsquery; +select count(*) from test_jsquery where v @@ '$ == false'::jsonpath; count ------- 2 (1 row) -select count(*) from test_jsquery where v @@ 't'::jsquery; +select count(*) from test_jsquery where v @? '$.t'::jsonpath; count ------- 10 (1 row) -select count(*) from test_jsquery where v @@ '$'::jsquery; +select count(*) from test_jsquery where v @? '$'::jsonpath; count ------- 1036 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids.#'::jsquery; +select count(*) from test_jsquery where v @? '$.similar_product_ids[*]'::jsonpath; count ------- 950 (1 row) -select count(*) from test_jsquery where v @@ '$ . ? (review_votes > 10) . review_rating < 7'::jsquery; +select count(*) from test_jsquery where v @@ '$ ? (@.review_votes > 10) . review_rating < 7'::jsonpath; count ------- 79 (1 row) -select count(*) from test_jsquery where v @@ 'similar_product_ids . ? (# = "B0002W4TL2") . $'::jsquery; +select count(*) from test_jsquery where v @? '$.similar_product_ids ? (@[*] == "B0002W4TL2") '::jsonpath; count ------- 3 (1 row) -explain (costs off) select v from test_jsquery where v @@ 'array <@ [2,3]'::jsquery order by v; - QUERY PLAN ---------------------------------------------------------------- - Sort - Sort Key: v - -> Bitmap Heap Scan on test_jsquery - Recheck Cond: (v @@ '"array" <@ [2, 3]'::jsquery) - -> Bitmap Index Scan on t_idx - Index Cond: (v @@ '"array" <@ [2, 3]'::jsquery) -(6 rows) - -explain (costs off) select v from test_jsquery where v @@ 'array && [2,3]'::jsquery order by v; - QUERY PLAN ---------------------------------------------------------------- +--explain (costs off) select v from test_jsquery where v @@ '$.array <@ [2,3]'::jsonpath order by v; +explain (costs off) select v from test_jsquery where v @? '$.array[*] ? (@ == 2 || @ == 3)'::jsonpath order by v; + QUERY PLAN +------------------------------------------------------------------------------ Sort Sort Key: v -> Bitmap Heap Scan on test_jsquery - Recheck Cond: (v @@ '"array" && [2, 3]'::jsquery) + Recheck Cond: (v @? '$."array"[*]?(@ == 2 || @ == 3)'::jsonpath) -> Bitmap Index Scan on t_idx - Index Cond: (v @@ '"array" && [2, 3]'::jsquery) + Index Cond: (v @? '$."array"[*]?(@ == 2 || @ == 3)'::jsonpath) (6 rows) -explain (costs off) select v from test_jsquery where v @@ 'array @> [2,3]'::jsquery order by v; - QUERY PLAN ---------------------------------------------------------------- +explain (costs off) select v from test_jsquery where v @@ '$.array[*] == 2 && $.array[*] == 3'::jsonpath order by v; + QUERY PLAN +--------------------------------------------------------------------------------------- Sort Sort Key: v -> Bitmap Heap Scan on test_jsquery - Recheck Cond: (v @@ '"array" @> [2, 3]'::jsquery) + Recheck Cond: (v @@ '($."array"[*] == 2 && $."array"[*] == 3)'::jsonpath) -> Bitmap Index Scan on t_idx - Index Cond: (v @@ '"array" @> [2, 3]'::jsquery) + Index Cond: (v @@ '($."array"[*] == 2 && $."array"[*] == 3)'::jsonpath) (6 rows) -explain (costs off) select v from test_jsquery where v @@ 'array = [2,3]'::jsquery order by v; - QUERY PLAN --------------------------------------------------------------- +explain (costs off) select v from test_jsquery where v @@ '$.array[0] == 2 && $.array[1] == 3 && $.array.size() == 2'::jsonpath order by v; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------ Sort Sort Key: v -> Bitmap Heap Scan on test_jsquery - Recheck Cond: (v @@ '"array" = [2, 3]'::jsquery) + Recheck Cond: (v @@ '(($."array"[0] == 2 && $."array"[1] == 3) && $."array".size() == 2)'::jsonpath) -> Bitmap Index Scan on t_idx - Index Cond: (v @@ '"array" = [2, 3]'::jsquery) + Index Cond: (v @@ '(($."array"[0] == 2 && $."array"[1] == 3) && $."array".size() == 2)'::jsonpath) (6 rows) -select v from test_jsquery where v @@ 'array <@ [2,3]'::jsquery order by v; - v -------------------- - {"array": [2]} - {"array": [2, 3]} -(2 rows) - -select v from test_jsquery where v @@ 'array && [2,3]'::jsquery order by v; +--select v from test_jsquery where v @@ 'array <@ [2,3]'::jsonpath order by v; +select v from test_jsquery where v @? '$.array[*] ? (@ == 2 || @ == 3)'::jsonpath order by v; v ---------------------- {"array": [2]} @@ -5496,7 +6378,7 @@ select v from test_jsquery where v @@ 'array && [2,3]'::jsquery order by v; {"array": [3, 4, 5]} (5 rows) -select v from test_jsquery where v @@ 'array @> [2,3]'::jsquery order by v; +select v from test_jsquery where v @@ '$.array[*] == 2 && $.array[*] == 3'::jsonpath order by v; v ---------------------- {"array": [2, 3]} @@ -5504,7 +6386,7 @@ select v from test_jsquery where v @@ 'array @> [2,3]'::jsquery order by v; {"array": [2, 3, 4]} (3 rows) -select v from test_jsquery where v @@ 'array = [2,3]'::jsquery order by v; +select v from test_jsquery where v @@ '$.array[0] == 2 && $.array[1] == 3 && $.array.size() == 2'::jsonpath order by v; v ------------------- {"array": [2, 3]} diff --git a/sql/jsquery.sql b/sql/jsquery.sql index a752922..a2c44e6 100644 --- a/sql/jsquery.sql +++ b/sql/jsquery.sql @@ -709,6 +709,60 @@ select v from test_jsquery where v @@ 'array && [2,3]'::jsquery order by v; select v from test_jsquery where v @@ 'array @> [2,3]'::jsquery order by v; select v from test_jsquery where v @@ 'array = [2,3]'::jsquery order by v; +explain (costs off) select count(*) from test_jsquery where v @@ '$.review_helpful_votes > 0'::jsonpath; +explain (costs off) select count(*) from test_jsquery where v @? '$.review_helpful_votes ? (@ > 0)'::jsonpath; + +select count(*) from test_jsquery where v @@ '$.review_helpful_votes > 0'::jsonpath; +select count(*) from test_jsquery where v @@ '$.review_helpful_votes > 19'::jsonpath; +select count(*) from test_jsquery where v @@ '$.review_helpful_votes < 19'::jsonpath; +select count(*) from test_jsquery where v @@ '$.review_helpful_votes >= 19'::jsonpath; +select count(*) from test_jsquery where v @@ '$.review_helpful_votes <= 19'::jsonpath; +select count(*) from test_jsquery where v @@ '$.review_helpful_votes == 19'::jsonpath; +select count(*) from test_jsquery where v @@ '$.review_helpful_votes > 16'::jsonpath AND + v @@ '$.review_helpful_votes < 20'::jsonpath; +select count(*) from test_jsquery where v @@ '$.review_helpful_votes > 16 && $.review_helpful_votes < 20'::jsonpath; +select count(*) from test_jsquery where v @? '$.review_helpful_votes ? (@ > 16 && @ < 20)'::jsonpath; +select count(*) from test_jsquery where v @@ '$.similar_product_ids[*] == "0440180295"'::jsonpath; +select count(*) from test_jsquery where v @? '$.similar_product_ids ? (@[*] == "0440180295")'::jsonpath; +select count(*) from test_jsquery where v @? '$.similar_product_ids[*] ? (@ == "0440180295")'::jsonpath; +select count(*) from test_jsquery where v @@ '$.similar_product_ids[*] == "0440180295" && $.product_sales_rank > 300000'::jsonpath; +--select count(*) from test_jsquery where v @@ 'similar_product_ids <@ ["B00000DG0U", "B00004SQXU", "B0001XAM18", "B00000FDBU", "B00000FDBV", "B000002H2H", "B000002H6C", "B000002H5E", "B000002H97", "B000002HMH"]'::jsquery; +select count(*) from test_jsquery where v @? 'strict $.similar_product_ids ? (@[*] == "B000002H2H" && @[*] == "B000002H6C")'::jsonpath; +select count(*) from test_jsquery where v @@ '$.customer_id == null'::jsonpath; +select count(*) from test_jsquery where v @@ '$.review_votes == true'::jsonpath; +select count(*) from test_jsquery where v @@ '$.product_group == false'::jsonpath; +select count(*) from test_jsquery where v @? '$.t'::jsonpath; +select count(*) from test_jsquery where v @@ '$.t.type() == "boolean"'::jsonpath; +select count(*) from test_jsquery where v @@ '$.t.type() == "string"'::jsonpath; +select count(*) from test_jsquery where v @@ '$.t.type() == "number"'::jsonpath; +select count(*) from test_jsquery where v @@ '$.t.type() == "array"'::jsonpath; +select count(*) from test_jsquery where v @@ '$.t.type() == "object"'::jsonpath; +select count(*) from test_jsquery where v @@ '$.type() == "boolean"'::jsonpath; +select count(*) from test_jsquery where v @@ '$.type() == "string"'::jsonpath; +select count(*) from test_jsquery where v @@ '$.type() == "number"'::jsonpath; +select count(*) from test_jsquery where v @@ '$.type() == "array"'::jsonpath; +select count(*) from test_jsquery where v @@ '$.type() == "object"'::jsonpath; +--select count(*) from test_jsquery where v @@ 'similar_product_ids.#: ($ is numeric)'::jsonpath; +--select count(*) from test_jsquery where v @@ 'similar_product_ids.#: ($ is string)'::jsonpath; +--select count(*) from test_jsquery where v @@ 'NOT similar_product_ids.#: (NOT $ = "0440180295")'::jsonpath; +select count(*) from test_jsquery where v @@ 'strict $ > 2'::jsonpath; +select count(*) from test_jsquery where v @@ '$ == false'::jsonpath; +select count(*) from test_jsquery where v @? '$.t'::jsonpath; +select count(*) from test_jsquery where v @? '$'::jsonpath; +select count(*) from test_jsquery where v @? '$.similar_product_ids[*]'::jsonpath; +select count(*) from test_jsquery where v @@ '$ ? (@.review_votes > 10) . review_rating < 7'::jsonpath; +select count(*) from test_jsquery where v @? '$.similar_product_ids ? (@[*] == "B0002W4TL2") '::jsonpath; + +--explain (costs off) select v from test_jsquery where v @@ '$.array <@ [2,3]'::jsonpath order by v; +explain (costs off) select v from test_jsquery where v @? '$.array[*] ? (@ == 2 || @ == 3)'::jsonpath order by v; +explain (costs off) select v from test_jsquery where v @@ '$.array[*] == 2 && $.array[*] == 3'::jsonpath order by v; +explain (costs off) select v from test_jsquery where v @@ '$.array[0] == 2 && $.array[1] == 3 && $.array.size() == 2'::jsonpath order by v; + +--select v from test_jsquery where v @@ 'array <@ [2,3]'::jsonpath order by v; +select v from test_jsquery where v @? '$.array[*] ? (@ == 2 || @ == 3)'::jsonpath order by v; +select v from test_jsquery where v @@ '$.array[*] == 2 && $.array[*] == 3'::jsonpath order by v; +select v from test_jsquery where v @@ '$.array[0] == 2 && $.array[1] == 3 && $.array.size() == 2'::jsonpath order by v; + drop index t_idx; create index t_idx on test_jsquery using gin (v jsonb_path_value_ops); @@ -767,6 +821,60 @@ select v from test_jsquery where v @@ 'array && [2,3]'::jsquery order by v; select v from test_jsquery where v @@ 'array @> [2,3]'::jsquery order by v; select v from test_jsquery where v @@ 'array = [2,3]'::jsquery order by v; +explain (costs off) select count(*) from test_jsquery where v @@ '$.review_helpful_votes > 0'::jsonpath; +explain (costs off) select count(*) from test_jsquery where v @? '$.review_helpful_votes ? (@ > 0)'::jsonpath; + +select count(*) from test_jsquery where v @@ '$.review_helpful_votes > 0'::jsonpath; +select count(*) from test_jsquery where v @@ '$.review_helpful_votes > 19'::jsonpath; +select count(*) from test_jsquery where v @@ '$.review_helpful_votes < 19'::jsonpath; +select count(*) from test_jsquery where v @@ '$.review_helpful_votes >= 19'::jsonpath; +select count(*) from test_jsquery where v @@ '$.review_helpful_votes <= 19'::jsonpath; +select count(*) from test_jsquery where v @@ '$.review_helpful_votes == 19'::jsonpath; +select count(*) from test_jsquery where v @@ '$.review_helpful_votes > 16'::jsonpath AND + v @@ '$.review_helpful_votes < 20'::jsonpath; +select count(*) from test_jsquery where v @@ '$.review_helpful_votes > 16 && $.review_helpful_votes < 20'::jsonpath; +select count(*) from test_jsquery where v @? '$.review_helpful_votes ? (@ > 16 && @ < 20)'::jsonpath; +select count(*) from test_jsquery where v @@ '$.similar_product_ids[*] == "0440180295"'::jsonpath; +select count(*) from test_jsquery where v @? '$.similar_product_ids ? (@[*] == "0440180295")'::jsonpath; +select count(*) from test_jsquery where v @? '$.similar_product_ids[*] ? (@ == "0440180295")'::jsonpath; +select count(*) from test_jsquery where v @@ '$.similar_product_ids[*] == "0440180295" && $.product_sales_rank > 300000'::jsonpath; +--select count(*) from test_jsquery where v @@ 'similar_product_ids <@ ["B00000DG0U", "B00004SQXU", "B0001XAM18", "B00000FDBU", "B00000FDBV", "B000002H2H", "B000002H6C", "B000002H5E", "B000002H97", "B000002HMH"]'::jsquery; +select count(*) from test_jsquery where v @? 'strict $.similar_product_ids ? (@[*] == "B000002H2H" && @[*] == "B000002H6C")'::jsonpath; +select count(*) from test_jsquery where v @@ '$.customer_id == null'::jsonpath; +select count(*) from test_jsquery where v @@ '$.review_votes == true'::jsonpath; +select count(*) from test_jsquery where v @@ '$.product_group == false'::jsonpath; +select count(*) from test_jsquery where v @? '$.t'::jsonpath; +select count(*) from test_jsquery where v @@ '$.t.type() == "boolean"'::jsonpath; +select count(*) from test_jsquery where v @@ '$.t.type() == "string"'::jsonpath; +select count(*) from test_jsquery where v @@ '$.t.type() == "number"'::jsonpath; +select count(*) from test_jsquery where v @@ '$.t.type() == "array"'::jsonpath; +select count(*) from test_jsquery where v @@ '$.t.type() == "object"'::jsonpath; +select count(*) from test_jsquery where v @@ '$.type() == "boolean"'::jsonpath; +select count(*) from test_jsquery where v @@ '$.type() == "string"'::jsonpath; +select count(*) from test_jsquery where v @@ '$.type() == "number"'::jsonpath; +select count(*) from test_jsquery where v @@ '$.type() == "array"'::jsonpath; +select count(*) from test_jsquery where v @@ '$.type() == "object"'::jsonpath; +--select count(*) from test_jsquery where v @@ 'similar_product_ids.#: ($ is numeric)'::jsonpath; +--select count(*) from test_jsquery where v @@ 'similar_product_ids.#: ($ is string)'::jsonpath; +--select count(*) from test_jsquery where v @@ 'NOT similar_product_ids.#: (NOT $ = "0440180295")'::jsonpath; +select count(*) from test_jsquery where v @@ 'strict $ > 2'::jsonpath; +select count(*) from test_jsquery where v @@ '$ == false'::jsonpath; +select count(*) from test_jsquery where v @? '$.t'::jsonpath; +select count(*) from test_jsquery where v @? '$'::jsonpath; +select count(*) from test_jsquery where v @? '$.similar_product_ids[*]'::jsonpath; +select count(*) from test_jsquery where v @@ '$ ? (@.review_votes > 10) . review_rating < 7'::jsonpath; +select count(*) from test_jsquery where v @? '$.similar_product_ids ? (@[*] == "B0002W4TL2") '::jsonpath; + +--explain (costs off) select v from test_jsquery where v @@ '$.array <@ [2,3]'::jsonpath order by v; +explain (costs off) select v from test_jsquery where v @? '$.array[*] ? (@ == 2 || @ == 3)'::jsonpath order by v; +explain (costs off) select v from test_jsquery where v @@ '$.array[*] == 2 && $.array[*] == 3'::jsonpath order by v; +explain (costs off) select v from test_jsquery where v @@ '$.array[0] == 2 && $.array[1] == 3 && $.array.size() == 2'::jsonpath order by v; + +--select v from test_jsquery where v @@ 'array <@ [2,3]'::jsonpath order by v; +select v from test_jsquery where v @? '$.array[*] ? (@ == 2 || @ == 3)'::jsonpath order by v; +select v from test_jsquery where v @@ '$.array[*] == 2 && $.array[*] == 3'::jsonpath order by v; +select v from test_jsquery where v @@ '$.array[0] == 2 && $.array[1] == 3 && $.array.size() == 2'::jsonpath order by v; + drop index t_idx; create index t_idx on test_jsquery using gin (v jsonb_laxpath_value_ops); @@ -825,4 +933,59 @@ select v from test_jsquery where v @@ 'array && [2,3]'::jsquery order by v; select v from test_jsquery where v @@ 'array @> [2,3]'::jsquery order by v; select v from test_jsquery where v @@ 'array = [2,3]'::jsquery order by v; + +explain (costs off) select count(*) from test_jsquery where v @@ '$.review_helpful_votes > 0'::jsonpath; +explain (costs off) select count(*) from test_jsquery where v @? '$.review_helpful_votes ? (@ > 0)'::jsonpath; + +select count(*) from test_jsquery where v @@ '$.review_helpful_votes > 0'::jsonpath; +select count(*) from test_jsquery where v @@ '$.review_helpful_votes > 19'::jsonpath; +select count(*) from test_jsquery where v @@ '$.review_helpful_votes < 19'::jsonpath; +select count(*) from test_jsquery where v @@ '$.review_helpful_votes >= 19'::jsonpath; +select count(*) from test_jsquery where v @@ '$.review_helpful_votes <= 19'::jsonpath; +select count(*) from test_jsquery where v @@ '$.review_helpful_votes == 19'::jsonpath; +select count(*) from test_jsquery where v @@ '$.review_helpful_votes > 16'::jsonpath AND + v @@ '$.review_helpful_votes < 20'::jsonpath; +select count(*) from test_jsquery where v @@ '$.review_helpful_votes > 16 && $.review_helpful_votes < 20'::jsonpath; +select count(*) from test_jsquery where v @? '$.review_helpful_votes ? (@ > 16 && @ < 20)'::jsonpath; +select count(*) from test_jsquery where v @@ '$.similar_product_ids[*] == "0440180295"'::jsonpath; +select count(*) from test_jsquery where v @? '$.similar_product_ids ? (@[*] == "0440180295")'::jsonpath; +select count(*) from test_jsquery where v @? '$.similar_product_ids[*] ? (@ == "0440180295")'::jsonpath; +select count(*) from test_jsquery where v @@ '$.similar_product_ids[*] == "0440180295" && $.product_sales_rank > 300000'::jsonpath; +--select count(*) from test_jsquery where v @@ 'similar_product_ids <@ ["B00000DG0U", "B00004SQXU", "B0001XAM18", "B00000FDBU", "B00000FDBV", "B000002H2H", "B000002H6C", "B000002H5E", "B000002H97", "B000002HMH"]'::jsquery; +select count(*) from test_jsquery where v @? 'strict $.similar_product_ids ? (@[*] == "B000002H2H" && @[*] == "B000002H6C")'::jsonpath; +select count(*) from test_jsquery where v @@ '$.customer_id == null'::jsonpath; +select count(*) from test_jsquery where v @@ '$.review_votes == true'::jsonpath; +select count(*) from test_jsquery where v @@ '$.product_group == false'::jsonpath; +select count(*) from test_jsquery where v @? '$.t'::jsonpath; +select count(*) from test_jsquery where v @@ '$.t.type() == "boolean"'::jsonpath; +select count(*) from test_jsquery where v @@ '$.t.type() == "string"'::jsonpath; +select count(*) from test_jsquery where v @@ '$.t.type() == "number"'::jsonpath; +select count(*) from test_jsquery where v @@ '$.t.type() == "array"'::jsonpath; +select count(*) from test_jsquery where v @@ '$.t.type() == "object"'::jsonpath; +select count(*) from test_jsquery where v @@ '$.type() == "boolean"'::jsonpath; +select count(*) from test_jsquery where v @@ '$.type() == "string"'::jsonpath; +select count(*) from test_jsquery where v @@ '$.type() == "number"'::jsonpath; +select count(*) from test_jsquery where v @@ '$.type() == "array"'::jsonpath; +select count(*) from test_jsquery where v @@ '$.type() == "object"'::jsonpath; +--select count(*) from test_jsquery where v @@ 'similar_product_ids.#: ($ is numeric)'::jsonpath; +--select count(*) from test_jsquery where v @@ 'similar_product_ids.#: ($ is string)'::jsonpath; +--select count(*) from test_jsquery where v @@ 'NOT similar_product_ids.#: (NOT $ = "0440180295")'::jsonpath; +select count(*) from test_jsquery where v @@ 'strict $ > 2'::jsonpath; +select count(*) from test_jsquery where v @@ '$ == false'::jsonpath; +select count(*) from test_jsquery where v @? '$.t'::jsonpath; +select count(*) from test_jsquery where v @? '$'::jsonpath; +select count(*) from test_jsquery where v @? '$.similar_product_ids[*]'::jsonpath; +select count(*) from test_jsquery where v @@ '$ ? (@.review_votes > 10) . review_rating < 7'::jsonpath; +select count(*) from test_jsquery where v @? '$.similar_product_ids ? (@[*] == "B0002W4TL2") '::jsonpath; + +--explain (costs off) select v from test_jsquery where v @@ '$.array <@ [2,3]'::jsonpath order by v; +explain (costs off) select v from test_jsquery where v @? '$.array[*] ? (@ == 2 || @ == 3)'::jsonpath order by v; +explain (costs off) select v from test_jsquery where v @@ '$.array[*] == 2 && $.array[*] == 3'::jsonpath order by v; +explain (costs off) select v from test_jsquery where v @@ '$.array[0] == 2 && $.array[1] == 3 && $.array.size() == 2'::jsonpath order by v; + +--select v from test_jsquery where v @@ 'array <@ [2,3]'::jsonpath order by v; +select v from test_jsquery where v @? '$.array[*] ? (@ == 2 || @ == 3)'::jsonpath order by v; +select v from test_jsquery where v @@ '$.array[*] == 2 && $.array[*] == 3'::jsonpath order by v; +select v from test_jsquery where v @@ '$.array[0] == 2 && $.array[1] == 3 && $.array.size() == 2'::jsonpath order by v; + RESET enable_seqscan; From 9a09f20c15598235c9184200aef8c144daecb83f Mon Sep 17 00:00:00 2001 From: Nikita Glukhov Date: Mon, 29 Apr 2019 20:13:18 +0300 Subject: [PATCH 18/21] Extract 'EXISTS(left) && EXISTS(right)' from jsonpath 'left OP right' # Conflicts: # sql/jsquery.sql --- expected/jsquery.out | 62 ++++++++++++++++++++++++++++++++++++++++++++ jsquery_extract.c | 24 +++++++++++++---- sql/jsquery.sql | 9 +++++++ 3 files changed, 90 insertions(+), 5 deletions(-) diff --git a/expected/jsquery.out b/expected/jsquery.out index 60ed213..4880f43 100644 --- a/expected/jsquery.out +++ b/expected/jsquery.out @@ -2823,6 +2823,24 @@ select gin_debug_jsonpath_value_path('$.a > 1'); (1 row) +SELECT gin_debug_jsonpath_value_path('lax $.a == $.b'); + gin_debug_jsonpath_value_path +------------------------------- + AND + + a = * , entry 0 + + b = * , entry 1 + + +(1 row) + +SELECT gin_debug_jsonpath_value_path('strict $.a == $.b'); + gin_debug_jsonpath_value_path +------------------------------- + AND + + a = * , entry 0 + + b = * , entry 1 + + +(1 row) + SELECT gin_debug_jsonpath_path_value('lax $'); gin_debug_jsonpath_path_value ------------------------------- @@ -3954,6 +3972,32 @@ SELECT gin_debug_jsonpath_path_value('strict $.a > 1 && $.a < 2 && $.b >= 3 && $ (1 row) +SELECT gin_debug_jsonpath_path_value('lax $.a == $.b'); + gin_debug_jsonpath_path_value +------------------------------- + AND + + OR + + #.a = * , entry 0 + + #.a.# = * , entry 1 + + a = * , entry 2 + + a.# = * , entry 3 + + OR + + #.b = * , entry 4 + + #.b.# = * , entry 5 + + b = * , entry 6 + + b.# = * , entry 7 + + +(1 row) + +SELECT gin_debug_jsonpath_path_value('strict $.a == $.b'); + gin_debug_jsonpath_path_value +------------------------------- + AND + + a = * , entry 0 + + b = * , entry 1 + + +(1 row) + SELECT gin_debug_jsonpath_laxpath_value('lax $'); gin_debug_jsonpath_laxpath_value ---------------------------------- @@ -4232,6 +4276,24 @@ SELECT gin_debug_jsonpath_laxpath_value('strict $.a > 1 && $.a < 2 && $.b >= 3 & (1 row) +SELECT gin_debug_jsonpath_laxpath_value('lax $.a == $.b'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + AND + + a = * , entry 0 + + b = * , entry 1 + + +(1 row) + +SELECT gin_debug_jsonpath_laxpath_value('strict $.a == $.b'); + gin_debug_jsonpath_laxpath_value +---------------------------------- + AND + + a = * , entry 0 + + b = * , entry 1 + + +(1 row) + ---table and index select count(*) from test_jsquery where (v->>'review_helpful_votes')::int4 > 0; count diff --git a/jsquery_extract.c b/jsquery_extract.c index c31cbc0..6194268 100644 --- a/jsquery_extract.c +++ b/jsquery_extract.c @@ -551,13 +551,14 @@ appendJsonPathExprNode(ExtractedNode *result, ExtractedNode *node, PathItem *pat } static ExtractedNode * -extractJsonPathExists(JsonPathItem *jpi, int flags, bool indirect, PathItem *path) +extractJsonPathExists(JsonPathItem *jpi, int flags, bool indirect, bool unwrap, + PathItem *path) { List *paths; ListCell *lc; ExtractedNode *result; - if (!(paths = extractJsonPath(jpi, flags, false, path))) + if (!(paths = extractJsonPath(jpi, flags, unwrap, path))) return NULL; result = NULL; @@ -668,7 +669,20 @@ extractJsonPathExpr(JsonPathItem *jpi, int flags, bool not, bool indirect, greater = !greater; } else - return NULL; + { + ExtractedNode *lnode; + ExtractedNode *rnode; + + lnode = extractJsonPathExists(&larg, flags, indirect, true, path); + rnode = extractJsonPathExists(&rarg, flags, indirect, true, path); + + if (lnode && rnode) + return makeBinaryNode(eAnd, path, indirect, lnode, rnode); + else if (lnode) + return lnode; + else + return rnode; + } if (!(paths = extractJsonPath(patharg, flags, true, path))) return NULL; @@ -755,7 +769,7 @@ extractJsonPathExpr(JsonPathItem *jpi, int flags, bool not, bool indirect, jspGetArg(jpi, &elem); - return extractJsonPathExists(&elem, flags, indirect, path); + return extractJsonPathExists(&elem, flags, indirect, false, path); } default: @@ -1374,7 +1388,7 @@ extractJsonPathQuery(JsonPath *jp, bool exists, bool arrayPathItems, int optimiz jspInit(&jsp, jp); root = exists - ? extractJsonPathExists(&jsp, flags, false, NULL) + ? extractJsonPathExists(&jsp, flags, false, false, NULL) : extractJsonPathExpr(&jsp, flags, false, false, NULL); return emitExtractedQuery(root, optimize, makeHandler, checkHandler, extra); diff --git a/sql/jsquery.sql b/sql/jsquery.sql index a2c44e6..2c14d72 100644 --- a/sql/jsquery.sql +++ b/sql/jsquery.sql @@ -510,6 +510,9 @@ select gin_debug_jsonpath_value_path('strict exists($.a.b ? (@.c.d == 1))'); select gin_debug_jsonpath_value_path('$.a > 1'); +SELECT gin_debug_jsonpath_value_path('lax $.a == $.b'); +SELECT gin_debug_jsonpath_value_path('strict $.a == $.b'); + SELECT gin_debug_jsonpath_path_value('lax $'); SELECT gin_debug_jsonpath_path_value('strict $'); SELECT gin_debug_jsonpath_path_value('lax $.a'); @@ -553,6 +556,9 @@ select gin_debug_jsonpath_path_value('lax $.a[*].b[*] == 1'); SELECT gin_debug_jsonpath_path_value('lax $.a > 1 && $.a < 2 && $.b >= 3 && $.c <= "aaa"'); SELECT gin_debug_jsonpath_path_value('strict $.a > 1 && $.a < 2 && $.b >= 3 && $.c <= "aaa"'); +SELECT gin_debug_jsonpath_path_value('lax $.a == $.b'); +SELECT gin_debug_jsonpath_path_value('strict $.a == $.b'); + SELECT gin_debug_jsonpath_laxpath_value('lax $'); SELECT gin_debug_jsonpath_laxpath_value('strict $'); SELECT gin_debug_jsonpath_laxpath_value('lax $.a'); @@ -595,6 +601,9 @@ select gin_debug_jsonpath_laxpath_value('lax $.a[*].b[*] == 1'); SELECT gin_debug_jsonpath_laxpath_value('lax $.a > 1 && $.a < 2 && $.b >= 3 && $.c <= "aaa"'); SELECT gin_debug_jsonpath_laxpath_value('strict $.a > 1 && $.a < 2 && $.b >= 3 && $.c <= "aaa"'); +SELECT gin_debug_jsonpath_laxpath_value('lax $.a == $.b'); +SELECT gin_debug_jsonpath_laxpath_value('strict $.a == $.b'); + ---table and index select count(*) from test_jsquery where (v->>'review_helpful_votes')::int4 > 0; From 71a11a9aaa9a1bd0d60dde8b4fcd0948313e9c4d Mon Sep 17 00:00:00 2001 From: Nikita Glukhov Date: Mon, 6 May 2019 16:09:53 +0300 Subject: [PATCH 19/21] Fix typos in README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7b15ae0..3d52ad0 100644 --- a/README.md +++ b/README.md @@ -195,7 +195,7 @@ examples. Same rules apply when you search inside objects and branchy structures. Type checking operators and "every" placeholders are useful for document -schema validation. JsQuery matchig operator `@@` is immutable and can be used +schema validation. JsQuery matching operator `@@` is immutable and can be used in CHECK constraint. See following example. ```sql @@ -309,7 +309,7 @@ query optimization by different opclasses. Unfortunately, jsonb have no statistics yet. That's why JsQuery optimizer has to do imperative decision while selecting conditions to be evaluated using -index. This decision is made by assumtion that some condition types are less +index. This decision is made by assumption that some condition types are less selective than others. Optimizer divides conditions into following selectivity class (listed by descending of selectivity). From c27434547bbce23d4d1f1e8a3e4288145be689b7 Mon Sep 17 00:00:00 2001 From: Nikita Glukhov Date: Mon, 6 May 2019 16:12:19 +0300 Subject: [PATCH 20/21] Add some literal quoting to README --- README.md | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 3d52ad0..060f0ea 100644 --- a/README.md +++ b/README.md @@ -223,8 +223,8 @@ GIN indexes JsQuery extension contains two operator classes (opclasses) for GIN which provide different kinds of query optimization. - * jsonb\_path\_value\_ops - * jsonb\_value\_path\_ops + * `jsonb_path_value_ops` + * `jsonb_value_path_ops` In each of two GIN opclasses jsonb documents are decomposed into entries. Each entry is associated with particular value and it's path. Difference between @@ -235,11 +235,11 @@ For example, jsonb document `{"a": [{"b": "xyz", "c": true}, 10], "d": {"e": [7, false]}}` would be decomposed into following entries: - * "a".#."b"."xyz" - * "a".#."c".true - * "a".#.10 - * "d"."e".#.7 - * "d"."e".#.false + * `"a".#."b"."xyz"` + * `"a".#."c".true` + * `"a".#.10` + * `"d"."e".#.7` + * `"d"."e".#.false` Since JsQuery doesn't support search in particular array index, we consider all array elements to be equivalent. Thus, each array element is marked with @@ -250,9 +250,9 @@ key "a" is presented three times. In the large branchy documents with long keys size of naive entries representation becomes unreasonable. Both opclasses address this issue but in a slightly different way. -### jsonb\_path\_value\_ops +### `jsonb_path_value_ops` -jsonb\_path\_value\_ops represents entry as pair of path hash and value. +`jsonb_path_value_ops` represents entry as pair of path hash and value. Following pseudocode illustrates it. (hash(path_item_1.path_item_2. ... .path_item_n); value) @@ -263,9 +263,10 @@ is hashed and it is higher part of entry we need to know the full path to the value in order to use it for search. However, once path is specified we can use both exact and range searches very efficiently. -### jsonb\_value\_path\_ops -jsonb\_value\_path\_ops represents entry as pair of value and bloom filter +### `jsonb_value_path_ops` + +`jsonb_value_path_ops` represents entry as pair of value and bloom filter of path. (value; bloom(path_item_1) | bloom(path_item_2) | ... | bloom(path_item_n)) @@ -313,11 +314,11 @@ index. This decision is made by assumption that some condition types are less selective than others. Optimizer divides conditions into following selectivity class (listed by descending of selectivity). - 1. Equality (x = c) - 2. Range (c1 < x < c2) - 3. Inequality (x > c) - 4. Is (x is type) - 5. Any (x = \*) + 1. Equality (`x = c`) + 2. Range (`c1 < x < c2`) + 3. Inequality (`x > c`) + 4. Is (`x is type`) + 5. Any (`x = *`) Optimizer evades index evaluation of less selective conditions when possible. For example, in the `x = 1 AND y > 0` query `x = 1` is assumed to be more From 1d4afff045b900d756dc653b15b7aeb81f1d08a9 Mon Sep 17 00:00:00 2001 From: Nikita Glukhov Date: Mon, 6 May 2019 16:13:33 +0300 Subject: [PATCH 21/21] Add docs for jsonb_laxpath_value_ops and for jsonpath debug functions --- README.md | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 060f0ea..eb8fb22 100644 --- a/README.md +++ b/README.md @@ -220,13 +220,14 @@ for more examples. GIN indexes ----------- -JsQuery extension contains two operator classes (opclasses) for GIN which +JsQuery extension contains three operator classes (opclasses) for GIN which provide different kinds of query optimization. * `jsonb_path_value_ops` + * `jsonb_laxpath_value_ops` * `jsonb_value_path_ops` -In each of two GIN opclasses jsonb documents are decomposed into entries. Each +In each of GIN opclasses jsonb documents are decomposed into entries. Each entry is associated with particular value and it's path. Difference between opclasses is in the entry representation, comparison and usage for search optimization. @@ -247,7 +248,7 @@ same `#` sign in the path. Major problem in the entries representation is its size. In the given example key "a" is presented three times. In the large branchy documents with long -keys size of naive entries representation becomes unreasonable. Both opclasses +keys size of naive entries representation becomes unreasonable. All opclasses address this issue but in a slightly different way. ### `jsonb_path_value_ops` @@ -263,6 +264,21 @@ is hashed and it is higher part of entry we need to know the full path to the value in order to use it for search. However, once path is specified we can use both exact and range searches very efficiently. +### `jsonb_laxpath_value_ops` + +`jsonb_laxpath_value_ops` differs from the `jsonb_path_value_ops` only in +that it skips array path items from the hashing. So, the jsonb document above +would be decomposed into following entries: + + * `(hash("a", "b"); "xyz")` + * `(hash("a", "c"); true)` + * `(hash("a"); 10)` + * `(hash("d", "e"); 7)` + * `(hash("d", "e"); false)` + +Skipping array items greatly simplifies extraction of lax JSON path queries – +there is no need to extract all possible unwrapped paths (see example for +`gin_debug_jsonpath_path_value()` below). ### `jsonb_value_path_ops` @@ -287,8 +303,15 @@ Unfortunately, opclasses aren't allowed to do any custom output to the EXPLAIN. That's why JsQuery provides following functions which allows to see how particular opclass optimizes given query. - * gin\_debug\_query\_path\_value(jsquery) – for jsonb\_path\_value\_ops - * gin\_debug\_query\_value\_path(jsquery) – for jsonb\_value\_path\_ops + * `jsonb_path_value_ops`: + - `gin_debug_query_path_value(jsquery)` + - `gin_debug_jsonpath_path_value(jsonpath)` + * `jsonb_laxpath_value_ops`: + - `gin_debug_query_laxpath_value(jsquery)` + - `gin_debug_jsonpath_laxpath_value(jsonpath)` + * `jsonb_value_path_ops`: + - `gin_debug_query_value_path(jsquery)` + - `gin_debug_jsonpath_value_path(jsonpath)` Result of these functions is a textual representation of query tree which leafs are GIN search entries. Following examples show different results of @@ -308,6 +331,31 @@ query optimization by different opclasses. *.y = 1 , entry 1 + y = 2 , entry 2 + +Examples for nearly equivalent JSON path query: + + # SELECT gin_debug_jsonpath_value_path('$.x == 1 && ($.**.y == 1 || $.y == 2)'); + gin_debug_jsonpath_value_path + ------------------------------- + AND + + OR + + *.y = 1 , entry 0 + + y = 2 , entry 1 + + x = 1 , entry 2 + + + # SELECT gin_debug_jsonpath_path_value('$.x == 1 && ($.**.y == 1 || $.y == 2)'); + gin_debug_jsonpath_path_value + ------------------------------- + OR + + #.x = 1 , entry 0 + + #.x.# = 1 , entry 1 + + x = 1 , entry 2 + + x.# = 1 , entry 3 + + + # SELECT gin_debug_jsonpath_laxpath_value('$.x == 1 && ($.**.y == 1 || $.y == 2)'); + gin_debug_jsonpath_laxpath_value + ---------------------------------- + x = 1 , entry 0 + + Unfortunately, jsonb have no statistics yet. That's why JsQuery optimizer has to do imperative decision while selecting conditions to be evaluated using index. This decision is made by assumption that some condition types are less