From 1eefe67990d8130461b3b2bd6ac33bc7ebd0a41f Mon Sep 17 00:00:00 2001 From: Seongmin Lee Date: Sun, 13 Jul 2025 22:40:24 +0900 Subject: [PATCH] appview: create search endpoint (wip) Change-Id: nvsoqymumsnqvkktqxvrzrxmvqstnxpr Signed-off-by: Seongmin Lee --- appview/db/issues.go | 85 ++++++++++++++++++++++++++++--- appview/indexer/issues/indexer.go | 2 +- appview/issues/issues.go | 21 +++++++- 3 files changed, 99 insertions(+), 9 deletions(-) diff --git a/appview/db/issues.go b/appview/db/issues.go index 7f68d1b..cd50c96 100644 --- a/appview/db/issues.go +++ b/appview/db/issues.go @@ -2,6 +2,9 @@ package db import ( "database/sql" + "fmt" + "strconv" + "strings" "time" "github.com/bluesky-social/indigo/atproto/syntax" @@ -104,12 +107,8 @@ func GetIssueOwnerDid(e Execer, repoAt syntax.ATURI, issueId int) (string, error return ownerDid, err } -func GetIssues(e Execer, repoAt syntax.ATURI, isOpen bool, page pagination.Page) ([]Issue, error) { +func GetIssues(e Execer, repoAt syntax.ATURI, page pagination.Page) ([]Issue, error) { var issues []Issue - openValue := 0 - if isOpen { - openValue = 1 - } rows, err := e.Query( ` @@ -129,7 +128,7 @@ func GetIssues(e Execer, repoAt syntax.ATURI, isOpen bool, page pagination.Page) left join comments c on i.repo_at = c.repo_at and i.issue_id = c.issue_id where - i.repo_at = ? and i.open = ? + i.repo_at = ? group by i.id, i.owner_did, i.issue_id, i.created, i.title, i.body, i.open ) @@ -146,7 +145,7 @@ func GetIssues(e Execer, repoAt syntax.ATURI, isOpen bool, page pagination.Page) numbered_issue where row_num between ? and ?`, - repoAt, openValue, page.Offset+1, page.Offset+page.Limit) + repoAt, page.Offset+1, page.Offset+page.Limit) if err != nil { return nil, err } @@ -178,6 +177,78 @@ func GetIssues(e Execer, repoAt syntax.ATURI, isOpen bool, page pagination.Page) return issues, nil } +func GetIssuesByIDs(e Execer, ids []int64) ([]Issue, error) { + var issues []Issue + + // HACK: would be better to create "?,?,?,..." or use something like sqlx + idStrings := make([]string, len(ids)) + for i, id := range ids { + idStrings[i] = strconv.FormatInt(id, 10) + } + idList := strings.Join(idStrings, ",") + query := fmt.Sprintf( + ` + select + i.id, + i.owner_did, + i.issue_id, + i.created, + i.title, + i.body, + i.open, + count(c.id) as comment_count + from + issues i + left join + comments c on i.repo_at = c.repo_at and i.issue_id = c.issue_id + where + i.id in (%s) + group by + i.id + order by i.created desc`, + idList, + ) + rows, err := e.Query(query) + if err != nil { + return nil, err + } + defer rows.Close() + + for rows.Next() { + var issue Issue + var createdAt string + var metadata IssueMetadata + err := rows.Scan( + &issue.ID, + &issue.OwnerDid, + &issue.IssueId, + &createdAt, + &issue.Title, + &issue.Body, + &issue.Open, + &metadata.CommentCount, + ) + if err != nil { + return nil, err + } + + createdTime, err := time.Parse(time.RFC3339, createdAt) + if err != nil { + return nil, err + } + issue.Created = createdTime + issue.Metadata = &metadata + + issues = append(issues, issue) + } + + if err := rows.Err(); err != nil { + return nil, err + } + + return issues, nil +} + // timeframe here is directly passed into the sql query filter, and any // timeframe in the past should be negative; e.g.: "-3 months" func GetIssuesByOwnerDid(e Execer, ownerDid string, timeframe string) ([]Issue, error) { diff --git a/appview/indexer/issues/indexer.go b/appview/indexer/issues/indexer.go index 13884b8..d65d541 100644 --- a/appview/indexer/issues/indexer.go +++ b/appview/indexer/issues/indexer.go @@ -87,7 +87,7 @@ func PopulateIndexer(ctx context.Context, ix *Indexer, e db.Execer) error { } for _, repo := range repos { page := pagination.FirstPage() - issues, err := db.GetIssues(e, repo.RepoAt(), true, page) + issues, err := db.GetIssues(e, repo.RepoAt(), page) if err != nil { return err } diff --git a/appview/issues/issues.go b/appview/issues/issues.go index 1ff3d6e..72dc468 100644 --- a/appview/issues/issues.go +++ b/appview/issues/issues.go @@ -620,6 +620,19 @@ func (rp *Issues) RepoIssues(w http.ResponseWriter, r *http.Request) { isOpen = true } + keyword := params.Get("q") + + res, err := rp.indexer.Search(r.Context(), issues_indexer.SearchOptions{ + Keyword: keyword, + IsOpen: &isOpen, + Page: pagination.FirstPage(), + }) + if err != nil { + log.Println("failed to search for issues", err) + return + } + log.Println("searched issues:", res.Hits) + page, ok := r.Context().Value("page").(pagination.Page) if !ok { log.Println("failed to get page") @@ -633,7 +646,13 @@ func (rp *Issues) RepoIssues(w http.ResponseWriter, r *http.Request) { return } - issues, err := db.GetIssues(rp.db, f.RepoAt, isOpen, page) + // issues, err := db.GetIssues(rp.db, f.RepoAt, isOpen, page) + // if err != nil { + // log.Println("failed to get issues", err) + // rp.pages.Notice(w, "issues", "Failed to load issues. Try again later.") + // return + // } + issues, err := db.GetIssuesByIDs(rp.db, res.Hits) if err != nil { log.Println("failed to get issues", err) rp.pages.Notice(w, "issues", "Failed to load issues. Try again later.") -- 2.43.0