The issue here is the row based format. You simply can't filter on arbitrary columns with that. Either use an external warehouse or a columnar plug-in like Timescale.
Columnar solves some query patterns but treating row format as a dealbreaker for top-k is an overreach. For modest-to-mid datasets with the right index Postgres handles top-k on composite keys well, especially if reads aren't scanning millions of rows or you can fit hot columns in memory.
If latency really matters and you are working with large datasets, columnar extensions help, but they come with operational overhead and can limit transactional features, so it's usually better to stick with row-based unless you have a clear need.
Columnar solves some query patterns but treating row format as a dealbreaker for top-k is an overreach. For modest-to-mid datasets with the right index Postgres handles top-k on composite keys well, especially if reads aren't scanning millions of rows or you can fit hot columns in memory.
If latency really matters and you are working with large datasets, columnar extensions help, but they come with operational overhead and can limit transactional features, so it's usually better to stick with row-based unless you have a clear need.