2015-12-05 Александр Коротков, Иван Панченко - Слабо-структурированные данные...
2015-12-05 Александр Коротков, Иван Панченко - Слабо-структурированные данные в СУБД глазами и руками разработчиков PostgreSQL
1. Слабо-структурированные данные в СУБД
PostgreSQL (мастер-класс)
Александр Коротков, Иван Панченко
Postgres Professional
2015
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 1 / 72
2. 1. Немного о массивах
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 2 / 72
3. Исходные данные: products
# d product
Table ”public.product”
Column | Type | Modifiers
---------------------+-----------------+-----------
product_id | character(10) | not null
product_title | text | not null
product_sales_rank | bigint | not null
product_group | text | not null
product_category | text |
product_subcategory | text |
similar_product_ids | character(10)[] | not null
Indexes:
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 3 / 72
4. Subscrip on – вытащить элемент по
заданному индексу
# SELECT product_id, similar_product_ids
FROM product LIMIT 5;
product_id | similar_product_ids
------------+----------------------------------------------------------
0001047655 | {0061007129,0061007358,0061007137,0061099341,0061007161}
0001053736 | {0440905605,0345336062,0140380531,0393320979,0395898714}
0001053744 | {}
0001054600 | {}
0001474103 | {0895403889,0001473123,0766177610,0001472933,0766187403}
(5 rows)
# SELECT product_id, similar_product_ids[2]
FROM product LIMIT 5;
product_id | similar_product_ids
------------+---------------------
0001047655 | 0061007358
0001053736 | 0345336062
0001053744 | NULL
0001054600 | NULL
0001474103 | 0001473123
(5 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 4 / 72
5. Subscrip on – вытащить slice
# SELECT product_id, similar_product_ids[1:2]
FROM product LIMIT 5;
product_id | similar_product_ids
------------+-------------------------
0001047655 | {0061007129,0061007358}
0001053736 | {0440905605,0345336062}
0001053744 | {}
0001054600 | {}
0001474103 | {0895403889,0001473123}
(5 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 5 / 72
6. Subscrip on – обновление
# UPDATE product
SET similar_product_ids[1] = ’0061007129’
WHERE product_id = ’0001047655’;
# UPDATE product
SET similar_product_ids[1:2] = ’{0440905605,0345336062}’
WHERE product_id = ’0001047655’;
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 6 / 72
7. Осторожно – произвольные границы!
# SELECT (’[-3:-2]={1,2}’::int[])[1];
int4
------
NULL
(1 row)
# SELECT (’[-3:-2]={1,2}’::int[])[-2];
int4
------
2
(1 row)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 7 / 72
8. Осторожно – многомерные массивы!
# SELECT (’{{1,2},{3,4}}’::int[])[1];
int4
------
NULL
(1 row)
# SELECT (’{{1,2},{3,4}}’::int[])[1][2];
int4
------
2
(1 row)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 8 / 72
9. array_lower и array_upper – верхняя и
нижняя границы массива
Откусываем у массива первый элемент.
# SELECT product_id,
similar_product_ids[
array_lower(similar_product_ids,1) + 1 :
array_upper(similar_product_ids,1)]
FROM product LIMIT 5;
product_id | similar_product_ids
------------+-----------------------------------------------
0001047655 | {0061007358,0061007137,0061099341,0061007161}
0001053736 | {0345336062,0140380531,0393320979,0395898714}
0001053744 | NULL
0001054600 | NULL
0001474103 | {0001473123,0766177610,0001472933,0766187403}
(5 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 9 / 72
10. Поиск по массиву: ANY
# SELECT product_id, product_title
FROM product WHERE ’0061007137’ = ANY(similar_product_ids);
product_id | product_title
------------+------------------------------------
0001047655 | Prodigal Daughter
0061007129 | Kane & Abel
0061007145 | The Prodigal Daughter
0061007161 | First Among Equals
0061007358 | Not a Penny More, Not a Penny Less
0061013315 | The Eleventh Commandment
0061013706 | Shall We Tell the President?
0061092045 | Honor Among Thieves
0061093653 | Twelve Red Herrings
0061099341 | As the Crow Flies
(10 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 10 / 72
11. Разворачивание массива в rowset: unnest
# SELECT unnest(similar_product_ids)
FROM product WHERE product_id = ’0061007137’;
unnest
------------
0061007358
0061099341
0061007145
0061007161
0061007129
(5 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 11 / 72
12. Сворачивание rowset в массив: array_agg
# SELECT array_agg(product_id)
FROM product
WHERE ’0061093653’ = ANY(similar_product_ids);
array_agg
-----------------------------------------------
{006018552X,0060185805,006100717X,0061032077}
(1 row)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 12 / 72
13. Задача #1
Для каждого продукта собрать массив тех,
которые ссылаются на него через
similar_product_ids.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 13 / 72
14. Задача #1: решение
SELECT
similar_id,
array_agg(product_id)
FROM (
SELECT
product_id,
unnest(similar_product_ids) AS similar_id
FROM
product) s
GROUP BY
similar_id;
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 14 / 72
15. Задача #2
Сделать выборку продуктов, для которых
все продукты, указанные в массиве
similar_product_ids имеют рейтинг не
меньше 100000.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 15 / 72
16. Задача #2: решение
SELECT
*
FROM
product p1
WHERE NOT EXISTS (
SELECT
1
FROM
unnest(p1.similar_product_ids) sid
JOIN
product p2
ON
p2.product_id = sid
WHERE
NOT (p2.product_sales_rank >= 100000));
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 16 / 72
17. Операторы &&, @>, <@ (1/2)
▶ a && b – у массивов a и b есть хотя бы один
общий элемент.
▶ a @> b – в массиве a есть все элементы массива b
и, быть может, другие.
▶ a <@ b – то же, что и b @> a.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 17 / 72
18. Операторы &&, @>, <@ (2/2)
# SELECT count(*) FROM product
WHERE similar_product_ids && ’{0061007137,0007149689}’;
count
-------
11
(1 row)
# SELECT count(*) FROM product
WHERE similar_product_ids @> ’{0061007137,0061007161}’;
count
-------
6
(1 row)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 18 / 72
19. Индексы по массивам (1/3)
▶ Встроенный GIN-индекс для массивов всех типов
(операторы &&, @>, <@, =).
▶ GiST-индекс для int[] в contrib/intarray.
▶ Индексы по конкретным элементам массива
(например, a[3]). Не очень интересно.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 19 / 72
20. Индексы по массивам (2/3)
# EXPLAIN ANALYZE SELECT * FROM product
WHERE similar_product_ids @> ’{0061007137,0061007161}’;
QUERY PLAN
---------------------------------------------------------------
Seq Scan on product (cost=0.00..9348.25 rows=1 width=161) (ac
Filter: (similar_product_ids @> ’{0061007137,0061007161}’::b
Rows Removed by Filter: 253614
Planning time: 0.120 ms
Execution time: 97.547 ms
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 20 / 72
21. Индексы по массивам (3/3)
# CREATE INDEX product_similar_product_ids_idx
ON product USING gin (similar_product_ids);
CREATE INDEX
Time: 18795,075 ms
# EXPLAIN ANALYZE SELECT * FROM product
WHERE similar_product_ids @> ’{0061007137,0061007161}’;
---------------------------------------------------------------
Bitmap Heap Scan on product (cost=28.00..32.01 rows=1 width=1
Recheck Cond: (similar_product_ids @> ’{0061007137,006100716
Heap Blocks: exact=5
-> Bitmap Index Scan on product_similar_product_ids_idx (c
Index Cond: (similar_product_ids @> ’{0061007137,00610
Planning time: 0.123 ms
Execution time: 0.089 ms
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 21 / 72
23. Исходные данные: stocks (1/2)
Table ”public.stocks”
Column | Type | Modifiers
----------+---------+------------------------------------------
id | integer | not null default nextval(’stocks_id_seq’:
country | text | not null
industry | text | not null
sector | text | not null
company | text | not null
ticker | text | not null
volume | integer | not null
h | hstore | not null
Indexes:
”stocks_pkey” PRIMARY KEY, btree (id)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 23 / 72
24. Исходные данные: stocks (2/2)
# SELECT * FROM stocks LIMIT 1;
-[ RECORD 1 ]--------------------------------------------------
id | 1
country | USA
industry | Semiconductor Equipment & Materials
sector | Technology
company | Brooks Automation Inc.
ticker | BRKS
volume | 516739
h | ”Gap”=>”0.0151”, ”P/B”=>”1.05”, ”P/E”=>”6.07”, ”P/S”
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 24 / 72
26. Вытаскивание полей (2/2)
# SELECT company FROM stocks
WHERE h -> ’Price’ > 1000.0;
ERROR: operator does not exist: text > numeric
# SELECT company FROM stocks
WHERE (h -> ’Price’)::numeric > 1000.0;
company
----------------------------
Seaboard Corp.
Berkshire Hathaway Inc.
Google Inc.
priceline.com Incorporated
(4 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 26 / 72
27. Превращаем hstore в набор пар
ключ-значение
# SELECT (each(h)).* FROM stocks WHERE id = 4;
key | value
-------------------------------+----------------------
Gap | 0
Price | 14.95
Change | 0.0047
50-Day Low | 0.0381
50-Day High | -0.0177
52-Week Low | 0.3304
Short Ratio | 0.14
......................................................
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 27 / 72
28. Задача #3
Собрать статистику по использованию
различных ключей в поле h таблицы stocks:
для каждого встречающегося ключа указать
его частоту.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 28 / 72
29. Задача #3: решение
# SELECT key,
count(*)::float8 /
(SELECT count(*) FROM stocks) freq
FROM (SELECT (each(h)).* FROM stocks) kv
GROUP BY key;
key | freq
-----------------------------------+-------------------
Shares Float | 0.725725281231498
Analyst Recom | 0.616489046773239
Insider Transactions | 0.554026050917703
Institutional Transactions | 0.705743043220841
P/B | 0.71166370633511
.......................................................
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 29 / 72
30. Оператор @>
a @> b тогда и только тогда, когда для каждой пары
ключ => значение из b, такая же пара присутствует и в
a.
Примеры.
key1 | key2 | result
-----------------+--------------+--------
a=>x, b=>y, c=>z | a=>x, b=>y | true
a=>x, b=>y | a=>x, b=>qqq | false
a=>x, b=>y | t=>x, b=>y | false
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 30 / 72
31. Операторы ?, ?&, ?|
▶ a ? b – проверяет наличие ключа b в массиве a.
▶ a ?& b – проверяет наличие всех ключей из
массива b в массиве a.
▶ a ?| b – проверяет наличие хотя бы одного из
ключей массива b в массиве a.
# SELECT count(*),
count(*) FILTER
(WHERE h ? ’Short Ratio’),
count(*) FILTER
(WHERE h ?| ’{Short Ratio, Change from Open}’),
count(*) FILTER
(WHERE h ?& ’{Short Ratio, Change from Open}’)
FROM stocks;
count | count | count | count
-------+-------+-------+-------
6756 | 5412 | 6743 | 5412
(1 row)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 31 / 72
32. Задача #4
Написать аналог оператора @> с помощью
функций each(hstore) и операторов ->, ?.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 32 / 72
33. Задача #4: решение
CREATE FUNCTION contains_sql(hstore, hstore) RETURNS bool
AS $$
SELECT NOT EXISTS (
SELECT 1
FROM each($2) kv
WHERE NOT (
$1 ? key AND
$1 -> key IS NOT DISTINCT FROM value)
)
$$ LANGUAGE sql;
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 33 / 72
34. Конструируем hstore
# SELECT hstore(’{key1, key2}’::text[],
’{value1, value2}’::text[]);
hstore
------------------------------------
”key1”=>”value1”, ”key2”=>”value2”
(1 row)
# SELECT hstore(array_agg(id::text),
array_agg(company))
FROM stocks WHERE id <= 4;
hstore
------------------------------------------------------
”1”=>”Brooks Automation Inc.”, ”2”=>”Broadcom Corp.”
(1 row)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 34 / 72
35. Задача #5
Посчитать для каждой отрасли (sector) из
таблицы stocks суммарный объём ценных
бумаг (volume), и выдать это в виде hstore
вида: отрасль => суммарный объём ценный
бумаг.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 35 / 72
36. Задача #5: решение
# SELECT hstore(
array_agg(sector),
array_agg(volume::text)) h
FROM (SELECT sector,
SUM(volume) AS volume
FROM stocks
GROUP BY sector) s;
-[ RECORD 1 ]--------------------------------------------------------
h | ”Services”=>”637878158”, ”Financial”=>”1389743864”,
| ”Utilities”=>”94268768”, ”Healthcare”=>”381247435”,
| ”Technology”=>”1225484169”, ”Conglomerates”=>”3547704”,
| ”Consumer Goods”=>”276207382”,
| ”Basic Materials”=>”625043686”,
| ”Industrial Goods”=>”200105263”
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 36 / 72
37. Индексы для hstore
▶ GIN/GiST индексы: индексируем ключи отдельно,
значения отдельно (операторы @>, ?, ?&, ?|).
▶ GIN индекс: индексируем пары hash(key) +
hash(value) (операторы @>, ?, ?&, ?|)
https://github.com/postgrespro/hstore_ops
▶ Индекс по отдельному ключу (h->’key’).
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 37 / 72
38. 3. Jsonb
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 38 / 72
39. Исходные данные: company
# d company
Table ”public.company”
Column | Type | Modifiers
--------+-------+-----------
js | jsonb |
# SELECT js FROM company limit 5;
---------------------------------------------------------------
{”_id”: {”$oid”: ”52cdef7c4bab8bd675297d8a”}, ”name”: ”Wetpain
{”_id”: {”$oid”: ”52cdef7c4bab8bd675297d8b”}, ”name”: ”AdventN
{”_id”: {”$oid”: ”52cdef7c4bab8bd675297d8c”}, ”name”: ”Zoho”,
{”_id”: {”$oid”: ”52cdef7c4bab8bd675297d8d”}, ”ipo”: null, ”na
{”_id”: {”$oid”: ”52cdef7c4bab8bd675297d8e”}, ”ipo”: {”pub_day
(5 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 39 / 72
40. Jsonb pre y print
# SELECT jsonb_pretty(js) FROM company LIMIT 1;
-[ RECORD 1 ]+-------------------------------------------
jsonb_pretty | {
| ”_id”: {
| ”$oid”: ”52cdef7c4bab8bd675297d8a”
| },
| ”name”: ”Wetpaint”,
| ”image”: {
| ”available_sizes”: [
| [
| [
| 150,
| 75
.........................................................
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 40 / 72
42. Вытаскивание полей и типы (1/2)
-> вытаскивает тип jsonb
# SELECT js -> ’name’ FROM company
WHERE js -> ’name’ ILIKE ’%paint%’;
ERROR: operator does not exist: jsonb ~~* unknown
Приведение значения поля к строке
# SELECT (js -> ’name’)::text, js ->> ’name’
FROM company LIMIT 5;
text | ?column?
-------------+-----------
”Wetpaint” | Wetpaint
”AdventNet” | AdventNet
”Zoho” | Zoho
”Digg” | Digg
”Facebook” | Facebook
(5 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 42 / 72
43. Вытаскивание полей и типы (2/2)
# SELECT
js ->> ’name’,
js ->> ’number_of_employees’
FROM company
WHERE
js ->> ’name’ ILIKE ’%paint%’ AND
(js ->> ’number_of_employees’)::int >= 10;
?column? | ?column?
----------+----------
Wetpaint | 47
(1 row)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 43 / 72
44. Вытаскивание элементов массивов
# SELECT
js -> ’offices’ -> 0 ->> ’city’,
js #>> ’{offices,0,city}’
FROM company LIMIT 5;
?column? | ?column?
---------------+---------------
Seattle | Seattle
Pleasanton | Pleasanton
Pleasanton | Pleasanton
San Francisco | San Francisco
Menlo Park | Menlo Park
(5 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 44 / 72
45. Индексы по выражениям
# CREATE UNIQUE INDEX company_id_idx
ON company ((js -> ’_id’ ->> ’$oid’));
CREATE INDEX
# EXPLAIN ANALYZE SELECT js -> ’name’ FROM company
WHERE js -> ’_id’ ->> ’$oid’ = ’52cdef7c4bab8bd675297d8a’;
QUERY P
---------------------------------------------------------------
Index Scan using company_id_idx on company (cost=0.29..8.31 r
Index Cond: (((js -> ’_id’::text) ->> ’$oid’::text) = ’52cde
Planning time: 0.084 ms
Execution time: 0.156 ms
(4 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 45 / 72
46. Разворачивание JSON-массивов в rowset
# SELECT jsonb_array_elements(js -> ’competitions’)
FROM company
WHERE js -> ’_id’ ->> ’$oid’ = ’52cdef7c4bab8bd675297d8a’;
jsonb_array_elements
---------------------------------------------------------------------
{”competitor”: {”name”: ”Wikia”, ”permalink”: ”wikia”}}
{”competitor”: {”name”: ”JotSpot”, ”permalink”: ”jotspot”}}
{”competitor”: {”name”: ”Socialtext”, ”permalink”: ”socialtext”}}
{”competitor”: {”name”: ”Ning by Glam Media”, ”permalink”: ”ning”}}
{”competitor”: {”name”: ”Soceeo”, ”permalink”: ”soceeo”}}
{”competitor”: {”name”: ”Yola”, ”permalink”: ”yola”}}
{”competitor”: {”name”: ”SocialGO”, ”permalink”: ”socialgo”}}
{”competitor”: {”name”: ”IslamNor”, ”permalink”: ”islamnor”}}
(8 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 46 / 72
47. Разворачивание JSON-объекта
в key-value rowset
# SELECT (jsonb_each(js #> ’{offices, 0}’)).*
FROM company
WHERE js -> ’_id’ ->> ’$oid’ = ’52cdef7c4bab8bd675297d8a’;
key | value
--------------+--------------------
city | ”Seattle”
address1 | ”710 - 2nd Avenue”
address2 | ”Suite 1100”
latitude | 47.603122
zip_code | ”98104”
longitude | -122.333253
state_code | ”WA”
description | ””
country_code | ”USA”
(9 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 47 / 72
48. Оператор @>
▶ Для скалярных a и b, a @> b тогда и только тогда, когда a = b.
▶ Если a и b объекты, то для каждого ключа в b должен
существовать ключ в a, содержащий соответствующее значение.
▶ Если a и b массивы, то для каждого элемента в b должен
существовать содержащий его элемент в a.
Примеры.
arg1 | arg2 | result
---------------------+----------------------+--------
1 | 1 | true
1 | ”1” | false
{”a”: 1, ”b”: 2} | {”a”: 1} | true
{”a”: 1, ”b”: 2} | {”a”: 2} | false
[1, 2] | [1] | true
[{”a”: 1, ”b”: 2}] | [{”a”: 1}, {”b”: 2}] | true
[{”a”: 1}, {”b”: 2}] | [{”a”: 1, ”b”: 2}] | false
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 48 / 72
49. Установка plv8
$ git clone https://github.com/plv8/plv8.git
$ cd plv8
$ make USE_PGXS=1
$ sudo make USE_PGXS=1 install
$ make USE_PGXS=1 installcheck
$ psql DB -c ”CREATE EXTENSION plv8;”
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 49 / 72
50. Задача #6
Написать на plv8 функцию, которая для
компании выводит список стран, где у неё
есть офисы.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 50 / 72
51. Задача #6: решение
CREATE FUNCTION extract_countries(company jsonb)
RETURNS text[]
AS $$
var tmp = {}, result = [];
for(var i = 0; i < company.offices.length; i++)
tmp[company.offices[i].country_code] = true;
for(var i in tmp)
result.push(i)
return result;
$$ LANGUAGE plv8 IMMUTABLE STRICT;
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 51 / 72
52. Индекс по странам
CREATE INDEX company_countries_idx ON company
USING gin(extract_countries(js));
# EXPLAIN ANALYZE SELECT * FROM company
WHERE extract_countries(js) @> ’{RUS}’;
QUER
---------------------------------------------------------------
Bitmap Heap Scan on company (cost=8.73..348.48 rows=94 width=
(actual time=0.038..0.089 rows=50 loops=1)
Recheck Cond: (extract_countries(js) @> ’{RUS}’::text[])
Heap Blocks: exact=48
-> Bitmap Index Scan on company_countries_idx (cost=0.00..
Index Cond: (extract_countries(js) @> ’{RUS}’::text[])
Planning time: 0.057 ms
Execution time: 0.113 ms
(7 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 52 / 72
53. Задача #7
Написать на plv8 агрегат, который который
посчитает частоты различных размеров
картинок.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 53 / 72
54. Задача #7: решение (1/2)
CREATE FUNCTION image_size_agg_sfunc(state jsonb,
company jsonb) RETURNS jsonb
AS $$
if (!company.image) return state;
var sizes = company.image.available_sizes
for (var i = 0; i < sizes.length; i++)
{
var sizeKey = sizes[i][0][0] + ’x’ +
sizes[i][0][1];
if (sizeKey in state) state[sizeKey]++;
else state[sizeKey] = 1;
}
return state;
$$ LANGUAGE plv8 IMMUTABLE STRICT;
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 54 / 72
55. Задача #7: решение (2/2)
CREATE AGGREGATE image_size_agg (jsonb) (
SFUNC = image_size_agg_sfunc,
STYPE = jsonb,
INITCOND = ’{}’);
# SELECT image_size_agg(js) FROM company;
-[ RECORD 1 ]--+-----------------------------------------------
image_size_agg | {”1x1”: 15, ”32x8”: 3, ”150x7”: 1, ”150x8”: 5,
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 55 / 72
56. Установка jsquery
$ git clone https://github.com/postgrespro/jsquery.git
$ cd jsquery
$ make USE_PGXS=1
$ sudo make USE_PGXS=1 install
$ make USE_PGXS=1 installcheck
$ psql DB -c ”CREATE EXTENSION jsquery;”
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 56 / 72
57. Индексы для jsonb
▶ GIN jsonb_ops: индексируем ключи отдельно,
значения отдельно (операторы @>, ?, ?&, ?|).
▶ GIN jsonb_path_ops: индексируем hash(path +
value) (операторы @>).
▶ jsonb_path_value_ops: индексируем hash(path) +
hash(value) (операторы @>, jsonb @@ jsquery).
▶ jsonb_value_path_ops: индексируем hash(value) +
bloom(path) (операторы @>, jsonb @@ jsquery).
▶ Индексы по отдельным ключам (js -> ’key’).
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 57 / 72
58. Пример: найти комании с офисами в
России
-- Оператор @>
# SELECT js->’name’ FROM company
WHERE js @> ’{”offices”:[{”country_code”: ”RUS”}]}’;
-- Оператор jsquery
# SELECT js->’name’ FROM company
WHERE js @@ ’offices.#.country_code = ”RUS”’;
-- Разворачивание jsonb в rowset
# SELECT js->’name’ FROM company
WHERE EXISTS (
SELECT 1
FROM jsonb_array_elements(js->’offices’) el
WHERE el ->> ’country_code’ = ’RUS’);
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 58 / 72
59. Задача #8
Найти компании, конкуренты Wikia, у
которых есть офис в США.
Реализовать решение с помощью:
▶ оператора @>,
▶ jsquery,
▶ разворачивания jsonb в rowset.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 59 / 72
60. Задача #8: решение (1/3)
-- Оператор @>
# SELECT js->’name’ FROM company
WHERE
js @>
’{”competitions”:[{”competitor”:
{”name”: ”Wikia”}
}]}’ AND
js @>
’{”offices”:[{”country_code”: ”USA”}]}’;
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 60 / 72
61. Задача #8: решение (2/3)
-- jsquery
# SELECT js->’name’ FROM company
WHERE js @@
’competitions.#.competitor.name = ”Wikia” AND
offices.#.country_code = ”USA”’;
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 61 / 72
62. Задача #8: решение (3/3)
-- Разворачивание jsonb в rowset
SELECT js->’name’ FROM company
WHERE
EXISTS (
SELECT 1
FROM jsonb_array_elements(js->’competitions’) el
WHERE el -> ’competitor’ ->> ’name’ = ’Wikia’
) AND EXISTS (
SELECT 1
FROM jsonb_array_elements(js->’offices’) el
WHERE el ->> ’country_code’ = ’USA’
);
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 62 / 72
63. Задача #9
Найти компании, в которые финансовая
организация Trinity Ventures инвестировала
в 2007 или 2008 году.
Реализовать решение с помощью:
▶ оператора @>,
▶ jsquery,
▶ разворачивания jsonb в rowset.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 63 / 72
64. Задача #9: решение (1/3)
-- Оператор @>
# SELECT js->’name’ FROM company
WHERE
js @>
’{”funding_rounds”:[{”funded_year”: 2007,
”investments”:[{”financial_org”:
{”name”:”Trinity Ventures”}}
]}
]}’ OR
js @>
’{”funding_rounds”:[{”funded_year”: 2008,
”investments”:[{”financial_org”:
{”name”:”Trinity Ventures”}}
]}
]}’;
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 64 / 72
65. Задача #9: решение (2/3)
-- jsquery
# SELECT js->’name’ FROM company
WHERE
js @@
’funding_rounds.#(
funded_year IN (2007,2008) AND
investments.#.
financial_org.name = ”Trinity Ventures”)’;
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 65 / 72
66. Задача #9: решение (3/3)
-- Разворачивание jsonb в rowset
# SELECT js->’name’ FROM company
WHERE EXISTS (
SELECT 1
FROM jsonb_array_elements(js->’funding_rounds’) el
WHERE el -> ’funded_year’ IN (’2007’,’2008’) AND EXISTS
(SELECT 1
FROM jsonb_array_elements(el -> ’investments’) el2
WHERE el2 -> ’financial_org’ ->>
’name’ = ’Trinity Ventures’));
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 66 / 72
67. Задача #10
Найти компании, которые в 2008 году
привлекли более 100 000 000 USD
инвестиций.
Реализовать решение с помощью:
▶ jsquery,
▶ разворачивания jsonb в rowset.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 67 / 72
68. Задача #10: решение (1/2)
-- jsquery
# SELECT js -> ’name’
FROM company
WHERE js @@
’funding_rounds.# (funded_year = 2008 AND
raised_currency_code = ”USD” AND
raised_amount > 100000000)’;
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 68 / 72
69. Задача #10: решение (2/2)
-- Разворачивание jsonb в rowset
# SELECT js->’name’ FROM company
WHERE EXISTS (
SELECT 1
FROM jsonb_array_elements(js->’funding_rounds’) el
WHERE
el -> ’funded_year’ = ’2008’ AND
el ->> ’raised_currency_code’ = ’USD’ AND
el -> ’raised_amount’ > ’100000000’);
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 69 / 72
70. Задача #11
Найти компании, у которых суммарный
объём инвестиций был более 1 000 000 000
USD.
Реализовать решение с помощью
разворачивания jsonb в rowset.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 70 / 72
71. Задача #11: решение
-- Разворачивание jsonb в rowset
SELECT js->’name’ FROM company
WHERE (
SELECT SUM((el ->> ’raised_amount’)::numeric)
FROM jsonb_array_elements(js->’funding_rounds’) el
WHERE
el ->> ’raised_currency_code’ = ’USD’) > 1000000000;
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 71 / 72
72. Спасибо за внимание!
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 72 / 72