2

Contexte: Nous utilisons PaperTrail pour conserver l'historique de nos modèles changeants. Maintenant, je veux interroger pour un article, qui appartenait à un certain client. PaperTrail stocke facultativement le object_changes et j'ai besoin d'interroger ce champ pour comprendre, quand quelque chose a été créé avec cet ID ou changé pour cet ID.Requête tableau jsonb pour le membre entier

Ma table semble simplifiée comme ceci:

item_type | object_changes 
----------|---------------------------------------------------------- 
"Item" | {"customer_id": [null, 5], "other": [null, "change"]} 
"Item" | {"customer_id": [4, 5], "other": ["unrelated", "change"]} 
"Item" | {"customer_id": [5, 6], "other": ["asht", "asht"]} 

Comment faire une requête pour les éléments modifiés en provenance ou à ID 5 (donc toutes les lignes ci-dessus)? J'ai essayé:

SELECT * FROM versions WHERE object_changes->'customer_id' ? 5; 

Ce qui m'a:

ERROR: operator does not exist: jsonb ? integer 
LINE 1: ...T * FROM versions WHERE object_changes->'customer_id' ? 5; 
                   ^
HINT: No operator matches the given name and argument type(s). 
You might need to add explicit type casts. 
+0

Vous pourriez également aimer la méthode 'where_object_changes'. C'est supposé être un moyen pratique de faire une clause 'where' pour la colonne' object_changes'. –

+0

Alors, avez-vous votre réponse? –

Répondre

0

Pour jsonb le contains operator @> fait ce que vous demandez:

Obtenez toutes les lignes où le nombre est un élément du tableau "customer_id":

SELECT * 
FROM versions 
WHERE object_changes->'customer_id' @> '5'; 

L'opérateur @> attend jsonb comme opérande de droite - ou une chaîne de caractères qui est valable pour jsonb (en ?text attend). Le littéral numérique sans guillemets simples que vous avez fourni dans votre exemple (5) ne peut pas être converti en jsonb (ni text), il est par défaut integer. D'où le message d'erreur. Connexes:

Cela peut être pris en charge avec différents styles d'index . Pour ma requête a suggéré ci-dessus, utiliser un index d'expression (spécialisée, petit et rapide):

CREATE INDEX versions_object_changes_customer_id_gin_idx ON versions 
USING gin ((object_changes->'customer_id')); 

Cette alternative fonctionne requête aussi:

SELECT * FROM versions WHERE object_changes @> '{"customer_id": [5]}'; 

Et peut être pris en charge avec un indice général (plus polyvalent , plus grand, plus lent):

CREATE INDEX versions_object_changes_gin_idx ON versions 
USING gin (object_changes jsonb_path_ops); 

connexes:

According to the manual, l'opérateur ? recherches pour toute top-level key within the JSON value.Les tests indiquent que les chaînes dans les tableaux sont considérés comme « clés de haut niveau », mais numéros sont pas (clés doivent être des chaînes après tout). Ainsi, alors que cette requête fonctionnerait:

SELECT * FROM versions WHERE object_changes->'other' ? 'asht'; 

Votre requête la recherche d'un numéro dans un tableau ne sera pas (même lorsque vous citez correctement la chaîne d'entrée littérale). Il ne trouverait que la chaîne (citée!) "5", classée clé, mais pas le numéro (non cité) 5, classé valeur.

Mis: Standard JSON only knows 4 primitives: chaîne, nombre, boolean et null. Il n'y a pas entier primitif (même si je l'ai entendu parler de logiciels ajoutant que), entier est juste un sous-ensemble de nombre, qui est mis en œuvre comme numeric dans Postgres:

Ainsi, le titre de votre question est légèrement trompeur car il n'y a pas de membres «entiers», à proprement parler.

0

Utilisez un latéral rejoindre et la fonction jsonb_array_elements_text pour traiter object_changes de chaque ligne:

SELECT DISTINCT v.* FROM versions v 
JOIN LATERAL jsonb_array_elements_text(v.object_changes->'customer_id') ids ON TRUE 
WHERE ids.value::int = 5; 

Le DISTINCT est nécessaire que si la customer_id vous cherchez peut apparaître plusieurs fois dans le tableau (si un champ différent changé mais customer_id est suivi de toute façon).