I think you need to do this with a lateral join:
select prod_id,
array_agg(t.chd_id)
from products p
left join lateral (
select e ->> 'chd_id' as chd_id
from jsonb_array_elements(p.prod_prop) as t(e)
) t on true
group by prod_id
limit 10;
The outer join is needed in case there are empty prod_prop values
Apparently the data in the JSONB is sometimes an array and sometimes a plain object. If all "plain objects" are indeed empty values, you can work around that using:
select prod_id,
array_agg(t.chd_id) filter (where t.chd_id is not null)
from products p
left join lateral (
select e ->> 'chd_id' as chd_id
from jsonb_array_elements(nullif(p.prod_prop,'{}')) as t(e)
) t on true
group by prod_id
limit 10;
If you do indeed mix plain objects and arrays in that column, you need a different approach:
select prod_id,
array_agg(t.chd_id) filter (where t.chd_id is not null)
from products p
left join lateral (
select e ->> 'chd_id' as chd_id
from jsonb_array_elements(case jsonb_typeof(p.prod_prop)
when 'array' then p.prod_prop
else '[]'
end) as t(e)
) t on true
group by prod_id
limit 10;
Online example