mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-11-25 14:55:40 +03:00
Add open/closed field support for issue index (#25708)
A couple of notes: * Future changes should refactor arguments into a struct * This filtering only is supported by meilisearch right now * Issue index number is bumped which will cause a re-index
This commit is contained in:
parent
7586b5815a
commit
cb01b8691d
12 changed files with 51 additions and 36 deletions
|
@ -138,7 +138,7 @@ func (b *Indexer) Delete(_ context.Context, ids ...int64) error {
|
|||
|
||||
// Search searches for issues by given conditions.
|
||||
// Returns the matching issue IDs
|
||||
func (b *Indexer) Search(ctx context.Context, keyword string, repoIDs []int64, limit, start int) (*internal.SearchResult, error) {
|
||||
func (b *Indexer) Search(ctx context.Context, keyword string, repoIDs []int64, limit, start int, state string) (*internal.SearchResult, error) {
|
||||
var repoQueriesP []*query.NumericRangeQuery
|
||||
for _, repoID := range repoIDs {
|
||||
repoQueriesP = append(repoQueriesP, numericEqualityQuery(repoID, "repo_id"))
|
||||
|
|
|
@ -77,7 +77,7 @@ func TestBleveIndexAndSearch(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, kw := range keywords {
|
||||
res, err := indexer.Search(context.TODO(), kw.Keyword, []int64{2}, 10, 0)
|
||||
res, err := indexer.Search(context.TODO(), kw.Keyword, []int64{2}, 10, 0, "")
|
||||
assert.NoError(t, err)
|
||||
|
||||
ids := make([]int64, 0, len(res.Hits))
|
||||
|
|
|
@ -36,7 +36,7 @@ func (i *Indexer) Delete(_ context.Context, _ ...int64) error {
|
|||
}
|
||||
|
||||
// Search searches for issues
|
||||
func (i *Indexer) Search(ctx context.Context, kw string, repoIDs []int64, limit, start int) (*internal.SearchResult, error) {
|
||||
func (i *Indexer) Search(ctx context.Context, kw string, repoIDs []int64, limit, start int, state string) (*internal.SearchResult, error) {
|
||||
total, ids, err := issues_model.SearchIssueIDsByKeyword(ctx, kw, repoIDs, limit, start)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -140,7 +140,7 @@ func (b *Indexer) Delete(ctx context.Context, ids ...int64) error {
|
|||
|
||||
// Search searches for issues by given conditions.
|
||||
// Returns the matching issue IDs
|
||||
func (b *Indexer) Search(ctx context.Context, keyword string, repoIDs []int64, limit, start int) (*internal.SearchResult, error) {
|
||||
func (b *Indexer) Search(ctx context.Context, keyword string, repoIDs []int64, limit, start int, state string) (*internal.SearchResult, error) {
|
||||
kwQuery := elastic.NewMultiMatchQuery(keyword, "title", "content", "comments")
|
||||
query := elastic.NewBoolQuery()
|
||||
query = query.Must(kwQuery)
|
||||
|
|
|
@ -242,12 +242,18 @@ func UpdateIssueIndexer(issue *issues_model.Issue) {
|
|||
comments = append(comments, comment.Content)
|
||||
}
|
||||
}
|
||||
issueType := "issue"
|
||||
if issue.IsPull {
|
||||
issueType = "pull"
|
||||
}
|
||||
indexerData := &internal.IndexerData{
|
||||
ID: issue.ID,
|
||||
RepoID: issue.RepoID,
|
||||
Title: issue.Title,
|
||||
Content: issue.Content,
|
||||
Comments: comments,
|
||||
ID: issue.ID,
|
||||
RepoID: issue.RepoID,
|
||||
State: string(issue.State()),
|
||||
IssueType: issueType,
|
||||
Title: issue.Title,
|
||||
Content: issue.Content,
|
||||
Comments: comments,
|
||||
}
|
||||
log.Debug("Adding to channel: %v", indexerData)
|
||||
if err := issueIndexerQueue.Push(indexerData); err != nil {
|
||||
|
@ -278,10 +284,10 @@ func DeleteRepoIssueIndexer(ctx context.Context, repo *repo_model.Repository) {
|
|||
|
||||
// SearchIssuesByKeyword search issue ids by keywords and repo id
|
||||
// WARNNING: You have to ensure user have permission to visit repoIDs' issues
|
||||
func SearchIssuesByKeyword(ctx context.Context, repoIDs []int64, keyword string) ([]int64, error) {
|
||||
func SearchIssuesByKeyword(ctx context.Context, repoIDs []int64, keyword, state string) ([]int64, error) {
|
||||
var issueIDs []int64
|
||||
indexer := *globalIndexer.Load()
|
||||
res, err := indexer.Search(ctx, keyword, repoIDs, 50, 0)
|
||||
res, err := indexer.Search(ctx, keyword, repoIDs, 50, 0, state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -50,19 +50,19 @@ func TestBleveSearchIssues(t *testing.T) {
|
|||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
ids, err := SearchIssuesByKeyword(context.TODO(), []int64{1}, "issue2")
|
||||
ids, err := SearchIssuesByKeyword(context.TODO(), []int64{1}, "issue2", "")
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, []int64{2}, ids)
|
||||
|
||||
ids, err = SearchIssuesByKeyword(context.TODO(), []int64{1}, "first")
|
||||
ids, err = SearchIssuesByKeyword(context.TODO(), []int64{1}, "first", "")
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, []int64{1}, ids)
|
||||
|
||||
ids, err = SearchIssuesByKeyword(context.TODO(), []int64{1}, "for")
|
||||
ids, err = SearchIssuesByKeyword(context.TODO(), []int64{1}, "for", "")
|
||||
assert.NoError(t, err)
|
||||
assert.ElementsMatch(t, []int64{1, 2, 3, 5, 11}, ids)
|
||||
|
||||
ids, err = SearchIssuesByKeyword(context.TODO(), []int64{1}, "good")
|
||||
ids, err = SearchIssuesByKeyword(context.TODO(), []int64{1}, "good", "")
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, []int64{1}, ids)
|
||||
}
|
||||
|
@ -73,19 +73,19 @@ func TestDBSearchIssues(t *testing.T) {
|
|||
setting.Indexer.IssueType = "db"
|
||||
InitIssueIndexer(true)
|
||||
|
||||
ids, err := SearchIssuesByKeyword(context.TODO(), []int64{1}, "issue2")
|
||||
ids, err := SearchIssuesByKeyword(context.TODO(), []int64{1}, "issue2", "")
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, []int64{2}, ids)
|
||||
|
||||
ids, err = SearchIssuesByKeyword(context.TODO(), []int64{1}, "first")
|
||||
ids, err = SearchIssuesByKeyword(context.TODO(), []int64{1}, "first", "")
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, []int64{1}, ids)
|
||||
|
||||
ids, err = SearchIssuesByKeyword(context.TODO(), []int64{1}, "for")
|
||||
ids, err = SearchIssuesByKeyword(context.TODO(), []int64{1}, "for", "")
|
||||
assert.NoError(t, err)
|
||||
assert.ElementsMatch(t, []int64{1, 2, 3, 5, 11}, ids)
|
||||
|
||||
ids, err = SearchIssuesByKeyword(context.TODO(), []int64{1}, "good")
|
||||
ids, err = SearchIssuesByKeyword(context.TODO(), []int64{1}, "good", "")
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, []int64{1}, ids)
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ type Indexer interface {
|
|||
internal.Indexer
|
||||
Index(ctx context.Context, issue []*IndexerData) error
|
||||
Delete(ctx context.Context, ids ...int64) error
|
||||
Search(ctx context.Context, kw string, repoIDs []int64, limit, start int) (*SearchResult, error)
|
||||
Search(ctx context.Context, kw string, repoIDs []int64, limit, start int, state string) (*SearchResult, error)
|
||||
}
|
||||
|
||||
// NewDummyIndexer returns a dummy indexer
|
||||
|
@ -37,6 +37,6 @@ func (d *dummyIndexer) Delete(ctx context.Context, ids ...int64) error {
|
|||
return fmt.Errorf("indexer is not ready")
|
||||
}
|
||||
|
||||
func (d *dummyIndexer) Search(ctx context.Context, kw string, repoIDs []int64, limit, start int) (*SearchResult, error) {
|
||||
func (d *dummyIndexer) Search(ctx context.Context, kw string, repoIDs []int64, limit, start int, state string) (*SearchResult, error) {
|
||||
return nil, fmt.Errorf("indexer is not ready")
|
||||
}
|
||||
|
|
|
@ -5,13 +5,15 @@ package internal
|
|||
|
||||
// IndexerData data stored in the issue indexer
|
||||
type IndexerData struct {
|
||||
ID int64 `json:"id"`
|
||||
RepoID int64 `json:"repo_id"`
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
Comments []string `json:"comments"`
|
||||
IsDelete bool `json:"is_delete"`
|
||||
IDs []int64 `json:"ids"`
|
||||
ID int64 `json:"id"`
|
||||
RepoID int64 `json:"repo_id"`
|
||||
State string `json:"state"` // open, closed, all
|
||||
IssueType string `json:"type"` // issue or pull
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
Comments []string `json:"comments"`
|
||||
IsDelete bool `json:"is_delete"`
|
||||
IDs []int64 `json:"ids"`
|
||||
}
|
||||
|
||||
// Match represents on search result
|
||||
|
|
|
@ -16,7 +16,7 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
issueIndexerLatestVersion = 0
|
||||
issueIndexerLatestVersion = 1
|
||||
)
|
||||
|
||||
var _ internal.Indexer = &Indexer{}
|
||||
|
@ -70,12 +70,19 @@ func (b *Indexer) Delete(_ context.Context, ids ...int64) error {
|
|||
|
||||
// Search searches for issues by given conditions.
|
||||
// Returns the matching issue IDs
|
||||
func (b *Indexer) Search(ctx context.Context, keyword string, repoIDs []int64, limit, start int) (*internal.SearchResult, error) {
|
||||
func (b *Indexer) Search(ctx context.Context, keyword string, repoIDs []int64, limit, start int, state string) (*internal.SearchResult, error) {
|
||||
repoFilters := make([]string, 0, len(repoIDs))
|
||||
for _, repoID := range repoIDs {
|
||||
repoFilters = append(repoFilters, "repo_id = "+strconv.FormatInt(repoID, 10))
|
||||
}
|
||||
filter := strings.Join(repoFilters, " OR ")
|
||||
if state == "open" || state == "closed" {
|
||||
if filter != "" {
|
||||
filter = "(" + filter + ") AND state = " + state
|
||||
} else {
|
||||
filter = "state = " + state
|
||||
}
|
||||
}
|
||||
searchRes, err := b.inner.Client.Index(b.inner.VersionedIndexName()).Search(keyword, &meilisearch.SearchRequest{
|
||||
Filter: filter,
|
||||
Limit: int64(limit),
|
||||
|
|
|
@ -195,7 +195,7 @@ func SearchIssues(ctx *context.APIContext) {
|
|||
}
|
||||
var issueIDs []int64
|
||||
if len(keyword) > 0 && len(repoIDs) > 0 {
|
||||
if issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx, repoIDs, keyword); err != nil {
|
||||
if issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx, repoIDs, keyword, ctx.FormString("state")); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "SearchIssuesByKeyword", err)
|
||||
return
|
||||
}
|
||||
|
@ -394,7 +394,7 @@ func ListIssues(ctx *context.APIContext) {
|
|||
var issueIDs []int64
|
||||
var labelIDs []int64
|
||||
if len(keyword) > 0 {
|
||||
issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx, []int64{ctx.Repo.Repository.ID}, keyword)
|
||||
issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx, []int64{ctx.Repo.Repository.ID}, keyword, ctx.FormString("state"))
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "SearchIssuesByKeyword", err)
|
||||
return
|
||||
|
|
|
@ -189,7 +189,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
|
|||
|
||||
var issueIDs []int64
|
||||
if len(keyword) > 0 {
|
||||
issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx, []int64{repo.ID}, keyword)
|
||||
issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx, []int64{repo.ID}, keyword, ctx.FormString("state"))
|
||||
if err != nil {
|
||||
if issue_indexer.IsAvailable(ctx) {
|
||||
ctx.ServerError("issueIndexer.Search", err)
|
||||
|
@ -2466,7 +2466,7 @@ func SearchIssues(ctx *context.Context) {
|
|||
}
|
||||
var issueIDs []int64
|
||||
if len(keyword) > 0 && len(repoIDs) > 0 {
|
||||
if issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx, repoIDs, keyword); err != nil {
|
||||
if issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx, repoIDs, keyword, ctx.FormString("state")); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "SearchIssuesByKeyword", err.Error())
|
||||
return
|
||||
}
|
||||
|
@ -2614,7 +2614,7 @@ func ListIssues(ctx *context.Context) {
|
|||
var issueIDs []int64
|
||||
var labelIDs []int64
|
||||
if len(keyword) > 0 {
|
||||
issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx, []int64{ctx.Repo.Repository.ID}, keyword)
|
||||
issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx, []int64{ctx.Repo.Repository.ID}, keyword, ctx.FormString("state"))
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
|
|
|
@ -725,7 +725,7 @@ func issueIDsFromSearch(ctx *context.Context, ctxUser *user_model.User, keyword
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("GetRepoIDsForIssuesOptions: %w", err)
|
||||
}
|
||||
issueIDsFromSearch, err := issue_indexer.SearchIssuesByKeyword(ctx, searchRepoIDs, keyword)
|
||||
issueIDsFromSearch, err := issue_indexer.SearchIssuesByKeyword(ctx, searchRepoIDs, keyword, ctx.FormString("state"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("SearchIssuesByKeyword: %w", err)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue