Adding filters to text search is as simple as using PostgreSQL’s built-in WHERE clauses and operators. For instance, the following query filters out results that do not meet rating > 2.
SELECT description, rating, category
FROM mock_items
WHERE description @@@ 'shoes' AND rating > 2;

Filter Pushdown

Non-Text Fields

While not required, filtering performance over non-text columns can be improved by including them in the BM25 index, and making sure they are configured as fast (which is already the default for non-text fields). When these columns are part of the index, WHERE clauses that reference them can be pushed down into the index scan itself. This can result in faster query execution over large datasets. For example, if rating and created_at are frequently used in filters, they can be added to the BM25 index during index creation:
CREATE INDEX search_idx ON mock_items
USING bm25(id, description, rating, created_at)
WITH (key_field = 'id');
Filter pushdown is currently supported for the following combinations of types and operators:
OperatorLeft Operand TypeRight Operand TypeExample
=, <, >, <=, >=, <>, BETWEENint2int2WHERE rating = 2
int4int4
int8int8
int2int4
int2int8
int4int8
float4float4
float8float8
float4float8
datedate
timetime
timetztimetz
timestamptimestamp
timestamptztimestamptz
uuiduuid
=boolboolWHERE in_stock = true
IN, ANY, ALLboolbool[]WHERE rating IN (1,2,3)
int2int2[]
int4int4[]
int8int8[]
int2int4[]
int2int8[]
int4int8[]
float4float4[]
float8float8[]
float4float8[]
datedate[]
timetztimetz[]
timestamptimestamp[]
timestamptztimestamptz[]
uuiduuid[]
IS, IS NOTboolboolWHERE in_stock IS true
IS NULL, IS NOT NULLboolWHERE rating IS NOT NULL
int2
int4
int8
int2
int2
int4
float4
float8
float4
date
time
timetz
timestamp
timestamptz
uuid

Text Fields

Suppose we have a text filter that looks for an exact string match like category = 'Footwear':
SELECT description, rating, category
FROM mock_items
WHERE description @@@ 'shoes' AND category = 'Footwear';
To push down the category = 'Footwear' filter, category must be indexed using the keyword tokenizer:
CREATE INDEX search_idx ON mock_items
USING bm25(id, description, category)
WITH (key_field = 'id', text_fields = '{"category": {"tokenizer": {"type": "keyword"}}}');
Pushdown of set filters over text fields also requires the keyword tokenizer:
SELECT description, rating, category
FROM mock_items
WHERE description @@@ 'shoes' AND category IN ('Footwear', 'Apparel');