2016-10-18 3 views
2

У нас есть таблица с jsonb поля с содержанием, как это:Индексации вложенная с Postgres JSON

{"terms": { 
    "foo": { 
    "uri": "foo" 
    }, 
    "bar": { 
    "uri": "bar" 
    } 
} 
} 

Мы хотели бы создать индекс, чтобы мы могли быстро найти записи по uri. Как мы его создаем? Помогло бы, если бы terms был массивом вместо объекта?

ответ

2

Это делает то, что вы хотите, но может не быть достаточно общим, я все равно отправлю его.

CREATE TABLE public.jin2 (
    id BIGINT 
    ,d JSONB 
    ,urls text[] 
); 

CREATE OR REPLACE FUNCTION public.extract_urls() RETURNS TRIGGER AS $$ 
    BEGIN 
     with data(json_value) as (
     values (NEW.d) 
    ) 
     select ARRAY_AGG(j2.value) 
     from data, 
     lateral jsonb_each(json_value->'terms') j1, 
     lateral jsonb_each_text(value) j2 
     where j2.key = 'uri' into NEW.urls; 
     RETURN NEW; 
    END; 
$$ LANGUAGE plpgsql; 

CREATE TRIGGER extract_trigger BEFORE INSERT OR UPDATE ON public.jin2 FOR EACH ROW EXECUTE PROCEDURE extract_urls(); 

CREATE INDEX idx_test on public.jin2 USING GIN (urls); 

INSERT INTO public.jin2(id,d) VALUES (1,'{"terms": { 
    "foo": { 
    "uri": "foo" 
    }, 
    "bar": { 
    "uri": "bar" 
    } 
} 
}'::jsonb); 

INSERT INTO public.jin2(id,d) VALUES (2,'{"terms": { 
    "foo": { 
    "uri": "foo2" 
    }, 
    "bar": { 
    "uri": "bar" 
    } 
} 
}'::jsonb); 

SET enable_seqscan TO off; 
EXPLAIN select * from public.jin2 where urls @> ARRAY['foo'] 

Bitmap Heap Scan on jin2 (cost=8.00..12.01 rows=1 width=72) 
    Recheck Cond: (urls @> '{foo}'::text[]) 
    -> Bitmap Index Scan on idx_test (cost=0.00..8.00 rows=1 width=0) 
     Index Cond: (urls @> '{foo}'::text[])" 

С помощью Collect Recursive JSON Keys In Postgres я пришел с этим

Обновленный триггер будет найти ключи uri на любой глубине и извлекать значения в urls колонке для индексации (если значение uri ключей является массивом он ставит только первый элемент (исправить требуемый))

CREATE OR REPLACE FUNCTION public.extract_urls() RETURNS TRIGGER AS $$ 
    BEGIN 

     WITH RECURSIVE doc_key_and_value_recursive(key, value) AS (
     SELECT 
      t.key, 
      t.value 
     FROM jsonb_each(NEW.d) AS t 

     UNION ALL 

     SELECT 
      t.key, 
      t.value 
     FROM doc_key_and_value_recursive, 
      jsonb_each(CASE 
        WHEN jsonb_typeof(doc_key_and_value_recursive.value) <> 'object' THEN '{}' :: JSONB 
        ELSE doc_key_and_value_recursive.value 
        END) AS t 
    ) 
     SELECT ARRAY_AGG(value->>0) 
     FROM doc_key_and_value_recursive 
     WHERE jsonb_typeof(doc_key_and_value_recursive.value) NOT IN ('array', 'object') 
      and key = 'uri' into NEW.urls; 

     RETURN NEW; 
    END; 
$$ LANGUAGE plpgsql; 

INSERT INTO public.jin2(id,d) VALUES (104,'{"terms": { 
    "foo": { 
    "uri": "foo" 
    ,"other":"buzz" 
    }, 
    "bar": { 
    "uri": "bar" 
    ,"deeper": { 
     "uri": "bar2" 
    } 
    } 
    ,"uri": "bar3" 
    ,"and": {"uri": ["bar3","bar4"]} 
} 
}'::jsonb); 
select urls from public.jin2 where id=104; 

Результат

"{bar3,bar,foo,bar2}" 
Смежные вопросы