2015-02-12 13 views
19

Ich habe eine Tabelle in einer postgresql 9.4 Datenbank mit einem Jsonb-Feld namens Empfänger. Einige Beispiel-Reihen:Postgresql Abfrage Array von Objekten im JSONB-Feld

[{"id": "145119603", "name": "145119603", "type": 2}] 
[{"id": "1884595530", "name": "1884595530", "type": 1}] 
[{"id": "363058213", "name": "363058213", "type": 1}] 
[{"id": "1427965764", "name": "1427965764", "type": 1}] 
[{"id": "193623800", "name": "193623800", "type": 0}, {"id": "419955814", "name": "419955814", "type": 0}] 
[{"id": "624635532", "name": "624635532", "type": 0}, {"id": "1884595530", "name": "1884595530", "type": 1}] 
[{"id": "791712670", "name": "791712670", "type": 0}] 
[{"id": "895207852", "name": "895207852", "type": 0}] 
[{"id": "144695994", "name": "144695994", "type": 0}, {"id": "384217055", "name": "384217055", "type": 0}] 
[{"id": "1079725696", "name": "1079725696", "type": 0}] 

Ich habe eine Liste von Werten für id und wollen jede Zeile auszuwählen, die ein Objekt mit einem der Werte aus dieser Liste enthält, innerhalb des Arrays im jsonb Feld.

Ist das möglich? Gibt es einen GIN-Index, den ich beschleunigen kann?

Antwort

31

Es gibt keine einzige Operation, die Ihnen helfen können, aber Sie haben ein paar Optionen:

1. Wenn Sie eine kleine (und fest) Anzahl der IDs haben abzufragen, können Sie mehrere Eindämmung verwenden Operatoren @> kombiniert mit or; f.ex .:

where data @> '[{"id": "1884595530"}]' or data @> '[{"id": "791712670"}]' 

Ein einfacher gin Index können Sie auf Ihre Datenspalte hier helfen.

2. Wenn Sie variable Anzahl von IDs haben (oder haben Sie eine Menge von ihnen), können Sie json[b]_array_elements() verwenden, um jedes Element des Arrays zu extrahieren, eine ID-Liste aufzubauen und es dann abfragen, mit dem ANY Containment Operator ?|:

select * 
from jsonbtest 
where to_json(array(select jsonb_array_elements(data) ->> 'id'))::jsonb ?| 
     array['1884595530', '791712670']; 

Leider können Sie einen Ausdruck nicht Index, der eine Unterabfrage in ihm hat. Wenn Sie indizieren möchten es, müssen Sie eine Funktion für sie schaffen:

create function idlist_jsonb(jsonbtest) 
    returns jsonb 
    language sql 
    strict 
    immutable 
as $func$ 
    select to_json(array(select jsonb_array_elements($1.data) ->> 'id'))::jsonb 
$func$; 

create index on jsonbtest using gin (idlist_jsonb(jsonbtest)); 

Danach, Sie IDs wie folgt abfragen:

select *, jsonbtest.idlist_jsonb 
from jsonbtest 
where jsonbtest.idlist_jsonb ?| array['193623800', '895207852']; 

Hinweis: Ich dot notation/computed field hier verwendet, aber Du musst nicht.

3. Aber an dieser Stelle müssen Sie nicht bei json [b] bleiben: Sie haben ein einfaches Text-Array, das auch von PostgreSQL unterstützt wird.

create function idlist_array(jsonbtest) 
    returns text[] 
    language sql 
    strict 
    immutable 
as $func$ 
    select array(select jsonb_array_elements($1.data) ->> 'id') 
$func$; 

create index on jsonbtest using gin (idlist_array(jsonbtest)); 

und Abfrage dieses berechnete Feld mit dem Überlappungs Array Operator &&:

select *, jsonbtest.idlist_array 
from jsonbtest 
where jsonbtest.idlist_array && array['193623800', '895207852']; 

Hinweis: Aus meiner internen Tests, diese letztere Lösung mit einem höheren Kosten als die jsonb Variante berechnet wird, aber in der Tat ist es schneller, ein bisschen. Wenn Ihnen die Leistung wirklich wichtig ist, sollten Sie beide testen.

+1

Hier ist ein weiterer Ansatz: http://dba.stackexchange.com/questions/130699/postgresql-json-query-array-against-multiple-values ​​ – Chrizt0f

+0

@ Chrizt0f, die meine ist 1. Ansatz ("ANY" kann durch "ODER" Ausdrücke ausgedrückt werden). Normalerweise ist es schwierig, 'jsonb []' in Client-Anwendungen zu binden, aber wenn OP (oder jemand, der daran interessiert ist) dies tun kann, ist es auch eine gültige Lösung - aber beachten Sie, dass es den Index mehrmals verwendet Original-Note gilt immer noch (* Wenn Sie eine kleine (und feste) (sic!) Anzahl der IDs, die abgefragt werden sollen *) - aber mit 'ANY' ist es bequemer, eine nicht festgelegte Anzahl von IDs OFC zu liefern. – pozs

4

Ich finde Abhilfe:
where data::text similar to '%("id": "145119603"|"id": "1884595530")%'

+0

obwohl es eine Volltextsuche pro Scan verursacht, ist dies ordentlich – AnthonyJClink