Given a table like

> select * from tbl;
+----+--------------------------------------+
| id | arr                                  |
|----+--------------------------------------|
| 1  | [{"foo": 0}]                         |
| 2  | [{"bar": 0, "baz": 0, "foo": 0}]     |
| 3  | [{"bar": 0}, {"foo": 0}, {"baz": 0}] |
| 4  | [{"foo": 1}]                         |
| 5  | [{"bar": {"foo": 0}}]                |
+----+--------------------------------------+

how to get the rows where arr has an item with "foo": 0 at its top level?

You can’t do something like:

> select id from tbl where
    (arr -> 0 -> 'foo')::int = 0
    or (arr -> 1 -> 'foo')::int = 0
    or (arr -> 2 -> 'foo')::int = 0;

because some of the arrays might have many items and because it looks silly.

It’s easy in postgres:

> select id from tbl where arr @> '[{"foo": 0}]';
+----+
| id |
|----|
| 1  |
| 2  |
| 3  |
+----+

From the docs:

Testing containment is an important capability of jsonb. …​

The general principle is that the contained object must match the containing object as to structure and data contents, possibly after discarding some non-matching array elements or object key/value pairs from the containing object. But remember that the order of array elements is not significant when doing a containment match, and duplicate array elements are effectively considered only once.

And if your filter is more complicated — say, arr has an item with top-level "foo": 0 and top-level "baz" — you can do something like:

> select id from tbl where exists (
   select 1
   from jsonb_array_elements(arr) as elem
   where
     (elem -> 'foo')::int = 0
     and elem ? 'baz'
 );
+----+
| id |
|----|
| 2  |
+----+