Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion engines/evidence-ledger/internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -1824,6 +1824,8 @@ func buildSearchQuery(opts SearchOpts) (string, []any) {
where, params := appendSearchResultFilters(opts, []string{"1=1"}, params)
params = append(params, limit)

// CROSS JOIN pins the candidate pool as the outer loop. A reorder through
// sources and items evaluates correlated filters against the whole archive.
sqlText := `with fts_candidates as materialized (
select item_id, snippet(item_fts, 5, '[', ']', '...', 20) as snippet, bm25(item_fts) as fts_score
from item_fts
Expand All @@ -1833,7 +1835,7 @@ func buildSearchQuery(opts SearchOpts) (string, []any) {
)
select i.id, s.kind, c.name, c.kind, i.kind, coalesce(a.type,''), coalesce(a.name,''), coalesce(i.created_at,''), fc.snippet, printf('%.6f', fc.fts_score), i.content_hash
from fts_candidates fc
join items i on i.id = fc.item_id
cross join items i on i.id = fc.item_id
join sources s on s.id = i.source_id
join collections c on c.id = i.collection_id
left join actors a on a.id = i.actor_id
Expand Down
37 changes: 37 additions & 0 deletions engines/evidence-ledger/internal/app/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1855,6 +1855,11 @@ values(?,?,?,?,?,?,?,?,?,?)`, id, "source-1", "collection-1", "actor-1", id, "me
}
}
}
if _, err := db.Exec(`insert into item_metadata(item_id, key, value) values
('item-000','project','escoffier-labs/brigade'),
('item-001','cwd','/workspace/brigade')`); err != nil {
t.Fatal(err)
}
}

func explainPlan(t *testing.T, db *sql.DB, sqlText string, args ...any) string {
Expand Down Expand Up @@ -2018,6 +2023,38 @@ func TestSearchPlanBoundsFTSCandidatesBeforeJoins(t *testing.T) {
}
}

projectSQL, projectParams := buildSearchQuery(SearchOpts{Query: "needle", Project: "workspace", Limit: 5})
projectPlan := explainPlan(t, db, projectSQL, projectParams...)
for _, want := range []string{
"SCAN fc",
"SEARCH i USING",
"SEARCH im USING COVERING INDEX",
"(item_id=?",
} {
if !strings.Contains(projectPlan, want) {
t.Fatalf("project filter plan missing %q:\n%s", want, projectPlan)
}
}
if strings.Index(projectPlan, "SCAN fc") > strings.Index(projectPlan, "SEARCH i USING") {
t.Fatalf("project filter plan does not keep FTS candidates outermost:\n%s", projectPlan)
}

exactProject, err := search(db, SearchOpts{Query: "needle", Project: "escoffier-labs/brigade", Limit: 5})
if err != nil {
t.Fatalf("exact project search: %v", err)
}
if len(exactProject) != 1 || exactProject[0].ID != "item-000" {
t.Fatalf("exact project results = %#v, want item-000", exactProject)
}

substringProject, err := search(db, SearchOpts{Query: "needle", Project: "workspace/brigade", Limit: 5})
if err != nil {
t.Fatalf("substring project search: %v", err)
}
if len(substringProject) != 1 || substringProject[0].ID != "item-001" {
t.Fatalf("substring project results = %#v, want item-001", substringProject)
}

results, err := search(db, opts)
if err != nil {
t.Fatalf("search: %v", err)
Expand Down
Loading