ARCHIVE FILE
Databaseløsninger for en verden i endring
NoSQL-databaser som MongoDB og CouchDB er i vinden, men etterhvert er det mange som spør seg om de egentlig har gjort det riktige valget når de har valgt en databasestruktur som gjør det vanskelig å koble tabeller sammen med hverandre.
I databaseverdenen bruker vi et par uttrykk som jeg kort vil forklare, normalisering og denormalisering. Å normalisere en database betyr i korte trekk å organisere databasen slik at du unngår å lagre data to ganger. Hvis jeg for eksempel vil ta i mot brukernavn og brukerdetaljer, kan jeg velge å lagre alt sammen i en enkelt tabell, eller jeg kan velge å lagre brukernavn i en tabell, og detaljer i en annen. Det første kalles denormalisering, mens det siste er normalisering. En fordel med å normalisere er at hver gang jeg skal hente ut navnet til brukeren så kan jeg henvise til brukernavntabellen. En ulempe er at jeg må kjøre mer komplekse spørringer hver gang jeg skal ha et brukernavn.
Moderne denormaliserte databaseløsninger av denne typen går under samlebegrepet NoSQL. Databaser er samlinger, og tabeller er dokumenter. Denne typen organisering er ikke ny. Faktisk var dette den vanlige måten å organisere databaser på før SQL ble introdusert. Forskjellen er at de nye NoSQL-databasen er bygget for distribuering av data over nettet. Det har vist seg å være den smidigste måten å betjene en global brukermasse som måles i millioner i stedet for hundretall.
NoSQL i seg selv er likevel ikke den endelige løsningen. Problemet med denormalisering er fortsatt til stede. For å illustrere problemet, så vil jeg trekke fram et eksempel fra min egen skuff. Jeg lagde for en tid tilbake et API for nyheter. Det tar aktuelle nyhetssaker fra alle kilder i Norge og samler disse i et dokument kalt «nyheter.» Hvert felt lagrer all data om saken. Det tar for eksempel ingen verdens tid å hente opp alle saker fra siste år, saker om et spesielt tema, eller saker fra en spesiell kilde.
Problemet duppet opp samtidig som når behovet for å analysere sakene oppsto. Jeg ville sammenligne sakene og finne ut hvilke som skrev om det samme. Når Aftenposten skriver «Bil dundret inn i hus i Sør-Odal» og VG skriver «Bil krasjet inn i hus i Hedmark», så vil jeg at systemet ikke bare skal finne ut at dette er samme sak, men også lagre relasjonene mellom disse.
Men man kan jo ikke lagre relasjoner i en NoSQL-struktur. Det jeg endte opp med var å opprette en ny tabell der begge sakene ble lagret, sammen med kildenavn, tittel osv. Kunne jeg gjort det på en bedre måte?
Svaret er selvfølgelig ja. Menneskene bak databasen PostgresSQL har ikke tvinnet på tommeltottene mens NoSQL har vunnet popularitet. De har laget en måte å ta i bruk NoSQL innenfor rammene til en relasjondatabase.
Datafeltet kalles json. Her et eksempel på hvordan du oppretter et en tabell med et json-felt og hvordan du søker.
CREATE TABLE saker ( id integer, data json );
INSERT INTO saker VALUES (1,
'{ "tittel": "Bil dundret inn i hus i Sør-Odal",
"forfatter": { "kilde": "aftenposten",
"dato": "2014-05-02 10:00:00" } }');
INSERT INTO saker VALUES (2,
'{ "tittel": "Bil krasjet inn i hus i Hedmark",
"forfatter": { "kilde": "vg",
"dato": "2014-05-02 08:00:00" } }');
INSERT INTO saker VALUES (3,
'{ "tittel": "Bob Hoskins: Den følsomme buldrebassen",
"forfatter": { "kilde": "vg", "dato":
"2014-05-02 11:00:00" } }');
SELECT id, data->>'tittel' AS tittel FROM saker;
id | tittel
----+----------------------------------------
1 | Bil dundret inn i hus i Sør-Odal
2 | Bil krasjet inn i hus i Hedmark
3 | Bob Hoskins: Den følsomme buldrebassen
(3 rows)
Operatorene du kan bruker er (->) som returnerer det originale json-feltet, eller (->>) som returner en tekststreng.
Du kan også nøste søket ved å skrive følgende:
SELECT id, data->>'tittel' as tittel,
data->'forfatter'->>'kilde' AS kilde FROM saker;
id | tittel | kilde
----+----------------------------------+-------------
1 | Bil dundret inn i hus i Sør-Odal | aftenposten
2 | Bil krasjet inn i hus i Hedmark | vg
(2 rows)
Du kan filtrere:
SELECT id, data->>'tittel' as tittel,
data->'forfatter'->>'kilde' AS kilde
FROM saker WHERE data->>'tittel' LIKE '%Hoskins%';
id | tittel | kilde
----+----------------------------------------+-------
3 | Bob Hoskins: Den følsomme buldrebassen | vg
(1 row)
Hva med det opprinnelige spørsmålet over? Jeg kan opprette en tabell som samler alle saker som jeg har identifisert som like:
CREATE TABLE felles_saker
( fk_id integer, fk_id_felles json );
INSERT INTO felles_saker VALUES (1, 2);
INSERT INTO felles_saker VALUES (2, 1);
For å finne ut hvilke saker som er lik saken med ID 2, kan jeg ganske enkelt kjøre følgende spørring:
SELECT
s.id, s.data->>'tittel' as tittel, s.data->'forfatter'->>'kilde' AS kilde
FROM
saker s
WHERE s.id IN(
SELECT f.fk_id FROM felles_saker f
WHERE f.fk_id_felles=2
);
id | tittel | kilde
----+----------------------------------+-------------
1 | Bil dundret inn i hus i Sør-Odal | aftenposten
(1 row)
Postgres støtter en rekke andre json-operatorer og funksjoner jeg ikke har nevnt. Ta en titt på den offisielle dokumentasjonen for et dypere innblikk i hva som tilbys.