Skip to content

πŸš€ [cloud_firestore] Pipeline search() cannot be scoped to a field β€” multi-tenant text search returns incorrect resultsΒ #18391

Description

@fuwapenguin

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions