feat: add document search query
This commit is contained in:
parent
d4138a0e2c
commit
05ede2e7cb
53
internal/store/search.go
Normal file
53
internal/store/search.go
Normal file
@ -0,0 +1,53 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type SearchHit struct {
|
||||
ThreadID int64 `json:"thread_id"`
|
||||
Number int `json:"number"`
|
||||
Kind string `json:"kind"`
|
||||
State string `json:"state"`
|
||||
Title string `json:"title"`
|
||||
HTMLURL string `json:"html_url"`
|
||||
AuthorLogin string `json:"author_login,omitempty"`
|
||||
Snippet string `json:"snippet"`
|
||||
}
|
||||
|
||||
func (s *Store) SearchDocuments(ctx context.Context, repoID int64, query string, limit int) ([]SearchHit, error) {
|
||||
if limit <= 0 {
|
||||
limit = 20
|
||||
}
|
||||
rows, err := s.db.QueryContext(ctx, `
|
||||
select t.id, t.number, t.kind, t.state, t.title, t.html_url, t.author_login,
|
||||
snippet(documents_fts, 3, '[', ']', '...', 18)
|
||||
from documents_fts
|
||||
join documents d on d.id = documents_fts.rowid
|
||||
join threads t on t.id = d.thread_id
|
||||
where t.repo_id = ? and documents_fts match ?
|
||||
order by bm25(documents_fts)
|
||||
limit ?
|
||||
`, repoID, query, limit)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("search documents: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var out []SearchHit
|
||||
for rows.Next() {
|
||||
var hit SearchHit
|
||||
var author sql.NullString
|
||||
if err := rows.Scan(&hit.ThreadID, &hit.Number, &hit.Kind, &hit.State, &hit.Title, &hit.HTMLURL, &author, &hit.Snippet); err != nil {
|
||||
return nil, fmt.Errorf("scan search hit: %w", err)
|
||||
}
|
||||
hit.AuthorLogin = author.String
|
||||
out = append(out, hit)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, fmt.Errorf("iterate search hits: %w", err)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
40
internal/store/search_test.go
Normal file
40
internal/store/search_test.go
Normal file
@ -0,0 +1,40 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSearchDocuments(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
st, err := Open(ctx, filepath.Join(t.TempDir(), "gitcrawl.db"))
|
||||
if err != nil {
|
||||
t.Fatalf("open store: %v", err)
|
||||
}
|
||||
defer st.Close()
|
||||
|
||||
repoID, err := st.UpsertRepository(ctx, Repository{Owner: "openclaw", Name: "gitcrawl", FullName: "openclaw/gitcrawl", RawJSON: "{}", UpdatedAt: "2026-04-26T00:00:00Z"})
|
||||
if err != nil {
|
||||
t.Fatalf("repo: %v", err)
|
||||
}
|
||||
threadID, err := st.UpsertThread(ctx, Thread{
|
||||
RepoID: repoID, GitHubID: "1", Number: 1, Kind: "issue", State: "open",
|
||||
Title: "download stalls", HTMLURL: "https://github.com/openclaw/gitcrawl/issues/1",
|
||||
LabelsJSON: "[]", AssigneesJSON: "[]", RawJSON: "{}", ContentHash: "hash", UpdatedAt: "2026-04-26T00:00:00Z",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("thread: %v", err)
|
||||
}
|
||||
if _, err := st.UpsertDocument(ctx, Document{ThreadID: threadID, Title: "download stalls", RawText: "artifact download stalls", DedupeText: "artifact download stalls", UpdatedAt: "2026-04-26T00:00:00Z"}); err != nil {
|
||||
t.Fatalf("document: %v", err)
|
||||
}
|
||||
|
||||
hits, err := st.SearchDocuments(ctx, repoID, "artifact", 10)
|
||||
if err != nil {
|
||||
t.Fatalf("search: %v", err)
|
||||
}
|
||||
if len(hits) != 1 || hits[0].Number != 1 {
|
||||
t.Fatalf("unexpected hits: %#v", hits)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user