Monorepo for Tangled tangled.org

appview: add basic issue indexer (wip) #494

merged opened by boltless.me targeting master from boltless.me/core: feat/search
  • Heavily inspired by gitea
  • add GetAllIssues which only receives a paginator and gathers all issues ignoring repoAt field

Signed-off-by: Seongmin Lee boltlessengineer@proton.me

Labels

None yet.

assignee

None yet.

Participants 2
AT URI
at://did:plc:xasnlahkri4ewmbuzly2rlc5/sh.tangled.repo.pull/3lwecnvoz4z22
+504 -1
Diff #0
+1
.gitignore
··· 15 15 .env 16 16 *.rdb 17 17 .envrc 18 + *.bleve 18 19 # Created if following hacking.md 19 20 genjwks.out 20 21 /nix/vm-data
+56
appview/db/issues.go
··· 104 104 return ownerDid, err 105 105 } 106 106 107 + func GetAllIssues(e Execer, page pagination.Page) ([]Issue, error) { 108 + var issues []Issue 109 + rows, err := e.Query( 110 + ` 111 + select 112 + i.id, 113 + i.owner_did, 114 + i.repo_at, 115 + i.issue_id, 116 + i.created, 117 + i.title, 118 + i.body, 119 + i.open, 120 + count(c.id) as comment_count 121 + from 122 + issues i 123 + left join 124 + comments c on i.repo_at = c.repo_at and i.issue_id = c.issue_id 125 + group by 126 + i.id 127 + order by i.created desc 128 + limit ? offset ?`, 129 + page.Limit, 130 + page.Offset, 131 + ) 132 + if err != nil { 133 + return nil, err 134 + } 135 + defer rows.Close() 136 + 137 + for rows.Next() { 138 + var issue Issue 139 + var createdAt string 140 + var metadata IssueMetadata 141 + err := rows.Scan(&issue.ID, &issue.OwnerDid, &issue.RepoAt, &issue.IssueId, &createdAt, &issue.Title, &issue.Body, &issue.Open, &metadata.CommentCount) 142 + if err != nil { 143 + return nil, err 144 + } 145 + 146 + createdTime, err := time.Parse(time.RFC3339, createdAt) 147 + if err != nil { 148 + return nil, err 149 + } 150 + issue.Created = createdTime 151 + issue.Metadata = &metadata 152 + 153 + issues = append(issues, issue) 154 + } 155 + 156 + if err := rows.Err(); err != nil { 157 + return nil, err 158 + } 159 + 160 + return issues, nil 161 + } 162 + 107 163 func GetIssues(e Execer, repoAt syntax.ATURI, isOpen bool, page pagination.Page) ([]Issue, error) { 108 164 var issues []Issue 109 165 openValue := 0
+20
appview/indexer/base36/base36.go
··· 1 + // mostly copied from gitea/modules/indexer/internal/base32 2 + 3 + package base36 4 + 5 + import ( 6 + "fmt" 7 + "strconv" 8 + ) 9 + 10 + func Encode(i int64) string { 11 + return strconv.FormatInt(i, 36) 12 + } 13 + 14 + func Decode(s string) (int64, error) { 15 + i, err := strconv.ParseInt(s, 36, 64) 16 + if err != nil { 17 + return 0, fmt.Errorf("invalid base36 integer %q: %w", s, err) 18 + } 19 + return i, nil 20 + }
+58
appview/indexer/bleve/batch.go
··· 1 + // Copyright 2021 The Gitea Authors. All rights reserved. 2 + // SPDX-License-Identifier: MIT 3 + 4 + package bleveutil 5 + 6 + import ( 7 + "github.com/blevesearch/bleve/v2" 8 + ) 9 + 10 + // FlushingBatch is a batch of operations that automatically flushes to the 11 + // underlying index once it reaches a certain size. 12 + type FlushingBatch struct { 13 + maxBatchSize int 14 + batch *bleve.Batch 15 + index bleve.Index 16 + } 17 + 18 + // NewFlushingBatch creates a new flushing batch for the specified index. Once 19 + // the number of operations in the batch reaches the specified limit, the batch 20 + // automatically flushes its operations to the index. 21 + func NewFlushingBatch(index bleve.Index, maxBatchSize int) *FlushingBatch { 22 + return &FlushingBatch{ 23 + maxBatchSize: maxBatchSize, 24 + batch: index.NewBatch(), 25 + index: index, 26 + } 27 + } 28 + 29 + // Index add a new index to batch 30 + func (b *FlushingBatch) Index(id string, data any) error { 31 + if err := b.batch.Index(id, data); err != nil { 32 + return err 33 + } 34 + return b.flushIfFull() 35 + } 36 + 37 + // Delete add a delete index to batch 38 + func (b *FlushingBatch) Delete(id string) error { 39 + b.batch.Delete(id) 40 + return b.flushIfFull() 41 + } 42 + 43 + func (b *FlushingBatch) flushIfFull() error { 44 + if b.batch.Size() < b.maxBatchSize { 45 + return nil 46 + } 47 + return b.Flush() 48 + } 49 + 50 + // Flush submit the batch and create a new one 51 + func (b *FlushingBatch) Flush() error { 52 + err := b.index.Batch(b.batch) 53 + if err != nil { 54 + return err 55 + } 56 + b.batch = b.index.NewBatch() 57 + return nil 58 + }
+27
appview/indexer/indexer.go
··· 1 + package indexer 2 + 3 + import ( 4 + "context" 5 + 6 + "tangled.sh/tangled.sh/core/appview/db" 7 + issues_indexer "tangled.sh/tangled.sh/core/appview/indexer/issues" 8 + "tangled.sh/tangled.sh/core/appview/notify" 9 + ) 10 + 11 + type Indexer struct { 12 + Issues *issues_indexer.Indexer 13 + notify.BaseNotifier 14 + } 15 + 16 + func New() *Indexer { 17 + return &Indexer { 18 + issues_indexer.NewIndexer("indexes.bleve"), 19 + notify.BaseNotifier{}, 20 + } 21 + } 22 + 23 + // Init initializes all indexers 24 + func (ix *Indexer) Init(ctx context.Context, db *db.DB) error { 25 + ix.Issues.Init(ctx, db) 26 + return nil 27 + }
+192
appview/indexer/issues/indexer.go
··· 1 + package issues_indexer 2 + 3 + import ( 4 + "context" 5 + "errors" 6 + "log" 7 + "os" 8 + 9 + "github.com/blevesearch/bleve/v2" 10 + "github.com/blevesearch/bleve/v2/index/upsidedown" 11 + "github.com/blevesearch/bleve/v2/search/query" 12 + "tangled.sh/tangled.sh/core/appview/db" 13 + "tangled.sh/tangled.sh/core/appview/indexer/base36" 14 + "tangled.sh/tangled.sh/core/appview/indexer/bleve" 15 + "tangled.sh/tangled.sh/core/appview/models" 16 + "tangled.sh/tangled.sh/core/appview/pagination" 17 + ) 18 + 19 + type Indexer struct { 20 + indexer bleve.Index 21 + path string 22 + } 23 + 24 + func NewIndexer(indexDir string) *Indexer { 25 + return &Indexer{ 26 + path: indexDir, 27 + } 28 + } 29 + 30 + // Init initializes the indexer 31 + func (ix *Indexer) Init(ctx context.Context, e db.Execer) { 32 + existed, err := ix.intialize(ctx) 33 + if err != nil { 34 + log.Fatalf("failed to initialize issue indexer: %v", err) 35 + } 36 + if !existed { 37 + log.Println("Populating the issue indexer") 38 + err := PopulateIndexer(ctx, ix, e) 39 + if err != nil { 40 + log.Fatalf("failed to populate issue indexer: %v", err) 41 + } 42 + } 43 + log.Println("Initialized the issue indexer") 44 + } 45 + 46 + func (ix *Indexer) intialize(_ context.Context) (bool, error) { 47 + if ix.indexer != nil { 48 + return false, errors.New("indexer is already initialized") 49 + } 50 + 51 + indexer, err := openIndexer(ix.path) 52 + if err != nil { 53 + return false, nil 54 + } 55 + if indexer != nil { 56 + ix.indexer = indexer 57 + return true, nil 58 + } 59 + 60 + mapping := bleve.NewIndexMapping() 61 + indexer, err = bleve.New(ix.path, mapping) 62 + if err != nil { 63 + return false, err 64 + } 65 + 66 + ix.indexer = indexer 67 + 68 + return false, nil 69 + } 70 + 71 + func openIndexer(path string) (bleve.Index, error) { 72 + _, err := os.Stat(path) 73 + indexer, err := bleve.Open(path) 74 + if err != nil { 75 + if errors.Is(err, upsidedown.IncompatibleVersion) { 76 + log.Println("Indexer was built with a previous version of bleve, deleting and rebuilding") 77 + return nil, os.RemoveAll(path) 78 + } 79 + return nil, nil 80 + } 81 + return indexer, nil 82 + } 83 + 84 + func PopulateIndexer(ctx context.Context, ix *Indexer, e db.Execer) error { 85 + page := pagination.FirstPage() 86 + for { 87 + issues, err := db.GetAllIssues(e, page) 88 + if err != nil { 89 + return err 90 + } 91 + var dataList []*IssueData 92 + for _, issue := range issues { 93 + issue, _, err := db.GetIssueWithComments(e, issue.RepoAt, issue.IssueId) 94 + if err != nil { 95 + return err 96 + } 97 + dataList = append(dataList, &IssueData{ 98 + ID: issue.ID, 99 + IssueID: issue.IssueId, 100 + Title: issue.Title, 101 + Body: issue.Body, 102 + IsOpen: issue.Open, 103 + }) 104 + } 105 + err = ix.Index(ctx, dataList...) 106 + if err != nil { 107 + return err 108 + } 109 + if len(issues) < page.Limit { 110 + break 111 + } 112 + page = page.Next() 113 + } 114 + return nil 115 + } 116 + 117 + // IssueData data stored and will be indexed 118 + type IssueData struct { 119 + ID int64 `json:"id"` 120 + IssueID int `json:"issue_id"` 121 + Title string `json:"title"` 122 + Body string `json:"body"` 123 + 124 + IsOpen bool `json:"is_open"` 125 + Comments []IssueCommentData `json:"comments"` 126 + } 127 + 128 + type IssueCommentData struct { 129 + Body string `json:"body"` 130 + } 131 + 132 + type SearchResult struct { 133 + Hits []int64 134 + Total uint64 135 + } 136 + 137 + const maxBatchSize = 20 138 + 139 + func (ix *Indexer) Index(ctx context.Context, issues ...*IssueData) error { 140 + batch := bleveutil.NewFlushingBatch(ix.indexer, maxBatchSize) 141 + for _, issue := range issues { 142 + if err := batch.Index(base36.Encode(issue.ID), issue); err != nil { 143 + return err 144 + } 145 + } 146 + return batch.Flush() 147 + } 148 + 149 + // Search searches for issues 150 + func (ix *Indexer) Search(ctx context.Context, opts models.IssueSearchOptions) (*SearchResult, error) { 151 + var queries []query.Query 152 + 153 + if opts.Keyword != "" { 154 + queries = append(queries, bleve.NewDisjunctionQuery( 155 + matchAndQuery(opts.Keyword, "title"), 156 + matchAndQuery(opts.Keyword, "body"), 157 + )) 158 + } 159 + queries = append(queries, boolFieldQuery(opts.IsOpen, "is_open")) 160 + // TODO: append more queries 161 + 162 + var indexerQuery query.Query = bleve.NewConjunctionQuery(queries...) 163 + searchReq := bleve.NewSearchRequestOptions(indexerQuery, opts.Page.Limit, opts.Page.Offset, false) 164 + res, err := ix.indexer.SearchInContext(ctx, searchReq) 165 + if err != nil { 166 + return nil, nil 167 + } 168 + ret := &SearchResult{ 169 + Total: res.Total, 170 + Hits: make([]int64, len(res.Hits)), 171 + } 172 + for i, hit := range res.Hits { 173 + id, err := base36.Decode(hit.ID) 174 + if err != nil { 175 + return nil, err 176 + } 177 + ret.Hits[i] = id 178 + } 179 + return ret, nil 180 + } 181 + 182 + func matchAndQuery(keyword, field string) query.Query { 183 + q := bleve.NewMatchQuery(keyword) 184 + q.FieldVal = field 185 + return q 186 + } 187 + 188 + func boolFieldQuery(val bool, field string) query.Query { 189 + q := bleve.NewBoolFieldQuery(val) 190 + q.FieldVal = field 191 + return q 192 + }
+26
appview/indexer/notifier.go
··· 1 + package indexer 2 + 3 + import ( 4 + "context" 5 + 6 + "tangled.sh/tangled.sh/core/appview/db" 7 + issues_indexer "tangled.sh/tangled.sh/core/appview/indexer/issues" 8 + "tangled.sh/tangled.sh/core/appview/notify" 9 + ) 10 + 11 + var _ notify.Notifier = &Indexer{} 12 + 13 + func (ix *Indexer) NewIssue(ctx context.Context, issue *db.Issue) { 14 + ix.Issues.Index(ctx, &issues_indexer.IssueData{ 15 + ID: issue.ID, 16 + IssueID: issue.IssueId, 17 + Title: issue.Title, 18 + Body: issue.Body, 19 + IsOpen: issue.Open, 20 + Comments: []issues_indexer.IssueCommentData{}, 21 + }) 22 + } 23 + 24 + func (ix *Indexer) NewPullComment(ctx context.Context, comment *db.PullComment) { 25 + panic("unimplemented") 26 + }
+4
appview/issues/issues.go
··· 18 18 "tangled.sh/tangled.sh/core/api/tangled" 19 19 "tangled.sh/tangled.sh/core/appview/config" 20 20 "tangled.sh/tangled.sh/core/appview/db" 21 + issues_indexer "tangled.sh/tangled.sh/core/appview/indexer/issues" 21 22 "tangled.sh/tangled.sh/core/appview/notify" 22 23 "tangled.sh/tangled.sh/core/appview/oauth" 23 24 "tangled.sh/tangled.sh/core/appview/pages" ··· 35 36 db *db.DB 36 37 config *config.Config 37 38 notifier notify.Notifier 39 + indexer *issues_indexer.Indexer 38 40 } 39 41 40 42 func New( ··· 45 47 db *db.DB, 46 48 config *config.Config, 47 49 notifier notify.Notifier, 50 + indexer *issues_indexer.Indexer, 48 51 ) *Issues { 49 52 return &Issues{ 50 53 oauth: oauth, ··· 54 57 db: db, 55 58 config: config, 56 59 notifier: notifier, 60 + indexer: indexer, 57 61 } 58 62 } 59 63
+23
appview/models/search.go
··· 1 + package models 2 + 3 + import "tangled.sh/tangled.sh/core/appview/pagination" 4 + 5 + type IssueSearchOptions struct { 6 + Keyword string 7 + RepoAt string 8 + IsOpen bool 9 + 10 + Page pagination.Page 11 + } 12 + 13 + // func (so *SearchOptions) ToFilters() []filter { 14 + // var filters []filter 15 + // if so.IsOpen != nil { 16 + // openValue := 0 17 + // if *so.IsOpen { 18 + // openValue = 1 19 + // } 20 + // filters = append(filters, FilterEq("open", openValue)) 21 + // } 22 + // return filters 23 + // }
+1
appview/pages/pages.go
··· 776 776 Issues []db.Issue 777 777 Page pagination.Page 778 778 FilteringByOpen bool 779 + FilterQuery string 779 780 } 780 781 781 782 func (p *Pages) RepoIssues(w io.Writer, params RepoIssuesParams) error {
+1 -1
appview/state/router.go
··· 219 219 } 220 220 221 221 func (s *State) IssuesRouter(mw *middleware.Middleware) http.Handler { 222 - issues := issues.New(s.oauth, s.repoResolver, s.pages, s.idResolver, s.db, s.config, s.notifier) 222 + issues := issues.New(s.oauth, s.repoResolver, s.pages, s.idResolver, s.db, s.config, s.notifier, s.indexer.Issues) 223 223 return issues.Router(mw) 224 224 } 225 225
+10
appview/state/state.go
··· 20 20 "tangled.sh/tangled.sh/core/appview/cache/session" 21 21 "tangled.sh/tangled.sh/core/appview/config" 22 22 "tangled.sh/tangled.sh/core/appview/db" 23 + "tangled.sh/tangled.sh/core/appview/indexer" 23 24 "tangled.sh/tangled.sh/core/appview/notify" 24 25 "tangled.sh/tangled.sh/core/appview/oauth" 25 26 "tangled.sh/tangled.sh/core/appview/pages" ··· 37 38 type State struct { 38 39 db *db.DB 39 40 notifier notify.Notifier 41 + indexer *indexer.Indexer 40 42 oauth *oauth.OAuth 41 43 enforcer *rbac.Enforcer 42 44 pages *pages.Pages ··· 56 58 return nil, fmt.Errorf("failed to create db: %w", err) 57 59 } 58 60 61 + indexer := indexer.New() 62 + err = indexer.Init(ctx, d) 63 + if err != nil { 64 + return nil, fmt.Errorf("failed to create indexer: %w", err) 65 + } 66 + 59 67 enforcer, err := rbac.NewEnforcer(config.Core.DbPath) 60 68 if err != nil { 61 69 return nil, fmt.Errorf("failed to create enforcer: %w", err) ··· 136 144 if !config.Core.Dev { 137 145 notifiers = append(notifiers, posthogService.NewPosthogNotifier(posthog)) 138 146 } 147 + notifiers = append(notifiers, indexer) 139 148 notifier := notify.NewMergedNotifier(notifiers...) 140 149 141 150 state := &State{ 142 151 d, 143 152 notifier, 153 + indexer, 144 154 oauth, 145 155 enforcer, 146 156 pgs,
+27
go.mod
··· 51 51 dario.cat/mergo v1.0.1 // indirect 52 52 github.com/Microsoft/go-winio v0.6.2 // indirect 53 53 github.com/ProtonMail/go-crypto v1.3.0 // indirect 54 + github.com/RoaringBitmap/roaring/v2 v2.4.5 // indirect 54 55 github.com/alecthomas/repr v0.4.0 // indirect 55 56 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect 56 57 github.com/aymerick/douceur v0.2.0 // indirect 57 58 github.com/beorn7/perks v1.0.1 // indirect 59 + github.com/bits-and-blooms/bitset v1.22.0 // indirect 60 + github.com/blevesearch/bleve/v2 v2.5.3 // indirect 61 + github.com/blevesearch/bleve_index_api v1.2.8 // indirect 62 + github.com/blevesearch/geo v0.2.4 // indirect 63 + github.com/blevesearch/go-faiss v1.0.25 // indirect 64 + github.com/blevesearch/go-porterstemmer v1.0.3 // indirect 65 + github.com/blevesearch/gtreap v0.1.1 // indirect 66 + github.com/blevesearch/mmap-go v1.0.4 // indirect 67 + github.com/blevesearch/scorch_segment_api/v2 v2.3.10 // indirect 68 + github.com/blevesearch/segment v0.9.1 // indirect 69 + github.com/blevesearch/snowballstem v0.9.0 // indirect 70 + github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect 71 + github.com/blevesearch/vellum v1.1.0 // indirect 72 + github.com/blevesearch/zapx/v11 v11.4.2 // indirect 73 + github.com/blevesearch/zapx/v12 v12.4.2 // indirect 74 + github.com/blevesearch/zapx/v13 v13.4.2 // indirect 75 + github.com/blevesearch/zapx/v14 v14.4.2 // indirect 76 + github.com/blevesearch/zapx/v15 v15.4.2 // indirect 77 + github.com/blevesearch/zapx/v16 v16.2.4 // indirect 58 78 github.com/bmatcuk/doublestar/v4 v4.7.1 // indirect 59 79 github.com/casbin/govaluate v1.3.0 // indirect 60 80 github.com/cenkalti/backoff/v4 v4.3.0 // indirect ··· 86 106 github.com/golang-jwt/jwt/v5 v5.2.3 // indirect 87 107 github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect 88 108 github.com/golang/mock v1.6.0 // indirect 109 + github.com/golang/protobuf v1.5.4 // indirect 110 + github.com/golang/snappy v0.0.4 // indirect 89 111 github.com/google/go-querystring v1.1.0 // indirect 90 112 github.com/gorilla/css v1.0.1 // indirect 91 113 github.com/gorilla/feeds v1.2.0 // indirect ··· 112 134 github.com/ipfs/go-log v1.0.5 // indirect 113 135 github.com/ipfs/go-log/v2 v2.6.0 // indirect 114 136 github.com/ipfs/go-metrics-interface v0.3.0 // indirect 137 + github.com/json-iterator/go v1.1.12 // indirect 115 138 github.com/kevinburke/ssh_config v1.2.0 // indirect 116 139 github.com/klauspost/compress v1.18.0 // indirect 117 140 github.com/klauspost/cpuid/v2 v2.3.0 // indirect ··· 126 149 github.com/moby/docker-image-spec v1.3.1 // indirect 127 150 github.com/moby/sys/atomicwriter v0.1.0 // indirect 128 151 github.com/moby/term v0.5.2 // indirect 152 + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 153 + github.com/modern-go/reflect2 v1.0.2 // indirect 129 154 github.com/morikuni/aec v1.0.0 // indirect 130 155 github.com/mr-tron/base58 v1.2.0 // indirect 156 + github.com/mschoch/smat v0.2.0 // indirect 131 157 github.com/multiformats/go-base32 v0.1.0 // indirect 132 158 github.com/multiformats/go-base36 v0.2.0 // indirect 133 159 github.com/multiformats/go-multibase v0.2.0 // indirect ··· 155 181 github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect 156 182 gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b // indirect 157 183 gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 // indirect 184 + go.etcd.io/bbolt v1.4.0 // indirect 158 185 go.opentelemetry.io/auto/sdk v1.1.0 // indirect 159 186 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 // indirect 160 187 go.opentelemetry.io/otel v1.37.0 // indirect
+58
go.sum
··· 9 9 github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= 10 10 github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw= 11 11 github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= 12 + github.com/RoaringBitmap/roaring/v2 v2.4.5 h1:uGrrMreGjvAtTBobc0g5IrW1D5ldxDQYe2JW2gggRdg= 13 + github.com/RoaringBitmap/roaring/v2 v2.4.5/go.mod h1:FiJcsfkGje/nZBZgCu0ZxCPOKD/hVXDS2dXi7/eUFE0= 12 14 github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= 13 15 github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= 14 16 github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= ··· 23 25 github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= 24 26 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 25 27 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 28 + github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= 29 + github.com/bits-and-blooms/bitset v1.22.0 h1:Tquv9S8+SGaS3EhyA+up3FXzmkhxPGjQQCkcs2uw7w4= 30 + github.com/bits-and-blooms/bitset v1.22.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= 31 + github.com/blevesearch/bleve/v2 v2.5.3 h1:9l1xtKaETv64SZc1jc4Sy0N804laSa/LeMbYddq1YEM= 32 + github.com/blevesearch/bleve/v2 v2.5.3/go.mod h1:Z/e8aWjiq8HeX+nW8qROSxiE0830yQA071dwR3yoMzw= 33 + github.com/blevesearch/bleve_index_api v1.2.8 h1:Y98Pu5/MdlkRyLM0qDHostYo7i+Vv1cDNhqTeR4Sy6Y= 34 + github.com/blevesearch/bleve_index_api v1.2.8/go.mod h1:rKQDl4u51uwafZxFrPD1R7xFOwKnzZW7s/LSeK4lgo0= 35 + github.com/blevesearch/geo v0.2.4 h1:ECIGQhw+QALCZaDcogRTNSJYQXRtC8/m8IKiA706cqk= 36 + github.com/blevesearch/geo v0.2.4/go.mod h1:K56Q33AzXt2YExVHGObtmRSFYZKYGv0JEN5mdacJJR8= 37 + github.com/blevesearch/go-faiss v1.0.25 h1:lel1rkOUGbT1CJ0YgzKwC7k+XH0XVBHnCVWahdCXk4U= 38 + github.com/blevesearch/go-faiss v1.0.25/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk= 39 + github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo= 40 + github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M= 41 + github.com/blevesearch/gtreap v0.1.1 h1:2JWigFrzDMR+42WGIN/V2p0cUvn4UP3C4Q5nmaZGW8Y= 42 + github.com/blevesearch/gtreap v0.1.1/go.mod h1:QaQyDRAT51sotthUWAH4Sj08awFSSWzgYICSZ3w0tYk= 43 + github.com/blevesearch/mmap-go v1.0.4 h1:OVhDhT5B/M1HNPpYPBKIEJaD0F3Si+CrEKULGCDPWmc= 44 + github.com/blevesearch/mmap-go v1.0.4/go.mod h1:EWmEAOmdAS9z/pi/+Toxu99DnsbhG1TIxUoRmJw/pSs= 45 + github.com/blevesearch/scorch_segment_api/v2 v2.3.10 h1:Yqk0XD1mE0fDZAJXTjawJ8If/85JxnLd8v5vG/jWE/s= 46 + github.com/blevesearch/scorch_segment_api/v2 v2.3.10/go.mod h1:Z3e6ChN3qyN35yaQpl00MfI5s8AxUJbpTR/DL8QOQ+8= 47 + github.com/blevesearch/segment v0.9.1 h1:+dThDy+Lvgj5JMxhmOVlgFfkUtZV2kw49xax4+jTfSU= 48 + github.com/blevesearch/segment v0.9.1/go.mod h1:zN21iLm7+GnBHWTao9I+Au/7MBiL8pPFtJBJTsk6kQw= 49 + github.com/blevesearch/snowballstem v0.9.0 h1:lMQ189YspGP6sXvZQ4WZ+MLawfV8wOmPoD/iWeNXm8s= 50 + github.com/blevesearch/snowballstem v0.9.0/go.mod h1:PivSj3JMc8WuaFkTSRDW2SlrulNWPl4ABg1tC/hlgLs= 51 + github.com/blevesearch/upsidedown_store_api v1.0.2 h1:U53Q6YoWEARVLd1OYNc9kvhBMGZzVrdmaozG2MfoB+A= 52 + github.com/blevesearch/upsidedown_store_api v1.0.2/go.mod h1:M01mh3Gpfy56Ps/UXHjEO/knbqyQ1Oamg8If49gRwrQ= 53 + github.com/blevesearch/vellum v1.1.0 h1:CinkGyIsgVlYf8Y2LUQHvdelgXr6PYuvoDIajq6yR9w= 54 + github.com/blevesearch/vellum v1.1.0/go.mod h1:QgwWryE8ThtNPxtgWJof5ndPfx0/YMBh+W2weHKPw8Y= 55 + github.com/blevesearch/zapx/v11 v11.4.2 h1:l46SV+b0gFN+Rw3wUI1YdMWdSAVhskYuvxlcgpQFljs= 56 + github.com/blevesearch/zapx/v11 v11.4.2/go.mod h1:4gdeyy9oGa/lLa6D34R9daXNUvfMPZqUYjPwiLmekwc= 57 + github.com/blevesearch/zapx/v12 v12.4.2 h1:fzRbhllQmEMUuAQ7zBuMvKRlcPA5ESTgWlDEoB9uQNE= 58 + github.com/blevesearch/zapx/v12 v12.4.2/go.mod h1:TdFmr7afSz1hFh/SIBCCZvcLfzYvievIH6aEISCte58= 59 + github.com/blevesearch/zapx/v13 v13.4.2 h1:46PIZCO/ZuKZYgxI8Y7lOJqX3Irkc3N8W82QTK3MVks= 60 + github.com/blevesearch/zapx/v13 v13.4.2/go.mod h1:knK8z2NdQHlb5ot/uj8wuvOq5PhDGjNYQQy0QDnopZk= 61 + github.com/blevesearch/zapx/v14 v14.4.2 h1:2SGHakVKd+TrtEqpfeq8X+So5PShQ5nW6GNxT7fWYz0= 62 + github.com/blevesearch/zapx/v14 v14.4.2/go.mod h1:rz0XNb/OZSMjNorufDGSpFpjoFKhXmppH9Hi7a877D8= 63 + github.com/blevesearch/zapx/v15 v15.4.2 h1:sWxpDE0QQOTjyxYbAVjt3+0ieu8NCE0fDRaFxEsp31k= 64 + github.com/blevesearch/zapx/v15 v15.4.2/go.mod h1:1pssev/59FsuWcgSnTa0OeEpOzmhtmr/0/11H0Z8+Nw= 65 + github.com/blevesearch/zapx/v16 v16.2.4 h1:tGgfvleXTAkwsD5mEzgM3zCS/7pgocTCnO1oyAUjlww= 66 + github.com/blevesearch/zapx/v16 v16.2.4/go.mod h1:Rti/REtuuMmzwsI8/C/qIzRaEoSK/wiFYw5e5ctUKKs= 26 67 github.com/bluesky-social/indigo v0.0.0-20250724221105-5827c8fb61bb h1:BqMNDZMfXwiRTJ6NvQotJ0qInn37JH5U8E+TF01CFHQ= 27 68 github.com/bluesky-social/indigo v0.0.0-20250724221105-5827c8fb61bb/go.mod h1:0XUyOCRtL4/OiyeqMTmr6RlVHQMDgw3LS7CfibuZR5Q= 28 69 github.com/bluesky-social/jetstream v0.0.0-20241210005130-ea96859b93d1 h1:CFvRtYNSnWRAi/98M3O466t9dYuwtesNbu6FVPymRrA= ··· 151 192 github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 152 193 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 153 194 github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 195 + github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= 196 + github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 197 + github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= 198 + github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 154 199 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 155 200 github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 156 201 github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= ··· 162 207 github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 163 208 github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= 164 209 github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= 210 + github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 165 211 github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= 166 212 github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 167 213 github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= ··· 244 290 github.com/ipfs/go-metrics-interface v0.3.0/go.mod h1:OxxQjZDGocXVdyTPocns6cOLwHieqej/jos7H4POwoY= 245 291 github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= 246 292 github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= 293 + github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 294 + github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 247 295 github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 248 296 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 249 297 github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= ··· 295 343 github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= 296 344 github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= 297 345 github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= 346 + github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 347 + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 348 + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 349 + github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 350 + github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 298 351 github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= 299 352 github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= 300 353 github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= 301 354 github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= 355 + github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM= 356 + github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw= 302 357 github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= 303 358 github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= 304 359 github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= ··· 435 490 gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b/go.mod h1:/y/V339mxv2sZmYYR64O07VuCpdNZqCTwO8ZcouTMI8= 436 491 gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 h1:qwDnMxjkyLmAFgcfgTnfJrmYKWhHnci3GjDqcZp1M3Q= 437 492 gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02/go.mod h1:JTnUj0mpYiAsuZLmKjTx/ex3AtMowcCgnE7YNyCEP0I= 493 + go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk= 494 + go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= 438 495 go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= 439 496 go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= 440 497 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 h1:Hf9xI/XLML9ElpiHVDNwvqI0hIFlzV8dgIr35kV1kRU= ··· 636 693 gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 637 694 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 638 695 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 696 + gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 639 697 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 640 698 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 641 699 gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=

History

15 rounds 2 comments
sign up or login to add to the discussion
1 commit
expand
appview: add basic issue indexer
expand 0 comments
pull request successfully merged
1 commit
expand
appview: add basic issue indexer
expand 0 comments
1 commit
expand
appview: add basic issue indexer
expand 0 comments
1 commit
expand
appview: add basic issue indexer
expand 0 comments
1 commit
expand
appview: add basic issue indexer
expand 0 comments
1 commit
expand
appview: add basic issue indexer
expand 0 comments
1 commit
expand
appview: add basic issue indexer
expand 0 comments
1 commit
expand
appview: add basic issue indexer
expand 0 comments
1 commit
expand
appview: add basic issue indexer
expand 1 comment

this changeset largely lgtm!

1 commit
expand
appview: add basic issue indexer
expand 0 comments
1 commit
expand
appview: add basic issue indexer
expand 0 comments
1 commit
expand
appview: add basic issue indexer
expand 1 comment
  • this is an N+1 query, because we first get all issues and then make one query for each issue. understandably this only needs to happen when populating the indices but we could improve this by just making one query
  • we can use a pagination free API for getting issues with comments while populating indexer
  • more of a note to self: search index should also handle issue deletions
1 commit
expand
appview: add basic issue indexer
expand 0 comments
1 commit
expand
appview: add basic issue indexer
expand 0 comments
boltless.me submitted #0
1 commit
expand
appview: add basic issue indexer (wip)
expand 0 comments