Skip to content

feat: add eqOrIn and neOrNin query value utils - #49

Merged
fratzinger merged 1 commit into
mainfrom
feat/eq-or-in-ne-or-nin
Sep 9, 2026
Merged

fratzinger merged 1 commit into
mainfrom
feat/eq-or-in-ne-or-nin

Conversation

@fratzinger

Copy link
Copy Markdown
Member

Summary

Adds two utils for a pattern that recurs whenever you build a query from a
list of values: collapse to the narrower operator when only one value remains.

eqOrIn([1, 2, 3]) // => { $in: [1, 2, 3] }
eqOrIn([1]) // => 1

neOrNin([1, 2, 3]) // => { $nin: [1, 2, 3] }
neOrNin([1]) // => { $ne: 1 }

onDelete already hand-rolled exactly this branch and now uses eqOrIn
instead.

An empty list yields { $in: [] } / { $nin: [] } — semantically correct
(matches nothing / excludes nothing), but documented, since some adapters
dislike an empty IN ().

Other Information

Deduplication. Values are deduplicated before the length check, so
[1, 1] collapses to the single-value form too. This is behaviour-preserving:
a deduplicated $in matches the same set of records.

The dedupe lives in a new shared src/common/dedupe-values.ts (in-source
tests) used by both utils. It compares primitives with SameValueZero and
non-primitives deep-equal via dequal — the dependency dedupeBranches
already uses for the same job on $and/$or branches. Deep-equal matters for
the most common real case: result.map((x) => x[keyHere]) over a mongo service
yields distinct ObjectId references with equal values, which a plain Set
would not collapse. Same for Date.

Primitives never reach the deep comparison, so a large list of scalar ids stays
a single Set pass and only the non-primitive elements are compared pairwise —
relevant because $in arrays can hold thousands of ids, unlike the handful of
branches dedupeBranches sees.

One documented trade-off: dequal ignores key order, so two plain objects with
the same entries in a different order count as one. MongoDB matches document
values in $in field-order-sensitively and would treat them as distinct. Plain
documents as $in values are vanishingly rare in Feathers, and
dedupeBranches already makes the same trade-off, so this seemed the right
default — happy to restrict deep-equal to non-plain objects if preferred.

Docs pages are auto-discovered from the sibling .md files; test/index.test.ts
export lists updated. pnpm test passes: 0 lint errors, no type errors, both
utils and the helper at full coverage.

🤖 Generated with Claude Code

Both collapse a list of values into the narrower query operator when only one
value remains: `eqOrIn` returns the bare value instead of `{ $in: [value] }`,
`neOrNin` returns `{ $ne: value }` instead of `{ $nin: [value] }`.

Values are deduplicated first via a new shared `dedupeValues` helper, which
compares primitives with SameValueZero and non-primitives deep-equal so value
wrappers like `Date` or a mongo `ObjectId` collapse across references.
Primitives never hit the deep comparison, keeping large scalar id lists a
single `Set` pass.

`onDelete` now uses `eqOrIn` instead of hand-rolling the same branch.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying feathers-utils with  Cloudflare Pages  Cloudflare Pages

Latest commit: 6a43a4b
Status: ✅  Deploy successful!
Preview URL: https://21026863.feathers-utils.pages.dev
Branch Preview URL: https://feat-eq-or-in-ne-or-nin.feathers-utils.pages.dev

View logs

@fratzinger
fratzinger merged commit 62186e4 into main Sep 9, 2026
9 checks passed
@fratzinger
fratzinger deleted the feat/eq-or-in-ne-or-nin branch September 9, 2026 07:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant