getIdsForRange computed includeMin/includeMax correctly for every operator (gt→false/true, lt→true/false, between→true/true) but DROPPED them when the column store served the query — it called columnStore.rangeQuery(field,min,max) with no flags, and the column-store path was inclusive-only. So whenever a field lived in the column store, strict lessThan/greaterThan silently behaved as lte/gte. The sparse-fallback path already forwarded the flags, so this was column-store-only. Thread includeMin/includeMax end to end: - ColumnSegmentCursor: add textbook lowerBound/upperBound helpers and express binarySearchRange in terms of them. Inclusive/inclusive is byte-identical to the old impl (lowerBound(lo) == old binarySearchValue(lo).index; upperBound(hi) == old "first position after hi"). Exclusive lower advances past ALL duplicates of the boundary value; exclusive upper stops before them. - ColumnStore.rangeQuery: accept the flags, apply them in the segment cursor AND the tail-buffer linear scan (strict > / < when exclusive). A bound taken from a segment's own min/max stays inclusive — it is a real stored value. - VectorIndex/types interface + getIdsForRange call site updated. Surfaced by brainy-complete dual-bound test (year/popularity exclusive both ends → ['Express','Vue.js']; React@95 and Angular@75 correctly excluded). Added 5 column-store unit tests: exclusive lower, exclusive upper, both, duplicate boundary values, and the unflushed tail-buffer path. 1469 unit green. |
||
|---|---|---|
| .. | ||
| column-store-interchange.test.ts | ||
| column-store.test.ts | ||
| manifest.test.ts | ||
| segment-cursor.test.ts | ||
| segment-format.test.ts | ||
| tail-buffer.test.ts | ||