108 lines
3.2 KiB
Go
108 lines
3.2 KiB
Go
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package db
|
|
|
|
import (
|
|
"context"
|
|
|
|
"code.gitea.io/gitea/models/db"
|
|
issue_model "code.gitea.io/gitea/models/issues"
|
|
indexer_internal "code.gitea.io/gitea/modules/indexer/internal"
|
|
inner_db "code.gitea.io/gitea/modules/indexer/internal/db"
|
|
"code.gitea.io/gitea/modules/indexer/issues/internal"
|
|
|
|
"xorm.io/builder"
|
|
)
|
|
|
|
var _ internal.Indexer = &Indexer{}
|
|
|
|
// Indexer implements Indexer interface to use database's like search
|
|
type Indexer struct {
|
|
indexer_internal.Indexer
|
|
}
|
|
|
|
func NewIndexer() *Indexer {
|
|
return &Indexer{
|
|
Indexer: &inner_db.Indexer{},
|
|
}
|
|
}
|
|
|
|
// Index dummy function
|
|
func (i *Indexer) Index(_ context.Context, _ ...*internal.IndexerData) error {
|
|
return nil
|
|
}
|
|
|
|
// Delete dummy function
|
|
func (i *Indexer) Delete(_ context.Context, _ ...int64) error {
|
|
return nil
|
|
}
|
|
|
|
// Search searches for issues
|
|
func (i *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (*internal.SearchResult, error) {
|
|
// FIXME: I tried to avoid importing models here, but it seems to be impossible.
|
|
// We can provide a function to register the search function, so models/issues can register it.
|
|
// So models/issues will import modules/indexer/issues, it's OK because it's by design.
|
|
// But modules/indexer/issues has already imported models/issues to do UpdateRepoIndexer and UpdateIssueIndexer.
|
|
// And to avoid circular import, we have to move the functions to another package.
|
|
// I believe it should be services/indexer, sounds great!
|
|
// But the two functions are used in modules/notification/indexer, that means we will import services/indexer in modules/notification/indexer.
|
|
// So that's the root problem:
|
|
// The notification is defined in modules, but it's using lots of things should be in services.
|
|
|
|
cond := builder.NewCond()
|
|
|
|
if options.Keyword != "" {
|
|
repoCond := builder.In("repo_id", options.RepoIDs)
|
|
if len(options.RepoIDs) == 1 {
|
|
repoCond = builder.Eq{"repo_id": options.RepoIDs[0]}
|
|
}
|
|
subQuery := builder.Select("id").From("issue").Where(repoCond)
|
|
|
|
cond = builder.Or(
|
|
db.BuildCaseInsensitiveLike("issue.name", options.Keyword),
|
|
db.BuildCaseInsensitiveLike("issue.content", options.Keyword),
|
|
builder.In("issue.id", builder.Select("issue_id").
|
|
From("comment").
|
|
Where(builder.And(
|
|
builder.Eq{"type": issue_model.CommentTypeComment},
|
|
builder.In("issue_id", subQuery),
|
|
db.BuildCaseInsensitiveLike("content", options.Keyword),
|
|
)),
|
|
),
|
|
)
|
|
}
|
|
|
|
opt, err := ToDBOptions(ctx, options)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// If pagesize == 0, return total count only. It's a special case for search count.
|
|
if options.Paginator != nil && options.Paginator.PageSize == 0 {
|
|
total, err := issue_model.CountIssues(ctx, opt, cond)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &internal.SearchResult{
|
|
Total: total,
|
|
}, nil
|
|
}
|
|
|
|
ids, total, err := issue_model.IssueIDs(ctx, opt, cond)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
hits := make([]internal.Match, 0, len(ids))
|
|
for _, id := range ids {
|
|
hits = append(hits, internal.Match{
|
|
ID: id,
|
|
})
|
|
}
|
|
return &internal.SearchResult{
|
|
Total: total,
|
|
Hits: hits,
|
|
}, nil
|
|
}
|