Multicolumn GIN index not fully facilitated by query planner
I cannot reproduce this issue where it chooses to use two indexes, but I suspect it has something to do with the massively incorrect estimate for JSON containment condition; combined with another unknown factor like maybe some kind of index bloat.

...rows=179434 width=0) (actual time=8908.682..8908.682 rows=81802589


That is off by over 400 fold.  But there isn't much you can do about it, other than pull &quot;CountryId&quot; out into its own column, or upgrade to v13 where json containment selectivity estimation has been improved (though not by as much as if it were in its own column).
In any event, the choice of two different indexes is unlikely to actually be a problem.  When you use a GIN multicolumn index like this, it behaves pretty much the same as two different indexes combined with a BitmapAnd.  It is just that the BitmapAnd is occurring inside a black box where EXPLAIN can't show you the details.  I would generally not expect much difference in performance.  (One place I have seen a difference is if the tuples which match one condition are physically separated from the ones which match the other, i.e. all the tuples with campaignid = 97 are physically before all the tuples with attributes @&gt; '{&quot;CountryId&quot;: 3}' in the table.  The opaque BitmapAnd of the gin indexes has a optimization for this case that the transparent BitmapAnd does not seem to have.)
If your main goal is to make the query faster rather than either micromanaging exactly how to sublists get combined or redesigning your table, then I see two things to try.  One is increasing work_mem, as the massive number of lossy blocks can't be good for performance.  The other would be to create a multicolumn btree index on (campaignid, ((attributes-&gt;&gt;'CountryId')::int)); and then rewrite the query accordingly:
WHERE campaignid = 97 AND (attributes-&gt;&gt; 'CountryId')::integer=3