mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-27 22:15:33 +00:00
fix(takes): keyword search matches words in long claims via word_similarity (#3267)
Both engines' searchTakes used whole-string trigram similarity (claim % query), which structurally cannot pass the 0.3 threshold for a short keyword against a 100-200 char claim — keyword search returned zero results on real brains. Switch the predicate to word similarity (query <% claim) and rank by word_similarity(query, claim), in both postgres-engine and pglite-engine per the engine-parity invariant. Holder allow-list and source-scope filters unchanged. Regression test: single-word query must match a long claim containing it (fails under the old predicate). Fixes #3267 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
ca47c054b8
commit
eeecc90968
@@ -4835,11 +4835,11 @@ export class PGLiteEngine implements BrainEngine {
|
||||
const { rows } = await this.db.query(
|
||||
`SELECT t.id AS take_id, t.page_id, p.slug AS page_slug, t.row_num,
|
||||
t.claim, t.kind, t.holder, t.weight,
|
||||
similarity(t.claim, $1)::real AS score
|
||||
word_similarity($1, t.claim)::real AS score
|
||||
FROM takes t
|
||||
JOIN pages p ON p.id = t.page_id
|
||||
WHERE t.active
|
||||
AND t.claim % $1
|
||||
AND $1 <% t.claim
|
||||
AND ($2::text[] IS NULL OR t.holder = ANY($2::text[]))
|
||||
AND ($4::text[] IS NULL OR p.source_id = ANY($4::text[]))
|
||||
AND ($5::text IS NULL OR p.source_id = $5::text)
|
||||
|
||||
@@ -4962,11 +4962,11 @@ export class PostgresEngine implements BrainEngine {
|
||||
const rows = await sql`
|
||||
SELECT t.id AS take_id, t.page_id, p.slug AS page_slug, t.row_num,
|
||||
t.claim, t.kind, t.holder, t.weight,
|
||||
similarity(t.claim, ${query})::real AS score
|
||||
word_similarity(${query}, t.claim)::real AS score
|
||||
FROM takes t
|
||||
JOIN pages p ON p.id = t.page_id
|
||||
WHERE t.active
|
||||
AND t.claim % ${query}
|
||||
AND ${query} <% t.claim
|
||||
AND (
|
||||
${opts.takesHoldersAllowList ?? null}::text[] IS NULL
|
||||
OR t.holder = ANY(${opts.takesHoldersAllowList ?? null}::text[])
|
||||
|
||||
@@ -100,6 +100,22 @@ describe('searchTakes', () => {
|
||||
const worldHits = await engine.searchTakes('founder', { takesHoldersAllowList: ['world'] });
|
||||
expect(worldHits.every(h => h.holder === 'world')).toBe(true);
|
||||
});
|
||||
|
||||
// #3267: whole-string trigram % structurally can't match a short keyword
|
||||
// against a long claim (similarity between the full strings stays under the
|
||||
// 0.3 threshold). word_similarity (<%) matches the keyword against the
|
||||
// best-matching word span instead.
|
||||
test('single-word keyword matches a long claim containing it (#3267)', async () => {
|
||||
await engine.addTakesBatch([
|
||||
{
|
||||
page_id: acmePageId, row_num: 50,
|
||||
claim: 'Acme will consolidate the mid-market vertical SaaS landscape through disciplined acquisitions and a shared billing platform over the next five years',
|
||||
kind: 'bet', holder: 'garry', weight: 0.6,
|
||||
},
|
||||
]);
|
||||
const hits = await engine.searchTakes('consolidate');
|
||||
expect(hits.some(h => h.claim.includes('consolidate the mid-market'))).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateTake', () => {
|
||||
|
||||
Reference in New Issue
Block a user