ABONAMENTE VIDEO REDACȚIA
RO
EN
NOU
Numărul 148
Numărul 147 Numărul 146 Numărul 145 Numărul 144 Numărul 143 Numărul 142 Numărul 141 Numărul 140 Numărul 139 Numărul 138 Numărul 137 Numărul 136 Numărul 135 Numărul 134 Numărul 133 Numărul 132 Numărul 131 Numărul 130 Numărul 129 Numărul 128 Numărul 127 Numărul 126 Numărul 125 Numărul 124 Numărul 123 Numărul 122 Numărul 121 Numărul 120 Numărul 119 Numărul 118 Numărul 117 Numărul 116 Numărul 115 Numărul 114 Numărul 113 Numărul 112 Numărul 111 Numărul 110 Numărul 109 Numărul 108 Numărul 107 Numărul 106 Numărul 105 Numărul 104 Numărul 103 Numărul 102 Numărul 101 Numărul 100 Numărul 99 Numărul 98 Numărul 97 Numărul 96 Numărul 95 Numărul 94 Numărul 93 Numărul 92 Numărul 91 Numărul 90 Numărul 89 Numărul 88 Numărul 87 Numărul 86 Numărul 85 Numărul 84 Numărul 83 Numărul 82 Numărul 81 Numărul 80 Numărul 79 Numărul 78 Numărul 77 Numărul 76 Numărul 75 Numărul 74 Numărul 73 Numărul 72 Numărul 71 Numărul 70 Numărul 69 Numărul 68 Numărul 67 Numărul 66 Numărul 65 Numărul 64 Numărul 63 Numărul 62 Numărul 61 Numărul 60 Numărul 59 Numărul 58 Numărul 57 Numărul 56 Numărul 55 Numărul 54 Numărul 53 Numărul 52 Numărul 51 Numărul 50 Numărul 49 Numărul 48 Numărul 47 Numărul 46 Numărul 45 Numărul 44 Numărul 43 Numărul 42 Numărul 41 Numărul 40 Numărul 39 Numărul 38 Numărul 37 Numărul 36 Numărul 35 Numărul 34 Numărul 33 Numărul 32 Numărul 31 Numărul 30 Numărul 29 Numărul 28 Numărul 27 Numărul 26 Numărul 25 Numărul 24 Numărul 23 Numărul 22 Numărul 21 Numărul 20 Numărul 19 Numărul 18 Numărul 17 Numărul 16 Numărul 15 Numărul 14 Numărul 13 Numărul 12 Numărul 11 Numărul 10 Numărul 9 Numărul 8 Numărul 7 Numărul 6 Numărul 5 Numărul 4 Numărul 3 Numărul 2 Numărul 1
×
▼ LISTĂ EDIȚII ▼
Numărul 25
Abonament PDF

Suportul JSON în PostgreSQL

Raul Rene Lepsa
Java Devolper
@3Pillar Global



PROGRAMARE


Există o nevoie crescândă incontestabilă pentru flexibilitate și scalabilitate în ceea ce privește datele, acesta fiind și motivul pentru care mulți au apelat la baze de date NoSQL pe durata ultimilor ani. Există avantaje și dezavantaje în ceea ce privește folosirea lor, mai ales că acestea nu au fost destinate să înlocuiască bazele de date relaționale.

Dezvoltatorii și arhitecții soft au deseori dificultăți în a alege una sau alta, în special când formatul datelor ce urmează să fie folosite este necunoscut sau poate fi modificat ulterior. O soluție de compromis este folosirea atât a bazelor de date relaționale cât și a celor non-relaționale și crearea unui sistem de comunicare între ele. Însă această soluție poate ajunge să dea mai multe dureri de cap și probleme decât dacă s-ar folosi doar un sistem de baze de date.

De ce nu 2 în 1?

Companii precum IBM și Oracle au început să ofere metode prin care Sistemele de Baze de Date Relaționale (RDBMS) și cele non-relaționale să coexiste. PostgreSQL oferă o alternativă prin tipuri de date speciale, cu o structură mai liberă și flexibilă, care imită comportamentul NoSQL într-un RDBMS.

Începând cu versiunea 8.3, PostgreSQL a introdus tipul de date hstore, care este folositor pentru rânduri cu multe atribute care sunt rar examinate și date semi-structurate. Permite stocarea de perechi cheie-valoare (similar cu unele NoSQL-uri), suportă diferite operații și oferă funcții pentru manipularea lor. În versiunea 9.2 a fost introdus tipul de date JSON, căruia i s-au adus îmbunătățiri în ceea ce privește performanța în versiunea 9.3 beta. JSON (Javascript Object Notation) este un format lightweight, lizibil și independent de limbaj, pe care Postgres îl stochează sub formă de text.

De ce l-ai lua în considerare?

Aproape toți dezvoltatorii au sau au avut de-a face cu cerințe schimbătoare din partea clienților și au simțit nevoia de flexibilitate din partea sistemului de stocare a datelor în special când vine vorba de aplicații cu clienți multipli. În același timp, clienții simt deseori nevoia de câmpuri personalizate și cer flexibilitate. Dar ce e de făcut când unii clienți vor un câmp particular și alții vor patru, pentru aceeași funcționalitate? Sunt sigur că majoritatea ați văzut coloane precum customField1, customField2, customField3, anotherCustomField ș.a.m.d. Acestea pot fi evitate folosind un tablou (array), dar ce facem când avem de stocat perechi? Sau triplete? Dacă câmpul particular are atât o etichetă cât și o valoare? Sau chiar o dată asociată? Lucrurile devin mai complicate.

O altă problemă generală o reprezintă tratarea numelor. Există o listă de 40 de neadevăruri despre nume, și doar ca să vă faceți o idee, am enumerat câteva pe care dezvoltatorii în general omit să le ia în calcul:

Desigur, probabil nu avem de a face cu multe din aceste cazuri, dar nu poți fi niciodată sigur că sistemul nu va trebui pe viitor să suporte nume chinezești. Un mod tradițional de a stoca nume este o combinație de prenume, nume de familie și eventual al doilea prenume care este opțional. Însă dacă o persoană nu are prenume sau nume de familie, această implementare devine eronată.

O posibilă cale de a rezolva această problemă este prin folosirea unui JSON:

{
    "first_name": "Ronaldo",
    "mother_name": "de Assis",
    "last_name": "Moreira",
    "nicknames": [
        "Ronaldinho", 
        "Gaúcho"
    ]
}

Implementarea de mai sus poate adăuga muncă în plus prin schimbarea câmpului de full_name de fiecare dată când unul din restul câmpurilor este schimbat, dar acesta este doar un exemplu. În general, soluția este dependentă de aplicație și de clienții țintă ai acesteia, dar reprezentarea de mai sus oferă multă flexibilitate în comparație cu modul clasic de stocare a numelor din cadrul bazelor de date relaționale. Având rar nevoie de accesarea tuturor numelor, putem vedea utilitatea acestui tip de reprezentare. De ce am crea o coloană pentru numele mamei când avem rareori nevoie de el? De ce s-ar crea o coloană pentru poreclă sau nume de alint când unele entități ar putea avea mai mult de unul singur? Aceeași logică poate fi aplicată și celorlalte nume.

Funcții și Operatori

Cu toate că lista completă de funcții și operatori poate fi găsită în documentația celor de la Postgres, este important de menționat că un obiect JSON e accesat folosind operatorul -> (poate fi returnat și ca text folosind operatorul ->>). De exemplu, pentru o coloană nume, accesarea numelui de familie s-ar face folosind: names->>'last_name'. Operatorul prezentat anterior poate fi folosit și pentru accesarea unui element aflat la o anumită poziție într-un tablou: (names->'nicknames')->0 ar întoarce primul obiect din tabloul nicknames din cadrul coloanei.

Pe lângă operatori, începând cu versiunea 9.3 au fost adăugate și multiple funcții, pentru a ajuta dezvoltatorii în folosirea acestui tip de coloană, precum funcții pentru extragerea obiectelor dintr-un tablou de JSON, pentru transformarea unui rând într-un obiect JSON, pentru expandarea unui JSON într-un set de perechi cheie-valoare sau pentru conversia oricărui element într-un obiect JSON.

Avantajele tipului de coloană JSON

Primul avantaj ale acestui tip de coloană este caracterul de format "open standard", fiind independent de limbaj. În al doilea rând, facilitează tratarea cerințelor schimbătoare ale clienților prin scalabilitate și flexibilitate. Devine folositor când e nevoie de stocarea grafurilor de obiecte multi-nivel, deoarece oferă performanță ridicată și codul în sine este mai ușor de scris și de menținut decât în implementările obișnuite de grafe. Coloana JSON nu ocupă mult spațiu (e stocată ca și text) și permite stocarea de până la 1 GB de date într-o singură coloană. În plus, previne SQL injection în mod implicit, deoarece obiectele JSON sunt validate înainte de a fi persistate.

Cheile străine pot fi evitate prin denormalizarea datelor și câmpurile din cadrul interogărilor complexe pot fi accesate fără să fie nevoie de join cu alte tabele (posibil mari), oferind astfel un comportament similar cu NoSQL-uri într-un sistem de baze de date relațional.

Se poate dovedi a fi un plus în aplicațiile web, facilitând transportul și conversia datelor de pe client către controller-e, și apoi către nivelul de acces la date, stocând obiectele JSON venite dinspre client.

Aceste tipuri de date pot fi indexate, iar în plus indecși pot fi creați în cadrul obiectelor JSON (de exemplu, pentru tablouri din cadrul unui JSON). Indecșii curenți suportați atât de hstore cât și de coloanele JSON sunt aproape la fel de performanți ca și cei ai tipurilor de date standard. Dar se lucrează la noua generație de indecși GIN. Aceștia au fost deja implementați pentru hstore în versiunea în curs de dezvoltare 9.4. Comparațiile de performanță cu MongoDB arată că deși scanarea secvențială este aproape la fel la nivel de performanță, scanarea de indecși este mai rapidă decât în Mongo. Acești indecși vor fi cel mai probabil aplicați și tipului de date JSON începând cu versiunea 9.4.

Dezavantaje

Cel mai mare dezavantaj al tipului de date JSON este faptul că nu e portabil, fiind momentan suportat doar în PostgreSQL. Alte dezavantaje includ imposibilitatea de a adăuga chei străine și sintaxa ciudată, mai puțin lizibilă decât în cazul interogărilor obișnuite.

În plus, interogările care sunt simple pe tipuri de date comune devin complicate când se folosește tipul de date JSON, în special când avem de-a face cu tablouri de obiecte. Pentru a exemplifica acest aspect, considerați o tabelă de utilizatori users care stochează numerele de telefon ca și un tablou de obiecte JSON, precum este prezentat în Figura 1. Cu toate că acest tip de configurare arată mai bine decât crearea de coloane pentru fiecare tip de număr, precum home_number, work_number ș.a.m.d., interogarea tabelei pentru a aduce numerele de telefon de tip primary de exemplu este destul de complicată. Rezultatul interogării este afișat în Figura 2:

SELECT users.id, phone->>’number’ AS number 
FROM users INNER JOIN
(
  SELECT id, 
     json_array_elements(phones) 
     AS phone
     FROM users 
     WHERE id=users.id
) phones 
     ON phones.id = users.id
     WHERE phone->>’type’=’primary’

Figura 1

Figura 1 - Numere de telefon stocate ca tablou de obiecte JSON, fiecare având un tip și un număr.

Figura 2

Figura 2 - Numerele de tip ”primary” returnate de interogare

În general, nu putem fi siguri că numărul de telefon de tip primary se află pe prima poziție a tabloului, acest lucru fiind subiectiv și dependent de sistem. Cu toate acestea, impunerea unor astfel de constrângeri pot duce la interogări simplificate, având operatorul -> pentru a accesa direct elementul de pe poziția n a unui tablou. De asemenea, important de notat este că exemplul precedent prezintă un caz simplu de stocare de numere de telefon. Interogările devin cu atât mai complicate cu cât datele stocate în tablourile JSON sunt mai complexe, în special când apare nevoia de a face join acestor date din cadrul tabloului cu alte tabele.

Concluzii

A opta doar pentru un sistem de baze de date relațional sau pentru unul NoSQL nu reprezintă întotdeauna o soluție viabilă. Cu toate că se fac pași pentru a ușura integrarea acestor două tipuri de baze de date, cel puțin pentru moment acest lucru poate să se dovedească a fi o prea mare bătaie de cap.

PostgreSQL oferă o soluție de "compromis" prin suportul pentru coloane hstore care imită mediile de stocare cheie-valoare și pentru coloane de tip JSON, care permit stocarea de obiecte și tablouri JSON. În plus, oferă operatori și funcții pentru a facilita manipularea datelor, suportând în același timp și indecși atât pe date JSON cât și pe câmpuri din interiorul lor.

Dacă această soluție este sau nu optimă este o întrebare subiectivă, dependentă de sistem și de cerințele pe care acesta trebuie să le îndeplinească. Ceea ce este important de ținut minte este că dezvoltatorul are la dispoziție puterea și complexitatea sistemului de baze de date relațional Postgres, indiferent dacă alege sau nu să folosească tipul de date JSON.

Conferință TSM

NUMĂRUL 147 - Automotive

Sponsori

  • Accenture
  • BT Code Crafters
  • Accesa
  • Bosch
  • Betfair
  • MHP
  • BoatyardX
  • .msg systems
  • P3 group
  • Ing Hubs
  • Cognizant Softvision
  • Colors in projects