filter_curated_hits is ignored for pinned documents that have no value for the group_by field
Description
When group_by is active, filter_curated_hits=true is not applied to a pinned document that has no value for the group_by field. The pinned document is returned even though it does not match filter_by.
A pinned document that does have a value for the group_by field is filtered out correctly under the exact same query, and both are filtered out correctly when group_by is removed — so the only variable is the presence of the grouping field on the pinned document.
This is easy to miss in production: filter_curated_hits is typically used to stop curated items that are out of stock / disabled / region-restricted from surfacing, and the leak is silent and affects only the subset of documents where the grouping field is absent.
Steps to reproduce
Against a Typesense v30.2 instance:
1. Create the collection — group_field is optional so some documents can omit it:
curl -X POST 'http://localhost:8108/collections' \
-H 'X-TYPESENSE-API-KEY: xyz' -H 'Content-Type: application/json' -d '{
"name":"repro_fch_groupby",
"fields":[
{"name":"title","type":"string"},
{"name":"brand","type":"string","facet":true},
{"name":"group_field","type":"string","facet":true,"optional":true}
]}'2. Import 4 documents — beta-no-group is the only one without group_field:
curl -X POST 'http://localhost:8108/collections/repro_fch_groupby/documents/import?action=create' \
-H 'X-TYPESENSE-API-KEY: xyz' -H 'Content-Type: text/plain' --data-binary '
{"id":"alpha-1","title":"alpha one","brand":"alpha","group_field":"g1"}
{"id":"alpha-2","title":"alpha two","brand":"alpha","group_field":"g2"}
{"id":"beta-with-group","title":"beta with group","brand":"beta","group_field":"g3"}
{"id":"beta-no-group","title":"beta no group","brand":"beta"}'3. Search with filter_by=brand:=alpha and pin a brand=beta document (so the pinned document never matches the filter), with filter_curated_hits=true:
# A — pinned doc HAS group_field, group_by active → correct
curl -G 'http://localhost:8108/collections/repro_fch_groupby/documents/search' \
-H 'X-TYPESENSE-API-KEY: xyz' \
--data-urlencode 'q=*' --data-urlencode 'query_by=title' \
--data-urlencode 'filter_by=brand:=alpha' \
--data-urlencode 'pinned_hits=beta-with-group:1' \
--data-urlencode 'filter_curated_hits=true' \
--data-urlencode 'group_by=group_field' --data-urlencode 'group_limit=1'
# B — pinned doc has NO group_field, group_by active → BUG
curl -G 'http://localhost:8108/collections/repro_fch_groupby/documents/search' \
-H 'X-TYPESENSE-API-KEY: xyz' \
--data-urlencode 'q=*' --data-urlencode 'query_by=title' \
--data-urlencode 'filter_by=brand:=alpha' \
--data-urlencode 'pinned_hits=beta-no-group:1' \
--data-urlencode 'filter_curated_hits=true' \
--data-urlencode 'group_by=group_field' --data-urlencode 'group_limit=1'Results
| # | pinned document | group_by |
returned ids | found |
|
|---|---|---|---|---|---|
| A | beta-with-group (has group_field) |
yes | alpha-2, alpha-1 |
2 | correct |
| B | beta-no-group (no group_field) |
yes | beta-no-group, alpha-2, alpha-1 |
3 | bug |
| C | beta-with-group |
no | alpha-2, alpha-1 |
2 | correct |
| D | beta-no-group |
no | alpha-2, alpha-1 |
2 | correct |
Adding group_missing_values=false to case B does not change the outcome — beta-no-group is still returned.
Expected behaviour
In case B, beta-no-group does not match filter_by=brand:=alpha, so with filter_curated_hits=true it should be excluded from the results — the same way it is in cases C and D, and the same way beta-with-group is in case A.
Actual behaviour
beta-no-group is returned at the top of the results and counted in found.
Environment
- Typesense v30.2 (self-hosted,
/debug→{"state":1,"version":"30.2"})
Possibly related
- #1416 — brought
filter_curated_hitstopinned_hits(fixed in v27); that path works here whenever the pinned document has a grouping value. - #2033 —
pinned_hits+group_by+filter_curated_hits, where the whole group was dropped if the pinned document did not match (fixed in 28.0.rc33). The behaviour reported here looks like the complementary case: a pinned document with no group at all appears to escape the curated-hit filtering entirely.
Source: typesense/typesense