q.lte() query includes records with null or undefined fields, causing inconsistent filter results
When using q.lte() condition, the query returns documents where the indexed field is null or undefined, in addition to valid numeric values. This results in inconsistent and incorrect query outputs compared to gte(), which behaves as expected.
LTE
Output
GTE
Output:

6 Replies
Please review the docs for using indexes. In short, the first check should always use
.eq(). Other checks can be used after that.
The main point of using an index is to precisely identify records that match a given value first (which is why .eq() is required as the first check), and then (if necessary) use other criteria to whittle down the list. It's not really designed primarily for grabbing documents that match less-than/greater-than comparisons.
If you're unable to alter your withIndex syntax to use .eq() first, then you might need to swap out .withIndex for .filter. It'll mean a full table readCan you please point me to the documentation that states we need to include at least one .eq() condition before using .lte()?
If this scenario isn’t supported, shouldn’t the query fail instead of returning inconsistent results?
Additionally, why does .gte() works as expected, while .lte() returns documents with null or undefined values along with those that actually satisfy the condition?
https://docs.convex.dev/database/reading-data/indexes

My apologies. I was mis-remembering the wording of that part of the docs. I thought it was 1 or more, not 0 or more.
As for why
.lte() returns records where the value is null or undefined, to me it kinda makes sense. In plain JS, null <= 1 returns true. While undefined <= 1 returns false in JS, I'm guessing that there's some other logic in the .lte() definition that makes it behave similar to the null comparison.Yeah. Hope someone from convex team confirm this behaviour.
This is by design - lte has to work against all field types, not just numbers, and this behavior is actually handy for strings and booleans, or any type where you want the field optional but want to query on defined fields (via .gte). You'll want to maybe make the field strictly numbers only to get the behavior you're looking for. You can also get pretty powerful filtering with streams: https://stack.convex.dev/merging-streams-of-convex-data
Thanks for the clarification. I added a filter condition with not null and undefined.
.filter((q) =>
q.and(
q.neq(q.field('field'), null),
q.neq(q.field('field'), undefined),
)