Pg jsonb format 16-9

54
BreizhCamp 2015 #BzhCmp #pgnosql Not only SQL avec PostgreSQL Pierre-Alban DEWITTE - @__pad__

Transcript of Pg jsonb format 16-9

Page 1: Pg jsonb format 16-9

BreizhCamp 2015

#BzhCmp

#pgnosql

Not only SQL avec

PostgreSQL

Pierre-Alban DEWITTE - @__pad__

Page 2: Pg jsonb format 16-9

dev.myscript.com

Page 3: Pg jsonb format 16-9

TROLL

Source : http://www.enterprisedb.com/nosql-for-enterprise

JSONB et Postgresql

Page 4: Pg jsonb format 16-9

Histoire du type JSONB

●2006 – HSTORE – Stockage clef/valeur

●2012 – JSON - JavaScript Object Notation

●2014 – JSONB – JSON Binaire

Introduit par Oleg Bartunov, Teodor Sigaev, Peter Geoghegan and Andrew Dunstan dans Postgresql 9.4

Page 5: Pg jsonb format 16-9

Caractéristiques du type JSONB

• Permet de stocker un document JSON synthaxiquement valide

• Stocker en interne sous une forme binaire permettant l’indexation

En comparaison du format JSON• Léger surcout à l’insertion• Attention, contrairement à un chaine de

charactère, l’ordre des objets n’est pas conservé

• Les attributs avec le même nom ne sont pas conservés

Page 6: Pg jsonb format 16-9

Modélisation

Page 7: Pg jsonb format 16-9

Possibilités offertes

• Ajouter simplement de la flexibilité au schéma

• Modéliser les données en fonction des use cases

• Regroupement dans une même entités l’ensemble des informations constituant sont identité

Page 8: Pg jsonb format 16-9

Application de démo

●Utilisation des données MovieLens http://grouplens.org/datasets/movielens/

Page 9: Pg jsonb format 16-9

Modèle Conceptuel de Données

MovieInteger idString yearString titleInteger imdbIdInteger tmdbId

RatingsInteger userIdInteger movieIdFloat ratingDate date

0.. n

1

TagsInteger userIdInteger movieIdString tagDate date

1

0.. n

Page 10: Pg jsonb format 16-9

Modèle orienté document{ "id" : 0, //Integer "year" : "...", //String "title" : "...", //String "imdbId" : "...", //Integer "tmdbId" : "...", //Integer "tags" : [{ //Array of tags "userId" : 0, //Integer "tag" : "...", //String "date" : "..." //String }], "ratings" : [{ //Array of ratings "userId" : 0, //Integer "rating" : "...", //String "date" : "..." //String }]}

Page 11: Pg jsonb format 16-9

Modèle hybride – PGsql 9.4

MovieInteger idString yearString titleInteger imdbIdInteger tmdbIdJsonb otherInformations

{ "tags" : [{ "userId" : 0, "tag" : "...", "date" : "..." }], "ratings" : [{ "userId" : 0, "rating" : "...", "date" : "..." }]}

Page 12: Pg jsonb format 16-9

Créer le schéma

Page 13: Pg jsonb format 16-9

Colonne JSONBCREATE TABLE movies( id integer primary key, name character varying(200), year character varying(4), imdbid integer, tmdbid integer, otherInformations jsonb);

ALTER TABLE books ADD COLUMN "otherInformations" jsonb;

Page 14: Pg jsonb format 16-9

Insérer un enregistrement

INSERT INTO movies(id, name, year, imdbid, tmdbid,

otherInformations)VALUES (666666, 'Diabolic movie', 1966, 666, 6666, '{"ratings" : [

{"userId" : 4,"rating" : 3},{"userId" : 5,"rating" : 5},

]}');

• Insertion des champs JSON dans un chaine de charactère.

• Validation synthaxique effectuée par le moteur

Page 15: Pg jsonb format 16-9

Insérer un enregistrement

●Nombreuses fonctions permettant de convertir un enregistrement en jsonb

●Et vis-versa

Page 16: Pg jsonb format 16-9

Construire un champ JSON

SELECT row_to_json(movies.*) FROM movies LIMIT 1

-- Résultats{"id":370,"name":"Naked Gun 33 1/3: The Final Insult ","year":"1994","imdbid":110622,"tmdbid":36593,"otherinformations":{"tags": [{"tag": "foqam", "date": 1139200469000, "userId": 14260}, {"tag": "Leslie Neilsen", "date": 1158438794000, "userId": 18390}, {"tag": "nazi", "date": 1161646656000, "userId": 18390}, {"tag": "sequel", "date": 1354555994000, "userId": 42640}, {"tag": "Comedy", "date": 1248035546000, "userId": 60710}, {"tag": "parody", "date": 1248035543000, "userId": 60710}, {"tag": "Comedy", "date": 116291114800

Page 17: Pg jsonb format 16-9

Fonctions de construction JSON

●to_json : convertit une valeur ●row_to_json : convertit un tuple●json_build_array : convertit un tableau●json_build_object : construit un object

JSON depuis un “varidic”●json_object : construit à partir d’une

chaine●array_to_json : convertit un tableau

Page 18: Pg jsonb format 16-9

Requete

Requêtes

Page 19: Pg jsonb format 16-9

Lecture brute d'un champ

-- Lecture brute d’un champ JSONSELECT otherinformationsFROM moviesWHERE otherinformations IS NOT NULLLIMIT 2

-- Résultats{"tags": [{"tag": "foqam", "date": 1139200469000, "userId": 14260}, {"tag": "Leslie Neilsen", "date": 1158438794000, "userId": 18390}, {"tag": "nazi", "date": 1161646656000, "userId": 18390}, {"tag": "sequel", "date": 1354555994000, "userId": 42640}, {"tag": "Comedy", "date": 1248035546000, "userId": 60710}, {"tag": "parody", "date": 1248035543000, "userId": 60710}, {"tag": "Comedy", "date": 1162911148000, "userId": 63500}, {"tag": "Slapstick crap", "date": 1162911074000, "userId": 63500}, {"tag": "Arma nua", "date": 1162374098000, "userId": 105530}, {"tag": "Comedy", "date":

Page 20: Pg jsonb format 16-9

Ce n’est pas du texte

-- Requête avec likeSELECT otherinformationsFROM moviesWHERE otherinformations LIKE '%tags%'LIMIT 2

//RésultatsCould not execute SQL statement. ERROR: operator does not exist: jsonb ~~ unknown Indice : No operator matches the given name and argument type(s). You might need to add explicit type casts.

Page 21: Pg jsonb format 16-9

Ce n’est pas du texte

-- Requête avec like et castSELECT otherinformationsFROM moviesWHERE otherinformations::text LIKE '%tags%'LIMIT 2

-- Résultats{"tags": [{"tag": "foqam", "date": 1139200469000, "userId": 14260}, {"tag": "Leslie Neilsen", "date": 1158438794000, "userId": 18390}, {"tag": "nazi", "date": 1161646656000, "userId": 18390}, {"tag": "sequel", "date": 1354555994000, "userId": 42640}, {"tag": "Comedy", "date": 1248035546000, "userId": 60710}, {"tag": "parody", "date": 1248035543000, "userId": 60710}, {"tag": "Comedy", "date": 1162911148000, "userId": 63500}, {"tag": "Slapstick crap", "date": 1162911074000, "userId": 63500}, {"tag": "Arma nua", "date": 1162374098000, "userId": 105530}, {"tag": "Comedy", "date":

Page 22: Pg jsonb format 16-9

Modèle orienté document{ "id" : 0, //Integer "year" : "...", //String "title" : "...", //String "imdbId" : "...", //Integer "tmdbId" : "...", //Integer "otherInformations" : {

"averageRating" : "...", "nbOfRatings" : "...", "tags" : [{ //Array of tags "userId" : 0, //Integer "tag" : "...", //String "date" : "..." //String }], "ratings" : [{ //Array of ratings "userId" : 0, //Integer "rating" : "...", //String "date" : "..." //String }]}

Page 23: Pg jsonb format 16-9

Lecture d’un champ

--Lecture d'un champ SELECT otherinformations->'averageRating' FROM movies WHERE (otherinformations->>'averageRating')::float > 4

-- Résultats4.002001000500254.0816203143893594.0895721925133694.2161100196463654.1267409470752094.078947368421052

Page 24: Pg jsonb format 16-9

Lecture d’un tableau (texte)

-- Lecture texte d'un élément de tableauSELECT otherinformations::jsonb->'tags'->>3 FROM movies LIMIT 2

-- Résultats{"tag": "sequel", "date": 1354555994000, "userId": 42640}{"tag": "Nudity (Full Frontal)", "date": 1298520169000, "userId": 33860}

Page 25: Pg jsonb format 16-9

Lecture d’un tableau (JSON)

-- Lecture JSON d'un élément de tableauSELECT otherinformations::jsonb->'tags'->3->'tag' FROM movies LIMIT 2

-- Résultats"sequel""Nudity (Full Frontal)"

Page 26: Pg jsonb format 16-9

Tester l’existence d’un champ-- Tester l’existence d’un champSELECT count(*) FROM movies WHERE otherinformations ? 'nbOfRatings'

-- Résultats1715

Page 27: Pg jsonb format 16-9

Recherche JSON

-- Recherche JSONSELECT id, otherinformations->'nbOfRatings' FROM movies WHERE otherinformations @> '{"nbOfRatings" : 50}'::jsonb

-- Résultats1180 504000 508690 50

Page 28: Pg jsonb format 16-9

Lecture avec chemin-- Lecture avec chemin en pseudo JSON-- Recherche du champ tag du cinquième élément du tableau tags-- Equivalent jsonb_extract_path et jsonb_extract_path_text

SELECT otherinformations #> '{tags, 5, tag}'FROM moviesLIMIT 2

-- Résultats"parody""Monty Python"

Page 29: Pg jsonb format 16-9

Fonctions de manipulation JSON●jsonb_array_length : nb

d’éléments d’un tableau JSONB●jsonb_each &

jsonb_each_text : transforme chaque clé valeur en une ligne

●jsonb_object_keys : liste des clefs d’un object json

Page 30: Pg jsonb format 16-9

Fonctions de manipulation JSON●jsonb_array_elements &

jsonb_array_elements_text : transform un tableau JSON en tuple d’objets

●jsonb_typeof : indique le type parmi object, array, string, number, boolean et null.

Page 31: Pg jsonb format 16-9

Fonctions de manipulation JSON●jsonb_populate_record &

jsonb_populate_recordset : construit implicitement un tuple

●json_to_record & json_to_recordset : construit explicitement un tuple

Page 32: Pg jsonb format 16-9

Démo

Page 33: Pg jsonb format 16-9

Et les indexes ?

Page 34: Pg jsonb format 16-9

Et les indexes ?

-- Création d'un index sur l'ensemble du documentCREATE INDEX IDX_GIN_otherInformations ON movies USING GIN (otherInformations);

Un index gin améliore les requêtes suivantes : • JSON @> JSON is a subset• JSON ? TEXT contains a value• JSON ?& TEXT[] contains all the values• JSON ?| TEXT[] contains at least one

value

Page 35: Pg jsonb format 16-9

Et les indexes ?

-- Création d'un index sur l'ensemble du documentCREATE INDEX IDX_GIN_otherInformations_optON movies USING GIN (otherInformations jsonb_path_ops);

Paramètre jsonb_path_ops optimise uniquement les recherches avec @>

Page 36: Pg jsonb format 16-9

Démo

Page 37: Pg jsonb format 16-9

Et en JAVA ?

Page 38: Pg jsonb format 16-9

JDBC

●Un champ JSONB se manipule comme un DBObject ou comme du TEXT

●Pas de particularité dans le driver JDBC PostgreSQL

●Nécessité d’utiliser un mapper objet / JSON

Page 39: Pg jsonb format 16-9

Démo

Page 40: Pg jsonb format 16-9

DAO Insert//Using Jackson to convert additional field to JSONString ratingsAsString = new ObjectMapper().writeValueAsString(overallRating);… pstSetter = new PreparedStatementSetter() {public void setValues(PreparedStatement preparedStatement) throws SQLException { //Using a PGObject to store otherInformations PGobject dataObject = new PGobject(); dataObject.setType("jsonb"); dataObject.setValue(ratingsAsString); preparedStatement.setObject(1, dataObject); … }};jdbcTemplateObject.update("UPDATE movies SET otherInformations = ? WHERE id = ?", pstSetter);

Page 41: Pg jsonb format 16-9

DAO Select

jdbcTemplateObject.query("SELECT id, otherinformations::text as text_otherinformations FROM movies WHERE otherinformations is not null limit 2", new RowCallbackHandler() {

public void processRow(ResultSet resultSet) { while (resultSet.next()) { … movie.setId(resultSet.getLong("id")); … movie.setOther( mapper.readValue( resultSet.getString("text_otherinformations"), OtherInformations.class)); …}}});

Page 42: Pg jsonb format 16-9

Avec un ORM

https://github.com/pires/hibernate-postgres-jsonb

Page 43: Pg jsonb format 16-9

Update

Page 44: Pg jsonb format 16-9

Et un update ?

●Postgresql 9.4 ne possède pas nativement d’opérateur permettant de manipuler un champ json

●Nécessité de lire / modifier / updater

Page 45: Pg jsonb format 16-9

Manipulation avec plv8

CREATE OR REPLACE FUNCTION json_data_update(data json, field text,value text)RETURNS jsonbLANGUAGE plv8 STABLE STRICT AS $$ var data = data; var val = value; data[field] = val; return JSON.stringify(data); $$;

Page 46: Pg jsonb format 16-9

Jsonbx

●Extension jsonbx en cours de portage dans PG 9.5

-- Update avec chemin en pseudo JSON

UPDATE movies

SET otherinformations = jsonb_replace(otherinformations, '{"tags",3,"tag"}', '{"SUPER"}'::jsonb)WHERE id = 1

Page 47: Pg jsonb format 16-9

Nouveautés PGSql 9.5

-- Add values to a jsonb object:SELECT '{"name": "Joe", "age": 30}'::jsonb || '{"town": "London"}'::jsonb;

-- Replace SELECT '{"town": "Dataville", "population": 4096}'::jsonb || '{"population": 8192}'::jsonb;

-- Résultats{"age": 30, "name": "Joe", "town": "London"}

{"town": "Dataville", "population": 8192}

Page 48: Pg jsonb format 16-9

Nouveautés PGSql 9.5

-- Remove a key SELECT '{"name": "James", "email": "james@localhost"}'::jsonb - 'email';

-- Résultats{"name": "James"}

Page 49: Pg jsonb format 16-9

PG vs MongoDB

Page 50: Pg jsonb format 16-9

PG vs MongoDB

PG 9.4 répond aux besoins en lecturePuissance du SQL sous réserve de bien maitriser les fonctions de convertion

PG ne permet pas simplement de mettre à jour un object JSON

Page 51: Pg jsonb format 16-9

PG vs MongoDB ?

Sharding plus facile Haute disponibilité

Page 52: Pg jsonb format 16-9

Suivre l’actualité

http://postgresweekly.com/

http://www.craigkerstiens.com/

https://planet.postgresql.org/

http://blog.2ndquadrant.com/

@tapoueh@petervgeoghegan@g_lelarge@EnterpriseDB@craigkerstiens

Page 53: Pg jsonb format 16-9

Questions

#pgnosql

@__pad__https://github.com/padewitte/bzhcamp_pgjson

Page 54: Pg jsonb format 16-9

SourcesRequete :• http://www.postgresql.org/docs/9.4/static/functions-json.html• http://adpgtech.blogspot.fr/2015/05/new-jsonb-features-for-95.htmlIndex• http

://blog.2ndquadrant.com/jsonb-type-performance-postgresql-9-4/JSONB et Postgresql• http://blog.2ndquadrant.com/postgresql-anti-patterns-unnecessary-

jsonhstore-dynamic-columns/

• http://www.pgcon.org/2014/schedule/attachments/313_xml-hstore-json.pdf

• https://www.compose.io/articles/is-postgresql-your-next-json-database/