fix(find): a page the metadata block already cut is not cut again
Some checks failed
Delta Gate / Delta gate — candidate vs control (push) Waiting to run
CI / Node 24 (push) Successful in 12m34s
CI / Node 22 (push) Successful in 12m40s
CI / Integration + conformance (Node 22) (push) Failing after 17m14s
CI / Bun (latest) (push) Successful in 12m29s

`find({ query, connected, where, offset })` answered [] for every page but the
first. The metadata block ranks the fused candidates and CUTS the page itself
— rows [offset, offset+limit) — and then returns early. Two shapes do not take
that early return, `connected` and `fusion`, and they fell through to the tail,
which sliced the already-cut page by `offset` a second time: a five-row page
sliced at offset five is nothing at all. Every page after the first was empty,
and the caller had no way to tell that from "no more rows".

The block now records that it consumed the offset, and the tail returns the
page it was handed instead of re-cutting it. Nothing changes at offset 0, where
the second slice was the identity.

Pinned in tests/integration/find-hybrid-filter-before-hydrate.test.ts: page two
of a `connected` hybrid find matches the pipeline oracle row for row, paging
reaches every matching neighbour exactly once, and a `fusion` find's second
page is the same page the plain find returns.
This commit is contained in:
David Snelling 2026-09-02 09:28:44 -07:00
parent b1c7054467
commit 905c267c47
2 changed files with 69 additions and 1 deletions

View file

@ -7658,6 +7658,11 @@ export class Brainy<T = any> implements BrainyInterface<T> {
// the hybrid branch, so every other path hydrates unchanged.
let finishHybridRow: ((row: Result<T>, pending: Result<T>) => void) | undefined
// Set once the metadata block below has already ranked and CUT the page.
// The tail must not cut it a second time: `offset` has been consumed, and
// re-slicing a `limit`-long page by `offset` returns nothing at all.
let pagedEarly = false
// Handle text-only query (user explicitly wants text search)
if (searchMode === 'text' && params.query && params.query.trim() !== '') {
results = await this.executeTextSearch(params.query, limit * 2)
@ -7754,6 +7759,7 @@ export class Brainy<T = any> implements BrainyInterface<T> {
const k = offset + limit
const order = rankIndicesByScore(results.map(r => r.score), k, true)
results = reorderByIndices(results, order).slice(offset, k)
pagedEarly = true
// Batch-load entities only for the paginated results (10x faster on GCS).
// This is the hydrate-last seam for the deferring paths: a row that
@ -7868,8 +7874,15 @@ export class Brainy<T = any> implements BrainyInterface<T> {
// Efficient pagination - only slice what we need (limit already defined
// above), THEN read canonical for the page. Rows that arrived hydrated
// pass straight through; a deferred path reads exactly these rows.
//
// A page the metadata block already cut is NOT cut again: it holds the
// rows at [offset, offset+limit) of the ranking, so slicing it by
// `offset` a second time drops the whole page. That is how
// `find({ query, connected, where, offset })` — the shapes that reach
// here after early paging, `connected` and `fusion` — answered [] for
// every page but the first.
return await this.hydrateResultPage(
results.slice(finalOffset, finalOffset + limit),
pagedEarly ? results : results.slice(finalOffset, finalOffset + limit),
finishHybridRow
)
})()

View file

@ -360,6 +360,61 @@ describe('hybrid find: filter before hydrate — the answer is unchanged', () =>
for (const r of actual) expect(neighbours.has(r.id)).toBe(true)
})
it('hybrid + connected + offset: page two is the page, not an empty answer', async () => {
const params = {
query: QUERY,
connected: { from: anchor, direction: 'out' as const },
where: { lane: 'alpha' },
limit: 5,
offset: 5
}
const expected = await legacyHybridFind(brain as any, params)
expect(expected).toHaveLength(5)
const actual = await brain.find(params as any)
expect(actual.length).toBe(expected.length)
expect(project(actual)).toEqual(expected)
})
it('hybrid + connected: paging reaches every matching neighbour exactly once', async () => {
const seen = new Set<string>()
for (let offset = 0; offset < MATCHES; offset += 6) {
const page = await brain.find({
query: QUERY,
connected: { from: anchor, direction: 'out' as const },
where: { lane: 'alpha' },
limit: 6,
offset
} as any)
for (const r of page) {
expect(seen.has(r.id)).toBe(false)
seen.add(r.id)
}
}
// Every row the fused candidate set holds is reachable by paging, and the
// neighbour set is the ceiling.
expect(seen.size).toBeGreaterThanOrEqual(MATCHES)
const neighbours = new Set(matchIds)
for (const id of seen) expect(neighbours.has(id)).toBe(true)
})
it('hybrid + fusion + offset: page two is the page', async () => {
const plain = await brain.find({
query: QUERY,
where: { lane: 'alpha' },
limit: 5,
offset: 5
} as any)
const fused = await brain.find({
query: QUERY,
where: { lane: 'alpha' },
fusion: 'weighted',
limit: 5,
offset: 5
} as any)
expect(fused).toHaveLength(plain.length)
expect(fused.map((r) => r.id)).toEqual(plain.map((r) => r.id))
})
it('a hydrated hybrid row is shaped exactly as an eagerly-built one', async () => {
const rows = await brain.find({ query: QUERY, where: { lane: 'alpha' }, limit: 8 } as any)
const row = rows[0]