Given the following jsonb
array, I want to filter it only for certain objects.
[
{
"id": "123"
},
{
"id": "456"
},
{
"id": "789"
}
]
I can use the function jsonb_path_query_array
with the path strict $[*] ? (@.id == $idToSearch[*])
and a variables
parameter of {"idToSearch":["123","456"]}
to filter by the id
parameter. The logic being:
$[*]
breaks out the array?
is a filter predicate...- ... where the
id
property is equal to ... - ... any value in the
idToSearch
array variable.
- ... where the
I get this straightforward result:
[
{
"id": "123"
},
{
"id": "456"
}
]
If however, I invert the ==
in the path query to strict $[*] ? (@.id != $idToSearch[*])
then instead of getting the correct result of
[
{
"id": "789"
}
]
instead I just get all the objects again.
As a workaround, I can use a negated equals strict $[*] ? (! (@.id == $idToSearch[*]))
to get the correct result.
Why is the query not returning the correct result?
Is this a bug? If so, what's the cause and has it been filed? If it's not a bug, where am I going wrong in the path query, and what would be the correct way to express it?
create table test (
_id int,
items jsonb
);
insert into test values (1, '[
{
"id": "123"
},
{
"id": "456"
},
{
"id": "789"
}
]');
select
jsonb_path_query_array(
items,
'strict $[*] ? (@.id != $idToSearch[*])',
'{"idToSearch":["123","456"]}')
from test;
1 Answer 1
(@.id == $idToSearch[*])
returns true if @.id
is equal to any member of $idToSearch
, yielding only those values present in $idToSearch[*]
.
(@.id != $idToSearch[*])
returns true if @.id
is different from any member of $idToSearch
, yielding every value from the left-hand array, because there's always at least one value in $idToSearch
which is different.
!(@.id == $idToSearch[*])
is obviously the inverse of the first condition; it returns false if @.id
is equal to any member of $idToSearch
, that is, true if it is different from all members of $idToSearch
.
In other words,
(@.id == $idToSearch[*])
means@.id = ANY($idToSearch)
(@.id != $idToSearch[*])
means@.id != ANY($idToSearch)
!(@.id == $idToSearch[*])
meansNOT (@.id == ANY($idToSearch))
, or@.id != ALL($idToSearch)
-
Is there any other way of writing "different from all members of the array"?Charlieface– Charlieface2025年07月07日 14:51:04 +00:00Commented Jul 7 at 14:51
-
What's wrong with the one you already got?mustaccio– mustaccio2025年07月07日 15:48:51 +00:00Commented Jul 7 at 15:48
-
Feels backwards with the double negative. Is there any other way of expressing
!= ANY
in JsonPath?Charlieface– Charlieface2025年07月07日 16:04:36 +00:00Commented Jul 7 at 16:04 -
@Charlieface
a != ANY(b)
(on a native postgres array) would also fail, for exactly the same reason: it would spread the!=
operator across the elements, and then return true if any of those results is true: postgresql.org/docs/current/… What you want is an equivalent fora != ALL(b)
. Boolean logic is tricky that way.IMSoP– IMSoP2025年07月08日 15:44:07 +00:00Commented Jul 8 at 15:44