code.gitea.io/gitea@v1.22.3/modules/indexer/issues/db/db.go (about) 1 // Copyright 2019 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package db 5 6 import ( 7 "context" 8 9 "code.gitea.io/gitea/models/db" 10 issue_model "code.gitea.io/gitea/models/issues" 11 indexer_internal "code.gitea.io/gitea/modules/indexer/internal" 12 inner_db "code.gitea.io/gitea/modules/indexer/internal/db" 13 "code.gitea.io/gitea/modules/indexer/issues/internal" 14 15 "xorm.io/builder" 16 ) 17 18 var _ internal.Indexer = &Indexer{} 19 20 // Indexer implements Indexer interface to use database's like search 21 type Indexer struct { 22 indexer_internal.Indexer 23 } 24 25 func NewIndexer() *Indexer { 26 return &Indexer{ 27 Indexer: &inner_db.Indexer{}, 28 } 29 } 30 31 // Index dummy function 32 func (i *Indexer) Index(_ context.Context, _ ...*internal.IndexerData) error { 33 return nil 34 } 35 36 // Delete dummy function 37 func (i *Indexer) Delete(_ context.Context, _ ...int64) error { 38 return nil 39 } 40 41 // Search searches for issues 42 func (i *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (*internal.SearchResult, error) { 43 // FIXME: I tried to avoid importing models here, but it seems to be impossible. 44 // We can provide a function to register the search function, so models/issues can register it. 45 // So models/issues will import modules/indexer/issues, it's OK because it's by design. 46 // But modules/indexer/issues has already imported models/issues to do UpdateRepoIndexer and UpdateIssueIndexer. 47 // And to avoid circular import, we have to move the functions to another package. 48 // I believe it should be services/indexer, sounds great! 49 // But the two functions are used in modules/notification/indexer, that means we will import services/indexer in modules/notification/indexer. 50 // So that's the root problem: 51 // The notification is defined in modules, but it's using lots of things should be in services. 52 53 cond := builder.NewCond() 54 55 if options.Keyword != "" { 56 repoCond := builder.In("repo_id", options.RepoIDs) 57 if len(options.RepoIDs) == 1 { 58 repoCond = builder.Eq{"repo_id": options.RepoIDs[0]} 59 } 60 subQuery := builder.Select("id").From("issue").Where(repoCond) 61 62 cond = builder.Or( 63 db.BuildCaseInsensitiveLike("issue.name", options.Keyword), 64 db.BuildCaseInsensitiveLike("issue.content", options.Keyword), 65 builder.In("issue.id", builder.Select("issue_id"). 66 From("comment"). 67 Where(builder.And( 68 builder.Eq{"type": issue_model.CommentTypeComment}, 69 builder.In("issue_id", subQuery), 70 db.BuildCaseInsensitiveLike("content", options.Keyword), 71 )), 72 ), 73 ) 74 } 75 76 opt, err := ToDBOptions(ctx, options) 77 if err != nil { 78 return nil, err 79 } 80 81 // If pagesize == 0, return total count only. It's a special case for search count. 82 if options.Paginator != nil && options.Paginator.PageSize == 0 { 83 total, err := issue_model.CountIssues(ctx, opt, cond) 84 if err != nil { 85 return nil, err 86 } 87 return &internal.SearchResult{ 88 Total: total, 89 }, nil 90 } 91 92 ids, total, err := issue_model.IssueIDs(ctx, opt, cond) 93 if err != nil { 94 return nil, err 95 } 96 97 hits := make([]internal.Match, 0, len(ids)) 98 for _, id := range ids { 99 hits = append(hits, internal.Match{ 100 ID: id, 101 }) 102 } 103 return &internal.SearchResult{ 104 Total: total, 105 Hits: hits, 106 }, nil 107 }