Skip to content

fix(firestore): keep non-filter constraints out of Query.and composite - #114

Open
detail-app[bot] wants to merge 3 commits into
mainfrom
detail/bug-fix/fix-firestore-keep-non-filter-constraints-out-of-q-07080c
Open

detail-app[bot] wants to merge 3 commits into
mainfrom
detail/bug-fix/fix-firestore-keep-non-filter-constraints-out-of-q-07080c

Conversation

@detail-app

@detail-app detail-app Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Detail bug report: View on Detail

Closes #94

Bug

Query.and(...) mixes filter (where) and non-filter (orderBy, limit, cursors) constraints in a single call — a pattern shown across its JSDoc, the README, and AGENTS.md. Its flatten-vs-wrap heuristic has two branches: with no nested And/Or it returns the constraints flat (works); when any child is an And/Or it wraps all children — including OrderBy/Limit/LimitToLast and the cursor constraints — into one And node. The Client and Admin query builders route every child of an And/Or through filter-only conversion and throw Cannot use <Tag> inside AND/OR composite filters on the non-filter ones. The crash only surfaces when a nested or(...) and a non-filter constraint are passed to the same Query.and(...) call; the transition from "works" to "crashes" is invisible to the caller.

Fix

In packages/effect-firebase/src/lib/firestore/query/query.ts, when Query.and decides to wrap, it now partitions the children: only filter constraints (Where/And/Or) go into the And composite; non-filter constraints are returned as top-level siblings after the composite, preserving their relative order. The emitted shape [and(filters...), orderBy, limit] is exactly what the Firestore client SDK's validateQueryConstraintArray accepts (compositeFilterCount=1, fieldFilterCount=0), and what both builders already handle on their non-composite path. Query.or is untouched (it is filter-only by contract). Introduced in 8e11e17.

Testing

  • New regression specs for the Query.and/Query.or combinators (core), the client builder, the admin builder, and the mock backend. With the fix reverted, 15 of those tests fail (builder throws, mock returns wrong/unsorted/unlimited results, structural asserts break); with the fix, all 52 pass.
  • Mock tests assert the wrapped form now filters, sorts, and limits identically to the equivalent pipeable addOrderBy/addLimit form (['And','OrderBy','Limit']).
  • End-to-end against the Firestore emulator: inserted 5 documents and ran the fix's emitted shape through the real client SDK — returned the expected filtered (published AND news-or-tech), sorted (category desc), limited rows.
  • The client SDK's synchronous validateQueryConstraintArray confirms the spread form [where, or, orderBy, limit] is still rejected (the SDK's own restriction, unchanged) while the fix's [and(where, or), orderBy, limit] shape is accepted.
  • Whole-repo build, test, and lint all pass (build for 8 projects, test for 6 projects, lint for 8 projects with 0 errors); prettier clean on the changed files.

Automatic Fixes PRs can be configured here.

@detail-app
detail-app Bot requested a review from fwal as a code owner September 19, 2026 03:40
@github-actions github-actions Bot added 🐛 fix Something is broken or doesn't work properly 📦 admin 📦 client 📦 core 📦 mock labels Sep 19, 2026
@greptile-apps

greptile-apps Bot commented Sep 19, 2026

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

The PR appears safe to merge, with the previous test-import findings fully addressed and no new actionable issues identified.

Summary

This PR fixes Query.and(...) so nested composite filters no longer absorb ordering, limits, or cursor constraints.

  • Partitions constraints into filter and non-filter groups when a nested And or Or requires wrapping.
  • Emits non-filter constraints as top-level siblings while preserving their relative order.
  • Adds coverage across core query composition, client and admin builders, and the mock backend.
  • Updates the remaining test-helper imports to use the repository-required @effect/vitest package.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["Query.and(...constraints)"] --> B{"Contains nested And or Or?"}
    B -->|No| C["Return constraints unchanged"]
    B -->|Yes| D["Partition constraints"]
    D --> E["Filters: Where / And / Or"]
    D --> F["Non-filters: ordering / limits / cursors"]
    E --> G["Wrap filters in And"]
    G --> H["Return And plus non-filter siblings"]
    F --> H
Loading

Reviews (3) · Last reviewed commit: "test(firestore): import query-combinator..."

Comment thread packages/admin/src/lib/firestore/query-builder.spec.ts Outdated
AGENTS.md requires tests to use `@effect/vitest`, like the admin function
specs converted in 10bdc0f. The new admin and client builder specs added
by this PR imported from `vitest` directly.

`@effect/vitest` re-exports all of `vitest`, so the admin spec is a plain
import swap (it has no `vi` usage). The client spec keeps `vi` on
`vitest` because it uses `vi.hoisted`/`vi.mock`, which vitest's hoisting
transformer only honours when `vi` is imported from `vitest` (or a
redistributed specifier) — `@effect/vitest` is a plain re-export, so
importing `vi` from it leaves the hoisted calls referencing an
uninitialised binding (`Cannot access '__vi_import_1__' before
initialization`).
Comment thread packages/effect-firebase/src/lib/firestore/query/query.spec.ts
AGENTS.md requires tests to use @effect/vitest, like the builder specs
fixed in cf6a3b3. The query-combinator spec in the core package and the
query-filter spec in the mock package imported from vitest directly.
Neither file uses vi, so this is a plain import swap.
@fwal fwal added this to the 1.0 milestone Sep 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📦 admin 📦 client 📦 core 🐛 fix Something is broken or doesn't work properly 📦 mock

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Detail Bug] Firestore query builder crashes when combining OR with orderBy/limit inside Query.and

1 participant