jsonb i Postgres 9.4+
Den binära JSON-datatypen jsonb förbättrar till stor del indexalternativ. Du kan nu ha ett GIN-index på en jsonb array direkt:
CREATE TABLE tracks (id serial, artists jsonb); -- !
CREATE INDEX tracks_artists_gin_idx ON tracks USING gin (artists);
Inget behov av en funktion för att konvertera arrayen. Detta skulle stödja en fråga:
SELECT * FROM tracks WHERE artists @> '[{"name": "The Dirty Heads"}]';
@> är jsonb "innehåller" operator, som kan använda GIN-index. (Inte för json , endast jsonb !)
Eller du använder den mer specialiserade, icke-standardiserade GIN-operatörsklassen jsonb_path_ops för indexet:
CREATE INDEX tracks_artists_gin_idx ON tracks
USING gin (artists jsonb_path_ops); -- !
Samma fråga.
För närvarande jsonb_path_ops stöder endast @> operatör. Men det är vanligtvis mycket mindre och snabbare. Det finns fler indexalternativ, detaljer i manualen .
Om kolumnen artists innehåller bara namn som visas i exemplet, det skulle vara mer effektivt att lagra bara värdena som JSON-text primitiver och den redundanta nyckeln kan vara kolumnnamnet.
Notera skillnaden mellan JSON-objekt och primitiva typer:
- Använda index i json-arrayen i PostgreSQL
CREATE TABLE tracks (id serial, artistnames jsonb);
INSERT INTO tracks VALUES (2, '["The Dirty Heads", "Louis Richards"]');
CREATE INDEX tracks_artistnames_gin_idx ON tracks USING gin (artistnames); Fråga:
SELECT * FROM tracks WHERE artistnames ? 'The Dirty Heads';
? fungerar inte för objektets värden , bara nycklar och matriselement .
Eller:
CREATE INDEX tracks_artistnames_gin_idx ON tracks
USING gin (artistnames jsonb_path_ops);
Fråga:
SELECT * FROM tracks WHERE artistnames @> '"The Dirty Heads"'::jsonb;
Mer effektivt om namn är mycket duplicerade.
json i Postgres 9.3+
Detta bör fungera med en IMMUTABLE funktion :
CREATE OR REPLACE FUNCTION json2arr(_j json, _key text)
RETURNS text[] LANGUAGE sql IMMUTABLE AS
'SELECT ARRAY(SELECT elem->>_key FROM json_array_elements(_j) elem)';
Skapa detta funktionella index :
CREATE INDEX tracks_artists_gin_idx ON tracks
USING gin (json2arr(artists, 'name'));
Och använd en fråga så här. Uttrycket i WHERE satsen måste matcha den i indexet:
SELECT * FROM tracks
WHERE '{"The Dirty Heads"}'::text[] <@ (json2arr(artists, 'name'));
Uppdaterad med feedback i kommentarer. Vi måste använda matrisoperatorer för att stödja GIN-index.
Operatorn "innehålls av" <@ i det här fallet.
Anmärkningar om funktionsvolatilitet
Du kan deklarera din funktion IMMUTABLE även om json_array_elements() är inte var det inte.
De flesta JSON funktioner brukade bara vara STABLE , inte IMMUTABLE . Det fanns en diskussion på hackerlistan för att ändra på det. De flesta är IMMUTABLE nu. Kolla med:
SELECT p.proname, p.provolatile
FROM pg_proc p
JOIN pg_namespace n ON n.oid = p.pronamespace
WHERE n.nspname = 'pg_catalog'
AND p.proname ~~* '%json%';
Funktionella index fungerar bara med IMMUTABLE funktioner.