jsonpath是用來解析json數據的工具,類似於xpath,jsonpath可以解析十分復雜的json數據。
PostgreSQL json發展歷史:
PostgreSQL從9.2開始就支持json數據類型,但是由於解析json數據的性能很差,導致並不受大家青睞,而是選擇使用nosql數據庫代替。於是從pg9.4開始支持了jsonb數據類型,相較於json類型,jsonb由於並不需要每次使用時都去進行解析,因此性能提升很多,都是還支持索引查詢等。
而從pg12開始對於json的支持更加強大:sql 2016的sql/json標准有15條, PG 12 實現了14條, 遠遠超過oracle(18c 11/15), mysql(8.0.4 5/15), sqlserver(2017 2/15)最新版本。
同時在pg12中引入了jsonpath類型,以及一系列相關的函數,使得json數據的查詢性能更進一步,功能也愈發強大。
JSONPATH語法:
JSONpath 函數表達式語法如下:
點號 . 表示引用 Json 數據的元素
方括號 [] 表示引用數組元素
Json 數據中的數組元素下標從0開始
JSONpath中的變量如下:
$ 符號表示要查詢的Json文本的變量
$varname 表示指定變量
@ 指在 filter 表達式中表示當前路徑元素的變量
JSONPATH使用舉例:
簡單查詢:
bill@bill=>SELECT jsonb_path_query_array('[1,2,3,4,5]', '$[*] ? (@ > 3)');
jsonb_path_query_array
[4, 5]
(1 row)
創建測試表:
CREATE TABLE house(js jsonb);
INSERT INTO house VALUES
('{
'address': {
'city':'Moscow',
'street': 'Ulyanova, 7A'
},
'lift': false,
'floor': [
{
'level': 1,
'apt': [
{'no': 1, 'area': 40, 'rooms': 1},
{'no': 2, 'area': 80, 'rooms': 3},
{'no': 3, 'area': 50, 'rooms': 2}
]
},
{
'level': 2,
'apt': [
{'no': 4, 'area': 100, 'rooms': 3},
{'no': 5, 'area': 60, 'rooms': 2}
]
}
]
}');
查詢:
bill@bill=>select jsonb_pretty(js) from house ;
jsonb_pretty
{ +
'lift': false, +
'floor': [ +
{ +
'apt': [ +
{ +
'no': 1, +
'area': 40, +
'rooms': 1 +
}, +
{ +
'no': 2, +
'area': 80, +
'rooms': 3 +
}, +
{ +
'no': 3, +
'area': 50, +
'rooms': 2 +
} +
], +
'level': 1 +
}, +
{ +
'apt': [ +
{ +
'no': 4, +
'area': 100,+
'rooms': 3 +
}, +
{ +
'no': 5, +
'area': 60, +
'rooms': 2 +
} +
], +
'level': 2 +
} +
], +
'address': { +
'city': 'Moscow', +
'street': 'Ulyanova, 7A'+
} +
}
(1 row)
看上去該數據層次挺復雜的,但是實際中可能數據層次遠比這個復雜的多,那么我們看看如何使用jsonpath來進行查詢的:
bill@bill=>SELECT jsonb_path_query_array(js, '$.floor[0, 1].apt[1 to last]') FROM house;
jsonb_path_query_array
[{'no': 2, 'area': 80, 'rooms': 3}, {'no': 3, 'area': 50, 'rooms': 2}, {'no': 5, 'area': 60, 'rooms': 2}]
(1 row)
而如果不用jsonpath的話,我們可能需要這么寫:
bill@bill=>SELECT jsonb_agg(apt) FROM (
bill(# SELECT apt->generate_series(1, jsonb_array_length(apt) - 1) FROM (
bill(# SELECT js->'floor'->unnest(array[0, 1])->'apt' FROM house
bill(# ) apts(apt)
bill(# ) apts(apt);
jsonb_agg
[{'no': 2, 'area': 80, 'rooms': 3}, {'no': 3, 'area': 50, 'rooms': 2}, {'no': 5, 'area': 60, 'rooms': 2}]
(1 row)
相比之下,使用jsonpath相關的函數查詢簡便太多了。
又比如,我們需要判斷json數據中是否包含某個值,可以這樣:
bill@bill=>SELECT jsonb_path_exists(js, '$.** ? (@ == 'Moscow')') FROM house;
jsonb_path_exists
t
(1 row)
而如果不使用jsonpath呢?
bill@bill=>WITH RECURSIVE t(value) AS (
bill(# SELECT * FROM house UNION ALL (
bill(# SELECT COALESCE(kv.value, e.value) AS value
bill(# FROM t
bill(# LEFT JOIN LATERAL jsonb_each (
bill(# CASE WHEN jsonb_typeof(t.value) = 'object' THEN t.value
bill(# ELSE NULL END
bill(# ) kv ON true
bill(# LEFT JOIN LATERAL jsonb_array_elements (
bill(# CASE WHEN jsonb_typeof(t.value) = 'array' THEN t.value
bill(# ELSE NULL END
bill(# ) e ON true
bill(# WHERE kv.value IS NOT NULL OR e.value IS NOT NULL
bill(# )
bill(# ) SELECT EXISTS (SELECT 1 FROM t WHERE value = ''Moscow'');
exists
t
(1 row)
那么如何使用jsonpath進行數據過濾呢?已上面這張表為例,我們查詢apt.no大於3的數據:
bill@bill=>SELECT jsonb_path_query(js, '$.floor.apt.no ? (@>3)') FROM house;
jsonb_path_query
4
5
(2 rows)
同樣,jsonpath也支持索引的使用:
bill@bill=>CREATE INDEX ON house USING gin (js);
CREATE INDEX
bill@bill=>SET ENABLE_SEQSCAN TO OFF;
SET
bill@bill=>EXPLAIN (COSTS OFF) SELECT * FROM house
bill-# WHERE js @? '$.floor[].apt[] ? (@.rooms == 3)';
QUERY PLAN
Bitmap Heap Scan on house
Recheck Cond: (js @? '$.'floor'[].'apt'[]?(@.'rooms' == 3)'::jsonpath)
-> Bitmap Index Scan on house_js_idx
Index Cond: (js @? '$.'floor'[].'apt'[]?(@.'rooms' == 3)'::jsonpath)
(4 rows)
除此之外,jsonpath支持了20多種相關的函數,是不是十分強大,趕快用起來吧!
postgres=# \df .jsonpath
List of functions
Schema | Name | Result data type | Argument data types
| Type
------------+------------------------------+------------------+------------------------------------------------------------------------------------------
-+------
pg_catalog | gin_consistent_jsonb_path | boolean | internal, smallint, jsonb, integer, internal, internal, internal, internal
| func
pg_catalog | gin_extract_jsonb_path | internal | jsonb, internal, internal
| func
pg_catalog | gin_extract_jsonb_query_path | internal | jsonb, internal, smallint, internal, internal, internal, internal
| func
pg_catalog | gin_triconsistent_jsonb_path | 'char' | internal, smallint, jsonb, integer, internal, internal, internal
| func
pg_catalog | json_extract_path | json | from_json json, VARIADIC path_elems text[]
| func
pg_catalog | json_extract_path_text | text | from_json json, VARIADIC path_elems text[]
| func
pg_catalog | jsonb_delete_path | jsonb | jsonb, text[]
| func
pg_catalog | jsonb_extract_path | jsonb | from_json jsonb, VARIADIC path_elems text[]
| func
pg_catalog | jsonb_extract_path_text | text | from_json jsonb, VARIADIC path_elems text[]
| func
pg_catalog | jsonb_path_exists | boolean | target jsonb, path jsonpath, vars jsonb DEFAULT '{}'::jsonb, silent boolean DEFAULT false
| func
pg_catalog | jsonb_path_exists_opr | boolean | jsonb, jsonpath
| func
pg_catalog | jsonb_path_exists_tz | boolean | target jsonb, path jsonpath, vars jsonb DEFAULT '{}'::jsonb, silent boolean DEFAULT false
| func
pg_catalog | jsonb_path_match | boolean | target jsonb, path jsonpath, vars jsonb DEFAULT '{}'::jsonb, silent boolean DEFAULT false
| func
pg_catalog | jsonb_path_match_opr | boolean | jsonb, jsonpath
| func
pg_catalog | jsonb_path_match_tz | boolean | target jsonb, path jsonpath, vars jsonb DEFAULT '{}'::jsonb, silent boolean DEFAULT false
| func
pg_catalog | jsonb_path_query | SETOF jsonb | target jsonb, path jsonpath, vars jsonb DEFAULT '{}'::jsonb, silent boolean DEFAULT false
| func
pg_catalog | jsonb_path_query_array | jsonb | target jsonb, path jsonpath, vars jsonb DEFAULT '{}'::jsonb, silent boolean DEFAULT false
| func
pg_catalog | jsonb_path_query_array_tz | jsonb | target jsonb, path jsonpath, vars jsonb DEFAULT '{}'::jsonb, silent boolean DEFAULT false
| func
pg_catalog | jsonb_path_query_first | jsonb | target jsonb, path jsonpath, vars jsonb DEFAULT '{}'::jsonb, silent boolean DEFAULT false
| func
pg_catalog | jsonb_path_query_first_tz | jsonb | target jsonb, path jsonpath, vars jsonb DEFAULT '{}'::jsonb, silent boolean DEFAULT false
| func
pg_catalog | jsonb_path_query_tz | SETOF jsonb | target jsonb, path jsonpath, vars jsonb DEFAULT '{}'::jsonb, silent boolean DEFAULT false
| func
pg_catalog | jsonpath_in | jsonpath | cstring
| func
pg_catalog | jsonpath_out | cstring | jsonpath
| func
pg_catalog | jsonpath_recv | jsonpath | internal
| func
pg_catalog | jsonpath_send | bytea | jsonpath
| func
(25 rows)