2015-12-05 Александр Коротков, Иван Панченко -...
TRANSCRIPT
Слабо-структурированные данные в СУБДPostgreSQL (мастер-класс)
Александр Коротков, Иван Панченко
Postgres Professional
2015
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 1 / 72
1. Немного о массивах
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 2 / 72
Исходные данные: products
# \d productTable ”public.product”
Column | Type | Modifiers---------------------+-----------------+-----------product_id | character(10) | not nullproduct_title | text | not nullproduct_sales_rank | bigint | not nullproduct_group | text | not nullproduct_category | text |product_subcategory | text |similar_product_ids | character(10)[] | not null
Indexes:
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 3 / 72
Subscrip on – вытащить элемент позаданному индексу
# SELECT product_id, similar_product_idsFROM 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 | 00610073580001053736 | 03453360620001053744 | NULL0001054600 | NULL0001474103 | 0001473123
(5 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 4 / 72
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
Subscrip on – обновление
# UPDATE productSET similar_product_ids[1] = ’0061007129’WHERE product_id = ’0001047655’;
# UPDATE productSET similar_product_ids[1:2] = ’{0440905605,0345336062}’WHERE product_id = ’0001047655’;
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 6 / 72
Осторожно – произвольные границы!
# 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
Осторожно – многомерные массивы!
# 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
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 | NULL0001054600 | NULL0001474103 | {0001473123,0766177610,0001472933,0766187403}
(5 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 9 / 72
Поиск по массиву: ANY
# SELECT product_id, product_titleFROM product WHERE ’0061007137’ = ANY(similar_product_ids);
product_id | product_title------------+------------------------------------0001047655 | Prodigal Daughter0061007129 | Kane & Abel0061007145 | The Prodigal Daughter0061007161 | First Among Equals0061007358 | Not a Penny More, Not a Penny Less0061013315 | The Eleventh Commandment0061013706 | Shall We Tell the President?0061092045 | Honor Among Thieves0061093653 | Twelve Red Herrings0061099341 | As the Crow Flies
(10 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 10 / 72
Разворачивание массива в rowset: unnest
# SELECT unnest(similar_product_ids)FROM product WHERE product_id = ’0061007137’;unnest
------------00610073580061099341006100714500610071610061007129
(5 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 11 / 72
Сворачивание rowset в массив: array_agg
# SELECT array_agg(product_id)FROM productWHERE ’0061093653’ = ANY(similar_product_ids);
array_agg-----------------------------------------------{006018552X,0060185805,006100717X,0061032077}
(1 row)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 12 / 72
Задача #1
Для каждого продукта собрать массив тех,которые ссылаются на него черезsimilar_product_ids.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 13 / 72
Задача #1: решение
SELECTsimilar_id,array_agg(product_id)
FROM (SELECT
product_id,unnest(similar_product_ids) AS similar_id
FROMproduct) s
GROUP BYsimilar_id;
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 14 / 72
Задача #2
Сделать выборку продуктов, для которыхвсе продукты, указанные в массивеsimilar_product_ids имеют рейтинг неменьше 100000.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 15 / 72
Задача #2: решение
SELECT*
FROMproduct p1
WHERE NOT EXISTS (SELECT
1FROM
unnest(p1.similar_product_ids) sidJOIN
product p2ON
p2.product_id = sidWHERE
NOT (p2.product_sales_rank >= 100000));
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 16 / 72
Операторы &&, @>, <@ (1/2)
▶ a && b – у массивов a и b есть хотя бы одинобщий элемент.
▶ a@> b – в массиве a есть все элементы массива bи, быть может, другие.
▶ a <@ b – то же, что и b@> a.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 17 / 72
Операторы &&, @>, <@ (2/2)
# SELECT count(*) FROM productWHERE similar_product_ids && ’{0061007137,0007149689}’;count
-------11
(1 row)
# SELECT count(*) FROM productWHERE similar_product_ids @> ’{0061007137,0061007161}’;count
-------6
(1 row)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 18 / 72
Индексы по массивам (1/3)
▶ Встроенный GIN-индекс для массивов всех типов(операторы &&, @>, <@, =).
▶ GiST-индекс для int[] в contrib/intarray.▶ Индексы по конкретным элементам массива(например, a[3]). Не очень интересно.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 19 / 72
Индексы по массивам (2/3)
# EXPLAIN ANALYZE SELECT * FROM productWHERE similar_product_ids @> ’{0061007137,0061007161}’;
QUERY PLAN-------------------------------------------------------------------------------------------------------Seq Scan on product (cost=0.00..9348.25 rows=1 width=161) (actual time=0.009..97.527 rows=6 loops=1)
Filter: (similar_product_ids @> ’{0061007137,0061007161}’::bpchar[])Rows Removed by Filter: 253614
Planning time: 0.120 msExecution time: 97.547 ms
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 20 / 72
Индексы по массивам (3/3)
# CREATE INDEX product_similar_product_ids_idxON product USING gin (similar_product_ids);
CREATE INDEXTime: 18795,075 ms# EXPLAIN ANALYZE SELECT * FROM product
WHERE similar_product_ids @> ’{0061007137,0061007161}’;QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------Bitmap Heap Scan on product (cost=28.00..32.01 rows=1 width=161) (actual time=0.059..0.065 rows=6 loops=1)
Recheck Cond: (similar_product_ids @> ’{0061007137,0061007161}’::bpchar[])Heap Blocks: exact=5-> Bitmap Index Scan on product_similar_product_ids_idx (cost=0.00..28.00 rows=1 width=0) (actual time=0.054..0.054 rows=6 loops=1)
Index Cond: (similar_product_ids @> ’{0061007137,0061007161}’::bpchar[])Planning time: 0.123 msExecution time: 0.089 ms
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 21 / 72
2. Hstore
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 22 / 72
Исходные данные: stocks (1/2)
Table ”public.stocks”Column | Type | Modifiers
----------+---------+-----------------------------------------------------id | integer | not null default nextval(’stocks_id_seq’::regclass)country | text | not nullindustry | text | not nullsector | text | not nullcompany | text | not nullticker | text | not nullvolume | integer | not nullh | hstore | not null
Indexes:”stocks_pkey” PRIMARY KEY, btree (id)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 23 / 72
Исходные данные: stocks (2/2)
# SELECT * FROM stocks LIMIT 1;-[ RECORD 1 ]-----------------------------------------------------------------------------------------------------------------------------------------id | 1country | USAindustry | Semiconductor Equipment & Materialssector | Technologycompany | Brooks Automation Inc.ticker | BRKSvolume | 516739h | ”Gap”=>”0.0151”, ”P/B”=>”1.05”, ”P/E”=>”6.07”, ”P/S”=>”1.45”, ”PEG”=>”0.34”, ”Beta”=>”1.66”, ”Price”=>”10.24”, ”Change”=>”0.0291”, ”P/Cash”
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 24 / 72
Вытаскивание полей (1/2)
# SELECT id, h -> ’Price’, h -> ’Change’FROM stocks LIMIT 10;id | ?column? | ?column?
----+-------------------+----------1 | 10.24 | 0.02912 | 26.94 | -0.0233 | 92.56999999999999 | 0.00164 | 14.95 | 0.00475 | 26.65 | 0.00136 | 25.33 | -0.02617 | 62.7 | 0.00618 | 11.9 | -0.00179 | 24.44 | 0.018810 | 36.87 | 0.0104
(10 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 25 / 72
Вытаскивание полей (2/2)
# SELECT company FROM stocksWHERE h -> ’Price’ > 1000.0;
ERROR: operator does not exist: text > numeric
# SELECT company FROM stocksWHERE (h -> ’Price’)::numeric > 1000.0;
company----------------------------Seaboard Corp.Berkshire Hathaway Inc.Google Inc.priceline.com Incorporated
(4 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 26 / 72
Превращаем hstore в набор парключ-значение
# SELECT (each(h)).* FROM stocks WHERE id = 4;key | value
-------------------------------+----------------------Gap | 0Price | 14.95Change | 0.004750-Day Low | 0.038150-Day High | -0.017752-Week Low | 0.3304Short Ratio | 0.14
......................................................
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 27 / 72
Задача #3
Собрать статистику по использованиюразличных ключей в поле h таблицы stocks:для каждого встречающегося ключа указатьего частоту.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 28 / 72
Задача #3: решение
# SELECT key,count(*)::float8 /
(SELECT count(*) FROM stocks) freqFROM (SELECT (each(h)).* FROM stocks) kvGROUP BY key;
key | freq-----------------------------------+-------------------Shares Float | 0.725725281231498Analyst Recom | 0.616489046773239Insider Transactions | 0.554026050917703Institutional Transactions | 0.705743043220841P/B | 0.71166370633511
.......................................................
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 29 / 72
Оператор @>
a@> b тогда и только тогда, когда для каждой парыключ => значение из b, такая же пара присутствует и вa.Примеры.
key1 | key2 | result-----------------+--------------+--------a=>x, b=>y, c=>z | a=>x, b=>y | truea=>x, b=>y | a=>x, b=>qqq | falsea=>x, b=>y | t=>x, b=>y | false
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 30 / 72
Операторы ?, ?&, ?|
▶ 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
Задача #4
Написать аналог оператора @> с помощьюфункций each(hstore) и операторов ->, ?.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 32 / 72
Задача #4: решение
CREATE FUNCTION contains_sql(hstore, hstore) RETURNS boolAS $$
SELECT NOT EXISTS (SELECT 1FROM each($2) kvWHERE NOT (
$1 ? key AND$1 -> key IS NOT DISTINCT FROM value)
)$$ LANGUAGE sql;
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 33 / 72
Конструируем 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
Задача #5
Посчитать для каждой отрасли (sector) изтаблицы stocks суммарный объём ценныхбумаг (volume), и выдать это в виде hstoreвида: отрасль => суммарный объём ценныйбумаг.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 35 / 72
Задача #5: решение
# SELECT hstore(array_agg(sector),array_agg(volume::text)) h
FROM (SELECT sector,SUM(volume) AS volume
FROM stocksGROUP 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
Индексы для hstore
▶ GIN/GiST индексы: индексируем ключи отдельно,значения отдельно (операторы @>, ?, ?&, ?|).
▶ GIN индекс: индексируем пары hash(key) +hash(value) (операторы @>, ?, ?&, ?|)https://github.com/postgrespro/hstore_ops
▶ Индекс по отдельному ключу (h->’key’).
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 37 / 72
3. Jsonb
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 38 / 72
Исходные данные: company
# \d companyTable ”public.company”
Column | Type | Modifiers--------+-------+-----------js | jsonb |
# SELECT js FROM company limit 5;
------------------------------------------------------------------{”_id”: {”$oid”: ”52cdef7c4bab8bd675297d8a”}, ”name”: ”Wetpaint”,{”_id”: {”$oid”: ”52cdef7c4bab8bd675297d8b”}, ”name”: ”AdventNet”,{”_id”: {”$oid”: ”52cdef7c4bab8bd675297d8c”}, ”name”: ”Zoho”,{”_id”: {”$oid”: ”52cdef7c4bab8bd675297d8d”}, ”ipo”: null, ”name”: ”Digg”,{”_id”: {”$oid”: ”52cdef7c4bab8bd675297d8e”}, ”ipo”: {”pub_day”: 18, ”pub_year”: 2012,$
(5 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 39 / 72
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
Вытаскивание полей
# SELECT js -> ’name’,js -> ’acquisition’ -> ’price_amount’,js #> ’{acquisition,price_amount}’
FROM companyLIMIT 5;
?column? | ?column? | ?column?-------------------------+-----------+-----------”Wetpaint” | 30000000 | 30000000”AdventNet” | NULL | NULL”Zoho” | NULL | NULL”Digg” | 500000 | 500000”Facebook” | NULL | NULL
(5 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 41 / 72
Вытаскивание полей и типы (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
Вытаскивание полей и типы (2/2)
# SELECTjs ->> ’name’,js ->> ’number_of_employees’
FROM companyWHERE
js ->> ’name’ ILIKE ’%paint%’ AND(js ->> ’number_of_employees’)::int >= 10;
?column? | ?column?----------+----------Wetpaint | 47
(1 row)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 43 / 72
Вытаскивание элементов массивов
# SELECTjs -> ’offices’ -> 0 ->> ’city’,js #>> ’{offices,0,city}’
FROM company LIMIT 5;?column? | ?column?
---------------+---------------Seattle | SeattlePleasanton | PleasantonPleasanton | PleasantonSan Francisco | San FranciscoMenlo Park | Menlo Park
(5 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 44 / 72
Индексы по выражениям
# CREATE UNIQUE INDEX company_id_idxON company ((js -> ’_id’ ->> ’$oid’));CREATE INDEX
# EXPLAIN ANALYZE SELECT js -> ’name’ FROM companyWHERE js -> ’_id’ ->> ’$oid’ = ’52cdef7c4bab8bd675297d8a’;
QUERY PLAN--------------------------------------------------------------------------------------------------------------------------Index Scan using company_id_idx on company (cost=0.29..8.31 rows=1 width=882) (actual time=0.126..0.129 rows=1 loops=1)
Index Cond: (((js -> ’_id’::text) ->> ’$oid’::text) = ’52cdef7c4bab8bd675297d8a’::text)Planning time: 0.084 msExecution time: 0.156 ms
(4 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 45 / 72
Разворачивание JSON-массивов в rowset
# SELECT jsonb_array_elements(js -> ’competitions’)FROM companyWHERE 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
Разворачивание JSON-объектав key-value rowset
# SELECT (jsonb_each(js #> ’{offices, 0}’)).*FROM companyWHERE js -> ’_id’ ->> ’$oid’ = ’52cdef7c4bab8bd675297d8a’;
key | value--------------+--------------------city | ”Seattle”address1 | ”710 - 2nd Avenue”address2 | ”Suite 1100”latitude | 47.603122zip_code | ”98104”longitude | -122.333253state_code | ”WA”description | ””country_code | ”USA”
(9 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 47 / 72
Оператор @>
▶ Для скалярных a и b, a@> b тогда и только тогда, когда a = b.▶ Если a и b объекты, то для каждого ключа в b должен
существовать ключ в a, содержащий соответствующее значение.▶ Если a и b массивы, то для каждого элемента в b должен
существовать содержащий его элемент в a.
Примеры.arg1 | arg2 | result
---------------------+----------------------+--------1 | 1 | true1 | ”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
Установка 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
Задача #6
Написать на plv8 функцию, которая длякомпании выводит список стран, где у неёесть офисы.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 50 / 72
Задача #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
Индекс по странам
CREATE INDEX company_countries_idx ON companyUSING gin(extract_countries(js));# EXPLAIN ANALYZE SELECT * FROM company
WHERE extract_countries(js) @> ’{RUS}’;QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------Bitmap Heap Scan on company (cost=8.73..348.48 rows=94 width=882)
(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..8.71 rows=94 width=0) (actual time=0.027..0.027 rows=50 loops=1)
Index Cond: (extract_countries(js) @> ’{RUS}’::text[])Planning time: 0.057 msExecution time: 0.113 ms
(7 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 52 / 72
Задача #7
Написать на plv8 агрегат, который которыйпосчитает частоты различных размеровкартинок.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 53 / 72
Задача #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_sizesfor (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
Задача #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, ”150x9”: 3, ”17x17”: 3, ”26x27”: 3, ”30x39”: 3, ”32x31”: 3, ”32x32”: 12, ”35x21”: 3, ”
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 55 / 72
Установка 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
Индексы для 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
Пример: найти комании с офисами вРоссии
-- Оператор @># 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 1FROM jsonb_array_elements(js->’offices’) elWHERE el ->> ’country_code’ = ’RUS’);
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 58 / 72
Задача #8
Найти компании, конкуренты Wikia, укоторых есть офис в США.Реализовать решение с помощью:
▶ оператора @>,▶ jsquery,▶ разворачивания jsonb в rowset.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 59 / 72
Задача #8: решение (1/3)
-- Оператор @># SELECT js->’name’ FROM company
WHEREjs @>’{”competitions”:[{”competitor”:
{”name”: ”Wikia”}}]}’ AND
js @>’{”offices”:[{”country_code”: ”USA”}]}’;
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 60 / 72
Задача #8: решение (2/3)
-- jsquery# SELECT js->’name’ FROM company
WHERE js @@’competitions.#.competitor.name = ”Wikia” ANDoffices.#.country_code = ”USA”’;
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 61 / 72
Задача #8: решение (3/3)
-- Разворачивание jsonb в rowsetSELECT js->’name’ FROM companyWHERE
EXISTS (SELECT 1FROM jsonb_array_elements(js->’competitions’) elWHERE el -> ’competitor’ ->> ’name’ = ’Wikia’
) AND EXISTS (SELECT 1FROM jsonb_array_elements(js->’offices’) elWHERE el ->> ’country_code’ = ’USA’
);
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 62 / 72
Задача #9
Найти компании, в которые финансоваяорганизация Trinity Ventures инвестировалав 2007 или 2008 году.Реализовать решение с помощью:
▶ оператора @>,▶ jsquery,▶ разворачивания jsonb в rowset.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 63 / 72
Задача #9: решение (1/3)
-- Оператор @># SELECT js->’name’ FROM company
WHEREjs @>’{”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
Задача #9: решение (2/3)
-- jsquery# SELECT js->’name’ FROM company
WHEREjs @@’funding_rounds.#(
funded_year IN (2007,2008) ANDinvestments.#.financial_org.name = ”Trinity Ventures”)’;
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 65 / 72
Задача #9: решение (3/3)
-- Разворачивание jsonb в rowset# SELECT js->’name’ FROM company
WHERE EXISTS (SELECT 1FROM jsonb_array_elements(js->’funding_rounds’) elWHERE el -> ’funded_year’ IN (’2007’,’2008’) AND EXISTS(SELECT 1FROM jsonb_array_elements(el -> ’investments’) el2WHERE el2 -> ’financial_org’ ->>
’name’ = ’Trinity Ventures’));
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 66 / 72
Задача #10
Найти компании, которые в 2008 годупривлекли более 100 000 000 USDинвестиций.Реализовать решение с помощью:
▶ jsquery,▶ разворачивания jsonb в rowset.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 67 / 72
Задача #10: решение (1/2)
-- jsquery# SELECT js -> ’name’
FROM companyWHERE js @@’funding_rounds.# (funded_year = 2008 AND
raised_currency_code = ”USD” ANDraised_amount > 100000000)’;
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 68 / 72
Задача #10: решение (2/2)
-- Разворачивание jsonb в rowset# SELECT js->’name’ FROM company
WHERE EXISTS (SELECT 1FROM jsonb_array_elements(js->’funding_rounds’) elWHEREel -> ’funded_year’ = ’2008’ ANDel ->> ’raised_currency_code’ = ’USD’ ANDel -> ’raised_amount’ > ’100000000’);
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 69 / 72
Задача #11
Найти компании, у которых суммарныйобъём инвестиций был более 1 000 000 000USD.Реализовать решение с помощьюразворачивания jsonb в rowset.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 70 / 72
Задача #11: решение
-- Разворачивание jsonb в rowsetSELECT js->’name’ FROM company
WHERE (SELECT SUM((el ->> ’raised_amount’)::numeric)FROM jsonb_array_elements(js->’funding_rounds’) elWHEREel ->> ’raised_currency_code’ = ’USD’) > 1000000000;
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 71 / 72
Спасибо за внимание!
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 72 / 72