jsonb в Postgres 9.4+
С новым двоичным типом данных JSON в jsonbPostgres 9.4 были значительно улучшены параметры индексации . Теперь вы можете иметь индекс GIN jsonbнепосредственно в массиве:
CREATE TABLE tracks (id serial, artists jsonb);
CREATE INDEX tracks_artists_gin_idx ON tracks USING gin (artists);
Нет необходимости в функции для преобразования массива. Это поддержит запрос:
SELECT * FROM tracks WHERE artists @> '[{"name": "The Dirty Heads"}]';
@>является новым jsonbоператором "содержит" , который может использовать индекс GIN. (Не для типа json, только jsonb!)
Или вы используете jsonb_path_opsдля индекса более специализированный класс операторов GIN, отличный от стандартного:
CREATE INDEX tracks_artists_gin_idx ON tracks
USING gin (artists jsonb_path_ops);
Тот же запрос.
На данный момент jsonb_path_opsподдерживает только @>оператор. Но обычно он намного меньше и быстрее. Есть еще варианты индекса, подробности в инструкции .
Если artists хранятся только имена, показанные в примере, было бы более эффективно сохранить для начала менее избыточное значение JSON: в имени столбца могут быть только значения в виде текстовых примитивов и избыточный ключ .
Обратите внимание на разницу между объектами JSON и примитивными типами:
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);
Запрос:
SELECT * FROM tracks WHERE artistnames ? 'The Dirty Heads';
?не работает для значений объекта , только для ключей и элементов массива .
Или (более эффективно, если имена повторяются часто):
CREATE INDEX tracks_artistnames_gin_idx ON tracks
USING gin (artistnames jsonb_path_ops);
Запрос:
SELECT * FROM tracks WHERE artistnames @> '"The Dirty Heads"'::jsonb;
json в Postgres 9.3+
Это должно работать с IMMUTABLE функцией :
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)';
Создайте этот функциональный индекс :
CREATE INDEX tracks_artists_gin_idx ON tracks
USING gin (json2arr(artists, 'name'));
И используйте такой запрос . Выражение в WHEREпредложении должно совпадать с выражением в индексе:
SELECT * FROM tracks
WHERE '{"The Dirty Heads"}'::text[] <@ (json2arr(artists, 'name'));
Обновлено с учетом отзывов в комментариях. Нам нужно использовать операторы массива для поддержки индекса GIN.
В этом случае оператор "содержится в"<@ .
Примечания по изменчивости функций
Вы можете объявить свою функцию, IMMUTABLEдаже если json_array_elements() это не так.
Большинство JSONфункций раньше были только STABLE, а не IMMUTABLE. В списке хакеров было обсуждение, чтобы это изменить. Большинство IMMUTABLEсейчас. Проверить с:
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%';
Функциональные индексы работают только с IMMUTABLEфункциями.