Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

BLQL range operators match fields of another BSON type: null under $lt, strings under $gt

Open
#267 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
1-2 days
Newbie friendliness
42/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Active
Tech stack
csharp
Domain
database

Research direction

Read src/BLite.Core/Query/Blql/BlqlFilter.cs and BsonValueComparer.cs to understand how range comparisons use the total BSON type order, then compare that behavior with the index scan in src/BLite.Core/DynamicCollection.cs. Use the reproductions and affected oracle cases in tests/BLite.Tests/Oracles/DynamicDifferentialTests.cs; done when range queries return the same results with and without an index while equality and sorting retain their documented behavior.

Written by the indexing model from the issue text.

Description

area/linq bug package/core triage

Without an index, BLQL's $lt, $lte, $gt and $gte match values of other BSON types: an explicit null is below every number, so {m: {$lt: 10}} matches documents whose m is null, and a string is above every number, so {m: {$gt: 0}} matches m: "x". With an index on the field the same query returns only the numbers. An index changes the answer.

Found by the DynamicCollection oracle tests (tests/BLite.Tests/Oracles/DynamicDifferentialTests.cs, PR to follow, stacked on #263), which carry the affected rows (4: plain/blql, both oracles, maybe <= 0 and maybe < 9.5 (double bound); e.g. expected 72 documents, got 178, every document whose maybecount is null) as a known candidate until this is fixed. The indexed/blql rows of the same queries agree with LINQ.

Repro

// m: null ("null"), -1 ("minus1"), 5 ("five"), "x" ("text")
col.Query(BlqlFilter.Lte("m", 0)).ToList();    // expected [minus1],       actual [minus1, null]
col.Query("{\"m\": {\"$lt\": 10}}").ToList();  // expected [minus1, five], actual [minus1, null, five]
col.Query(BlqlFilter.Gt("m", 0)).ToList();     // expected [five],         actual [five, text]
// after await col.CreateIndexAsync("m"): [minus1], [minus1, five], [five]

Expected: the same answer with and without an index. MQL brackets range operators by type ($lt: 10 only matches numbers), and LINQ over int? agrees (null <= 0 is false). A missing field already behaves that way in BLQL (CompareFilter.Matches returns false for every operator but $ne, BlqlFilter.cs:315-319); an explicit null does not.

Actual: see above.

Mechanism (verified on 0450b97)

  • CompareFilter.CompareValues (src/BLite.Core/Query/Blql/BlqlFilter.cs:330) applies the operator to BsonValueComparer.Compare, which is a total order across types: null first (BsonValueComparer.cs:21-24), then by TypeOrder (:90), as its summary describes ("Null < Boolean < (Int32 | Int64 | Double | Decimal) < String < ObjectId < DateTime", BsonValueComparer.cs:10).
  • With an index, GetScanSource scans only the bound's key range: QueryIndexAsync(null, upper) starts after the null bucket (src/BLite.Core/DynamicCollection.cs:1065), and string keys sort below the integer bound. The residual filter can only drop documents, so the indexed collection answers like LINQ.

Affected versions: the comparer since BLQL (a86d458, v1.9.0). Reproduced on v5.2.3 / 0450b97.

Fix considerations

  • $gt, $gte, $lt, $lte are false unless both values are of one comparable kind (the numeric family, the date family, or the same type); $eq, $ne and sorting keep the total order, which sorting needs.
  • The total order is documented in the comparer's summary, so whether range operators should bracket by type (MQL) or keep the total order (and make the index scan include the other types) is your call; the two ways should agree either way.
  • This is a separate fix from #268, which is in the numeric branch of the same comparer.

Related: #248 (open, the typed != and stored nulls), and the other BLQL rows of the same tests: #264, #265, #266, #268.

Dominant language
C#
Stars
66
Forks
6
Avg merge
1d 2h
Merged PRs (30d)
43

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from EntglDb/BLite

All issues in EntglDb/BLite

Similar issues

More C# issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.