Skip to content

fix(filter): accept runtime numeric values in filter_by#13

Merged
tharropoulos merged 1 commit into
tharropoulos:1.0.0from
georgegebbett:fix/filter-runtime-numeric-value
May 29, 2026
Merged

fix(filter): accept runtime numeric values in filter_by#13
tharropoulos merged 1 commit into
tharropoulos:1.0.0from
georgegebbett:fix/filter-runtime-numeric-value

Conversation

@georgegebbett

@georgegebbett georgegebbett commented May 29, 2026

Copy link
Copy Markdown
Contributor

Problem

Interpolating a runtime number into a filter fails to type-check:

const n: number = 1214009;
multisearchEntry({
  collection: "matters",
  q: "*",
  query_by: ["title"],
  filter_by: `humanReadableId:=${n}`, // Error: Unknown token: ${number}
});

The value humanReadableId:=1214009 is valid at runtime, but there's currently no way to pass a runtime numeric value into filter_by. The backtick escape that works for string fields (field:=`${value}`) doesn't help here — Typesense rejects a backtick-quoted number with "Numerical field has an invalid comparator". So the one form that type-checks is the one the engine refuses, and vice-versa.

Cause

When a number is interpolated, the value arrives as the template-literal type `${number}`. The tokenizer's char-split (T extends \${infer Head}${infer Tail}`) collapses the whole placeholder into Head, which doesn't extend NumericValue (Digit | "." | "-"), so it falls through to Unknown token: ${number}`. It's a coverage gap in the tokenizer.

Fix

Add a FilterTokenizer branch that treats a `${number}`/`${bigint}` placeholder as a NumToken and recurses on the tail. It's placed after the literal NumericValue branch, so literal digits still go through ReadNum and keep their exact value (NumToken<"20">). The tail recursion handles continuation clauses, arrays, and ranges:

  • humanReadableId:=${n} -> valid
  • humanReadableId:=${n} && title:foo -> valid
  • humanReadableId:[${n}] and humanReadableId:[${a}..${b}] -> valid

Invalid filters are still rejected (wrong operator for a numeric field, unknown fields, etc.) — the fix isn't over-permissive.

Tests

Added tokenizer and ParseFilter cases for ${number}/${bigint} interpolation across :=, comparisons, arrays, ranges, and continuation clauses, plus a guard that literal numbers keep their exact NumToken value.

Interpolating a `number` into a filter (e.g. `field:=${n}`) arrives as the
template-literal type `${number}`, which the tokenizer's char-split collapsed
into a single Head that doesn't extend NumericValue, falling through to
`Unknown token: ${number}`. Backtick-quoting type-checked but Typesense rejects
it at runtime ("invalid comparator"), so numeric fields had no working path for
runtime values.

Add a FilterTokenizer branch that treats a `${number}`/`${bigint}` placeholder
Head as a NumToken and recurses on the tail. Placed after the literal
NumericValue branch so literal digits keep their exact value, and the tail
recursion handles continuation clauses, arrays, and ranges.

@tharropoulos tharropoulos left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this!

@tharropoulos tharropoulos merged commit 1a8c343 into tharropoulos:1.0.0 May 29, 2026
2 checks passed
@georgegebbett georgegebbett deleted the fix/filter-runtime-numeric-value branch May 29, 2026 18:35
@georgegebbett

Copy link
Copy Markdown
Contributor Author

thanks for the merge! lmk when you are able to cut a release as would love to be able to upgrade to the version with the fix!

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.

2 participants