Query results not as expected - Jsonb Postgres
Try this.. Just out of curiosity..

CREATE INDEX idx_btree_ig_follower_count
  ON public.influencers
  USING BTREE (CAST(ig-&gt;&gt;'follower_count' AS int));


Then in your query, cast to int.

SELECT count(*)
FROM "influencers"
WHERE ((ig -&gt;&gt; 'follower_count')::int &gt;= '1000')
  AND ((ig -&gt;&gt; 'follower_count')::int &lt;= '10000');


You don't need to specify IS NOT NULL if you specify &gt;=, and &lt;=

Test case

I created some test data with 10 million rows of jsonb, with btree index over it.

CREATE TABLE influences
AS
  SELECT
    jsonb_build_object('follower_count', trunc(random()*1e7)::int) AS ig 
  FROM generate_series(1,1e7);

CREATE INDEX ON influences( CAST( (ig-&gt;&gt;'follower_count') AS int) );


I'm getting the same query plan you provided,. You can see mine here.

EXPLAIN (ANALYZE,BUFFERS)
SELECT count(*) FROM influences
WHERE (ig-&gt;&gt;'follower_count')::int BETWEEN 0 AND 854780;
                                                                     QUERY PLAN                                                                     
----------------------------------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=130124.36..130124.37 rows=1 width=0) (actual time=482.615..482.615 rows=1 loops=1)
   Buffers: shared hit=85674
   -&gt;  Bitmap Heap Scan on influences  (cost=18483.87..127946.94 rows=870969 width=0) (actual time=206.182..426.880 rows=855570 loops=1)
         Recheck Cond: ((((ig -&gt;&gt; 'follower_count'::text))::integer &gt;= 0) AND (((ig -&gt;&gt; 'follower_count'::text))::integer &lt;= 854780))
         Heap Blocks: exact=83334
         Buffers: shared hit=85674
         -&gt;  Bitmap Index Scan on influences_int4_idx  (cost=0.00..18266.13 rows=870969 width=0) (actual time=182.158..182.158 rows=855570 loops=1)
               Index Cond: ((((ig -&gt;&gt; 'follower_count'::text))::integer &gt;= 0) AND (((ig -&gt;&gt; 'follower_count'::text))::integer &lt;= 854780))
               Buffers: shared hit=2340
 Planning time: 0.341 ms
 Execution time: 482.665 ms
(11 rows)


I'm aggregating far more rows, 855570 in my example, and it's over 10x faster.

I clustered it

CLUSTER influences USING influences_int4_idx ;


And, that halfed the time further to 200ms

EXPLAIN (ANALYZE,BUFFERS) SELECT count(*) FROM influences WHERE (ig-&gt;&gt;'follower_count')::int BETWEEN 0 AND 854780;
                                                                    QUERY PLAN                                                                    
--------------------------------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=130124.66..130124.67 rows=1 width=0) (actual time=211.880..211.881 rows=1 loops=1)
   Buffers: shared hit=4 read=9466
   -&gt;  Bitmap Heap Scan on influences  (cost=18483.94..127947.22 rows=870976 width=0) (actual time=69.503..155.670 rows=855570 loops=1)
         Recheck Cond: ((((ig -&gt;&gt; 'follower_count'::text))::integer &gt;= 0) AND (((ig -&gt;&gt; 'follower_count'::text))::integer &lt;= 854780))
         Heap Blocks: exact=7130
         Buffers: shared hit=4 read=9466
         -&gt;  Bitmap Index Scan on influences_int4_idx  (cost=0.00..18266.20 rows=870976 width=0) (actual time=68.150..68.150 rows=855570 loops=1)
               Index Cond: ((((ig -&gt;&gt; 'follower_count'::text))::integer &gt;= 0) AND (((ig -&gt;&gt; 'follower_count'::text))::integer &lt;= 854780))
               Buffers: shared hit=3 read=2337
 Planning time: 0.412 ms
 Execution time: 211.929 ms
(11 rows)