Skip to content

Why my strict comparison returns nothing ​

You saw this warning:

Routier: 'price' is a number, and this filter compares it against "5", which is a string.
A strict comparison between them is the same answer for every row, so no row matches and
the filter runs in memory.

Your filter compares a column against a value of a different type, with === or !==. JavaScript answers that comparison the same way for every row, so the filter cannot select anything.

The cause ​

=== compares type as well as value. 5 === "5" is false in JavaScript, whatever the row holds.

ts
// price is s.number()
const price = req.query.price;      // "5" — a string, from the URL

await ctx.products.where(p => p.price === price).toArrayAsync();
// 0 rows, always

The value usually arrives as a string from a form, a URL, a query parameter, or JSON.parse of a field that was serialized as text.

What Routier does ​

Routier detects the mismatch and runs the filter in memory rather than in the database. The rows you get are the rows JavaScript gives you.

.explain() reports it:

STEP 1 of 1 — memory  [predicate-error]
    filter   price === "5"

Routier does not correct your filter. Correcting it would mean guessing which of the two types you meant, and a wrong guess returns rows you did not ask for.

Both directions ​

filterrows returned
p.price === "5"none
p.price !== "5"every row

The second one is the dangerous one. It looks like it filters, and it does not.

How to fix it ​

Convert the value to the column's type before the filter. This is the recommended fix.

ts
const price = Number(req.query.price);

await ctx.products.where(([p, params]) => p.price === params.price, { price }).toArrayAsync();

If you want JavaScript's coercing comparison, use == or !=. Routier coerces the value to the column's type for a loose comparison, and pushes the filter down.

ts
await ctx.products.where(p => p.price == "5").toArrayAsync();
// 1 row, and it runs in the database

Which types Routier checks ​

schema typeJavaScript type it must be compared against
s.number()number
s.string()string
s.boolean()boolean
s.date()Date

A null or undefined value is never a mismatch: p.name === null is a legitimate filter.

A bigint compared to a s.number() column is a mismatch, because 5 === 5n is false in JavaScript.

Turning the warning off ​

The warning goes through Routier's logger, at warn level.

bash
ROUTIER_LOG_LEVEL=error   # or: silent

Turning the warning off does not change the rows. The filter still runs in memory, and still returns what JavaScript returns.

Released under the MIT License.