PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Aggiornamento dell'array JSONB per un elemento specifico

PostgreSQL 11+

Se sei già in PostgreSQL v. 11 (a causa di nuovo JSONB tipo di supporto per la conversione ) la soluzione migliore sarebbe probabilmente una funzione personalizzata scritta in Perl o Python.

Poiché preferisco Python 3, ecco un esempio funzionale:

CREATE OR REPLACE FUNCTION jsonb_replace_in_array (val jsonb, path_to_array text[], replacement jsonb, entry_filters jsonb)
    RETURNS jsonb
    TRANSFORM FOR TYPE jsonb
    LANGUAGE plpython3u
AS $$
v_new = val
tmp = v_new
for e in path_to_array:
    tmp = tmp[e]

for item in tmp:
    if (entry_filters is None or entry_filters.items() <= item.items()):
        item.update(replacement)

return v_new
$$;

...che quindi può essere utilizzato come segue:

UPDATE configuration
SET
  config = jsonb_replace_in_array(
    config,
    '{data}',
    '{"value":"changed"}'::jsonb,
    '{"oid":"11.5.15.1.4","instance":"1.1.4"}'::jsonb
  )
WHERE config->'data' @> '[{"oid":"11.5.15.1.4","instance":"1.1.4"}]';

Quindi sì, la condizione è duplicata, ma solo per limitare la quantità di righe da toccare in primo luogo.

Per funzionare effettivamente con una semplice installazione di PostgreSQL 11, hai bisogno delle estensioni plpython3u e jsonb_plpython3u :

CREATE EXTENSION plpython3u;
CREATE EXTENSION jsonb_plpython3u;

La logica Python spiegata:

for e in path_to_array:
    tmp = tmp[e]

...ci porta alla serie di voci che dobbiamo esaminare.

for item in tmp:
    if (entry_filters is None or entry_filters.items() <= item.items()):
        item.update(replacement)

...per ogni elemento nell'array, controlliamo se il criterio del filtro è null (entry_filters is None =corrisponde a qualsiasi voce) o se la voce "contiene" l'esempio fornito, inclusi chiavi e valori (entry_filters.items() <= item.items() ).

Se en entry corrisponde, sovrascrivi/aggiungi contenuto con la sostituzione fornita.

Spero che vada nella direzione che stavi cercando.

Osservando le attuali capacità di PostgreSQL relative alla modifica JSON, sarebbe molto complesso (se non complicato) e introdurrebbe molto sovraccarico per fare lo stesso con SQL puro.

PostgreSQL 9.6+

Nel caso in cui non disponi ancora della versione 11, la seguente funzione farà lo stesso a scapito della gestione delle conversioni di tipo da sola ma la manterrà completamente compatibile con le API, quindi una volta eseguito l'aggiornamento, l'unica cosa che devi fare è sostituire la funzione (nessuna modifica alle istruzioni che utilizzano questa funzione richiesta):

CREATE OR REPLACE FUNCTION jsonb_replace_in_array (val jsonb, path_to_array text[], replacement jsonb, entry_filters jsonb)
    RETURNS jsonb
    LANGUAGE plpython3u
AS $$
import json

v_new = json.loads(val)
t_replace = json.loads(replacement)
t_filters = json.loads(entry_filters)
tmp = v_new
for e in path_to_array:
    tmp = tmp[e]

for item in tmp:
    if (entry_filters is None or t_filters.items() <= item.items()):
        item.update(t_replace)

return json.dumps(v_new)
$$;