What feature would you like to see?
Following #18308 / #18312, Pipeline.search() shipped in cloud_firestore 6.6.0 π β thank you! However, there is currently no supported way to scope/pre-filter a text search to a field value (e.g. a tenant / partition id). This makes search() return incorrect results for multi-tenant apps. I'd like a supported way to express "full-text search within the documents where field == value".
Use case
A multi-tenant app keeps every tenant's records in one collection (contacts), each tagged with businessId. A user can belong to several tenants. A search must be scoped to the currently-selected tenant.
What I tried, and what happens
search() must be the first stage after the source, so the tenant filter can't precede it. Both natural workarounds fail:
1) Combine the field filter into the search expression (the intuitive approach):
firestore
.pipeline()
.collection('contacts')
.search(
SearchStage.withQueryExpression(
Expression.and(
Expression.documentMatches('jan'),
Field('businessId').equalValue(currentBusinessId),
),
),
)
.execute();
β rejected by the server:
[cloud_firestore/invalid-argument] AND function in search(...) query is not supported yet.
2) Filter after the search stage with a trailing where():
firestore
.pipeline()
.collection('contacts')
.search(SearchStage.withQuery('jan', limit: 20))
.where(Field('businessId').equalValue(currentBusinessId))
.execute();
β This runs, but limit is applied by search() globally across every tenant the user can read, before the trailing where(). The other tenants' matches consume the limit and the selected tenant is starved.
Reproduction / evidence
cloud_firestore 6.6.0, Flutter web, Firestore Enterprise (Native mode), with a Text index on the searched fields:
- User is a member of business A (2 docs matching
"stefan") and business B (0 matching).
search(SearchStage.withQuery('stefan', limit: 1)).where(businessId == A) β 1 row.
- After adding one matching doc to B, the same query for A returns 0 rows (the single global slot is taken by B's newest doc), while the query for B returns 1. The sum across both tenants is 1, not 2 β confirming
limit is applied globally, pre-filter.
So for any user who belongs to β₯2 tenants, a scoped search returns missing/incorrect results. Raising limit is not a reliable fix β it would have to exceed the global match count across all of the user's tenants, which defeats the index.
What I'd like (any one of)
- compound
documentMatches(...) AND field == value accepted inside search() (the "not supported yet" message suggests this may be planned?), or
- a text-index partition option (e.g.
customPartitionFields / partition by a field) that the Pipeline search() path honors, so a search is implicitly scoped to a partition, or
- a documented pattern for tenant-scoped full-text search.
I appreciate this is likely a Firestore Enterprise backend capability rather than a pure FlutterFire change β filing here since this is where search() landed (#18312), and happy to move it if there's a better tracker. Is compound / field-scoped search on the roadmap?
Environment
cloud_firestore: 6.6.0 (cloud_firestore_platform_interface: 8.0.3, cloud_firestore_web: 5.6.0)
- Firestore Enterprise edition (Native mode), named database
- Reproduced on Flutter web (CanvasKit); the API is platform-agnostic
- Text index created on the searched fields via the Google Cloud console
What feature would you like to see?
Following #18308 / #18312,
Pipeline.search()shipped incloud_firestore6.6.0 π β thank you! However, there is currently no supported way to scope/pre-filter a text search to a field value (e.g. a tenant / partition id). This makessearch()return incorrect results for multi-tenant apps. I'd like a supported way to express "full-text search within the documents wherefield == value".Use case
A multi-tenant app keeps every tenant's records in one collection (
contacts), each tagged withbusinessId. A user can belong to several tenants. A search must be scoped to the currently-selected tenant.What I tried, and what happens
search()must be the first stage after the source, so the tenant filter can't precede it. Both natural workarounds fail:1) Combine the field filter into the search expression (the intuitive approach):
firestore .pipeline() .collection('contacts') .search( SearchStage.withQueryExpression( Expression.and( Expression.documentMatches('jan'), Field('businessId').equalValue(currentBusinessId), ), ), ) .execute();β rejected by the server:
2) Filter after the search stage with a trailing
where():firestore .pipeline() .collection('contacts') .search(SearchStage.withQuery('jan', limit: 20)) .where(Field('businessId').equalValue(currentBusinessId)) .execute();β This runs, but
limitis applied bysearch()globally across every tenant the user can read, before the trailingwhere(). The other tenants' matches consume the limit and the selected tenant is starved.Reproduction / evidence
cloud_firestore6.6.0, Flutter web, Firestore Enterprise (Native mode), with a Text index on the searched fields:"stefan") and business B (0 matching).search(SearchStage.withQuery('stefan', limit: 1)).where(businessId == A)β 1 row.limitis applied globally, pre-filter.So for any user who belongs to β₯2 tenants, a scoped search returns missing/incorrect results. Raising
limitis not a reliable fix β it would have to exceed the global match count across all of the user's tenants, which defeats the index.What I'd like (any one of)
documentMatches(...) AND field == valueaccepted insidesearch()(the"not supported yet"message suggests this may be planned?), orcustomPartitionFields/ partition by a field) that the Pipelinesearch()path honors, so a search is implicitly scoped to a partition, orI appreciate this is likely a Firestore Enterprise backend capability rather than a pure FlutterFire change β filing here since this is where
search()landed (#18312), and happy to move it if there's a better tracker. Is compound / field-scoped search on the roadmap?Environment
cloud_firestore: 6.6.0(cloud_firestore_platform_interface: 8.0.3,cloud_firestore_web: 5.6.0)