Background: We use PaperTrail to keep the history of our changing models. Now I want to query for a Item, which belonged to a certain customer. PaperTrail optionally stores the object_changes and I need to query this field to understand, when something was created with this ID or changed to this ID.
My table looks simplified like this:
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"]}
How do I query for elements changed from or to ID 5 (so all rows above)? I tried:
SELECT * FROM versions WHERE object_changes->'customer_id' ? 5;
Which got me:
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.
For
jsonbthe contains operator@>does what you ask for:Get all rows where the number 5 is an element of the "customer_id" array:
The
@>operator expectsjsonbas right operand - or a string literal that is valid forjsonb(while?expectstext). The numeric literal without single quotes you provided in your example (5) cannot be coerced tojsonb(nortext), it defaults tointeger. Hence the error message. Related:This can be supported with different index styles. For my query suggested above, use an expression index (specialized, small and fast):
This alternative query works, too:
And can be supported with a general index (more versatile, bigger, slower):
Related:
According to the manual, the operator
?searches for anytop-level key within the JSON value. Testing indicates that strings in arrays are considered "top-level keys", but numbers are not (keys have to be strings after all). So while this query would work:Your query looking for a number in an array will not (even when you quote the input string literal properly). It would only find the (quoted!) string
"5", classified as key, but not the (unquoted) number5, classified as value.Aside: Standard JSON only knows 4 primitives: string, number, boolean and null. There is no integer primitive (even if I have heard of software adding that), integer is a just a subset of number, which is implemented as
numericin Postgres:So your question title is slightly misleading as there are no "integer" members, strictly speaking.