I looked at a course at Pluralsight that addressed a very similar problem. The course was "Postgres for.NET Developers," and it was in the section "Fun With Simple SQL," "Full Text Search."
To summarize their solution using your example:
Create a new column in your table that will represent your entity_city_state_zip as tsvector:
create table entities_entity ( date_filed date, entity_city_state_zip text, csz_search tsvector not null
Initially, you may need to set it to null, and then fill in the data and make it invalid.
update entities_entity set csz_search = to_tsvector (entity_city_state_zip);
Then create a trigger that will populate a new field every time you add or change a record:
create trigger entities_insert_update before insert or update on entities_entity for each row execute procedure tsvector_update_trigger(csz_search,'pg_catalog.english',entity_city_state_zip);
Now your search queries can query the tsvector field, rather than the city / state / zip field:
select * from entities_entity where csz_search @@ to_tsquery('Atherton')
Some notes about this:
- to_tsquery, in case you did not use it, WAY is more complex than the above example. This allows conditions, partial matches, etc.
- it is also not case sensitive, so there is no need to execute the
upper functions that you have in your request
As a last step, put the GIN index in the tsquery field:
create index entities_entity_ix1 on entities_entity using gin(csz_search);
If I understand the course correctly, this should make your request fly, and it will overcome the problem of the inability of the btree index to work on a like '% request.
Here is an explanation plan for such a request:
Bitmap Heap Scan on entities_entity (cost=56.16..1204.78 rows=505 width=81) Recheck Cond: (csz_search @@ to_tsquery('Atherton'::text)) -> Bitmap Index Scan on entities_entity_ix1 (cost=0.00..56.04 rows=505 width=0) Index Cond: (csz_search @@ to_tsquery('Atherton'::text))