mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-27 22:15:33 +00:00
query: filter since/until on effective date, not updated_at (#1706)
since/until range filters were applied against updated_at, so edited-but-old entries leaked into time-bounded queries. Filter on the effective date instead. Closes #1520 Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
This commit is contained in:
co-authored by
Matt Van Horn
parent
414940204a
commit
d33aee843b
@@ -1614,16 +1614,16 @@ export class PostgresEngine implements BrainEngine {
|
||||
params.push(symbolKind);
|
||||
symbolKindClause = `AND cc.symbol_type = $${params.length}`;
|
||||
}
|
||||
// v0.27.0: date filtering support
|
||||
// v0.29.1: since/until filter by effective date, with import-time fallback.
|
||||
let afterDateClause = '';
|
||||
if (opts?.afterDate) {
|
||||
params.push(opts.afterDate);
|
||||
afterDateClause = `AND COALESCE(p.updated_at, p.created_at) > $${params.length}::timestamptz`;
|
||||
afterDateClause = `AND COALESCE(p.effective_date, p.updated_at, p.created_at) > $${params.length}::timestamptz`;
|
||||
}
|
||||
let beforeDateClause = '';
|
||||
if (opts?.beforeDate) {
|
||||
params.push(opts.beforeDate);
|
||||
beforeDateClause = `AND COALESCE(p.updated_at, p.created_at) < $${params.length}::timestamptz`;
|
||||
beforeDateClause = `AND COALESCE(p.effective_date, p.updated_at, p.created_at) < $${params.length}::timestamptz`;
|
||||
}
|
||||
// v0.34.1 (#861 — P0 leak seal): source-isolation filter. When the
|
||||
// caller's auth scope is set, narrow the inner CTE candidate set so
|
||||
@@ -1763,16 +1763,16 @@ export class PostgresEngine implements BrainEngine {
|
||||
params.push(symbolKind);
|
||||
symbolKindClause = `AND cc.symbol_type = $${params.length}`;
|
||||
}
|
||||
// v0.27.0: date filtering support
|
||||
// v0.29.1: since/until filter by effective date, with import-time fallback.
|
||||
let afterDateClause = '';
|
||||
if (opts?.afterDate) {
|
||||
params.push(opts.afterDate);
|
||||
afterDateClause = `AND COALESCE(p.updated_at, p.created_at) > $${params.length}::timestamptz`;
|
||||
afterDateClause = `AND COALESCE(p.effective_date, p.updated_at, p.created_at) > $${params.length}::timestamptz`;
|
||||
}
|
||||
let beforeDateClause = '';
|
||||
if (opts?.beforeDate) {
|
||||
params.push(opts.beforeDate);
|
||||
beforeDateClause = `AND COALESCE(p.updated_at, p.created_at) < $${params.length}::timestamptz`;
|
||||
beforeDateClause = `AND COALESCE(p.effective_date, p.updated_at, p.created_at) < $${params.length}::timestamptz`;
|
||||
}
|
||||
// v0.34.1 (#861 — P0 leak seal): source-isolation. Anchor primitive
|
||||
// for two-pass retrieval, so cross-source anchors would let the walk
|
||||
@@ -1885,16 +1885,16 @@ export class PostgresEngine implements BrainEngine {
|
||||
params.push(symbolKind);
|
||||
symbolKindClause = `AND cc.symbol_type = $${params.length}`;
|
||||
}
|
||||
// v0.27.0: date filtering support
|
||||
// v0.29.1: since/until filter by effective date, with import-time fallback.
|
||||
let afterDateClause = '';
|
||||
if (opts?.afterDate) {
|
||||
params.push(opts.afterDate);
|
||||
afterDateClause = `AND COALESCE(p.updated_at, p.created_at) > $${params.length}::timestamptz`;
|
||||
afterDateClause = `AND COALESCE(p.effective_date, p.updated_at, p.created_at) > $${params.length}::timestamptz`;
|
||||
}
|
||||
let beforeDateClause = '';
|
||||
if (opts?.beforeDate) {
|
||||
params.push(opts.beforeDate);
|
||||
beforeDateClause = `AND COALESCE(p.updated_at, p.created_at) < $${params.length}::timestamptz`;
|
||||
beforeDateClause = `AND COALESCE(p.effective_date, p.updated_at, p.created_at) < $${params.length}::timestamptz`;
|
||||
}
|
||||
// v0.34.1 (#861, F2 — P0 leak seal): source-isolation in the INNER CTE
|
||||
// specifically. Pushing the filter inside narrows the HNSW candidate set
|
||||
|
||||
@@ -94,12 +94,30 @@ describe('postgres-engine / search path timeout isolation', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('postgres-engine / search date filtering', () => {
|
||||
test('search paths filter since/until on effective_date before import-time fallback', () => {
|
||||
const expectedDateExpr = 'COALESCE(p.effective_date, p.updated_at, p.created_at)';
|
||||
const staleDatePredicate = /COALESCE\(p\.updated_at,\s*p\.created_at\)\s*[<>]\s*\$/;
|
||||
|
||||
for (const methodName of ['searchKeyword', 'searchKeywordChunks', 'searchVector']) {
|
||||
const fn = stripComments(extractMethod(SRC, methodName));
|
||||
|
||||
expect(countOccurrences(fn, expectedDateExpr)).toBe(2);
|
||||
expect(fn).not.toMatch(staleDatePredicate);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function stripComments(s: string): string {
|
||||
return s
|
||||
.replace(/\/\*[\s\S]*?\*\//g, '')
|
||||
.replace(/(^|\s)\/\/[^\n]*/g, '$1');
|
||||
}
|
||||
|
||||
function countOccurrences(s: string, needle: string): number {
|
||||
return s.split(needle).length - 1;
|
||||
}
|
||||
|
||||
// extractMethod grabs the body of a class method by brace-matching from
|
||||
// its opening line. Returns the method body up to the matching closing
|
||||
// brace. Good enough for the small number of methods in this file.
|
||||
|
||||
Reference in New Issue
Block a user