mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2025-06-22 09:00:51 +00:00
An attempt at solving #7956. This (and rebuilding the index) seems enough to ensure the issue *appears* among the results. However, I couldn't figure out from [bleve docs](https://github.com/blevesearch/bleve/blob/master/docs/scoring.md) how to affect the scoring based on specific fields, or whether that is possible at all. Disclaimer: I've never written Go before, sorry 😅 take it as a quick PoC more than anything. ### Tests - I added test coverage for Go changes... - [x] in their respective `*_test.go` for unit tests. - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server. - I added test coverage for JavaScript changes... - [ ] in `web_src/js/*.test.js` if it can be unit tested. - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)). ### Documentation - [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change. - [x] I did not document these changes and I do not expect someone else to do it. ### Release notes - [ ] I do not want this change to show in the release notes. - [x] I want the title to show in the release notes with a link to this pull request. - [ ] I want the content of the `release-notes/<pull request number>.md` to be be used for the release notes instead of the title. <!--start release-notes-assistant--> ## Release notes <!--URL:https://codeberg.org/forgejo/forgejo--> - Features - [PR](https://codeberg.org/forgejo/forgejo/pulls/7968): <!--number 7968 --><!--line 0 --><!--description QWRkIGlzc3VlIG51bWJlciB0byB0aGUgc2VhcmNoIGluZGV4LCByYW5rIG51bWJlciBhbmQgdGl0bGUgbWF0Y2hlcyBoaWdoZXIgKCM3OTU2KQ==-->Add issue number to the search index, rank number and title matches higher (#7956)<!--description--> <!--end release-notes-assistant--> Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7968 Reviewed-by: Shiny Nematoda <snematoda@noreply.codeberg.org> Co-authored-by: Danko Aleksejevs <danko@very.lv> Co-committed-by: Danko Aleksejevs <danko@very.lv>
194 lines
5.7 KiB
Go
194 lines
5.7 KiB
Go
// Copyright 2023 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package issues
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"forgejo.org/models/db"
|
|
issue_model "forgejo.org/models/issues"
|
|
"forgejo.org/modules/container"
|
|
"forgejo.org/modules/indexer/issues/internal"
|
|
"forgejo.org/modules/log"
|
|
"forgejo.org/modules/queue"
|
|
)
|
|
|
|
// getIssueIndexerData returns the indexer data of an issue and a bool value indicating whether the issue exists.
|
|
func getIssueIndexerData(ctx context.Context, issueID int64) (*internal.IndexerData, bool, error) {
|
|
issue, err := issue_model.GetIssueByID(ctx, issueID)
|
|
if err != nil {
|
|
if issue_model.IsErrIssueNotExist(err) {
|
|
return nil, false, nil
|
|
}
|
|
return nil, false, err
|
|
}
|
|
|
|
// FIXME: what if users want to search for a review comment of a pull request?
|
|
// The comment type is CommentTypeCode or CommentTypeReview.
|
|
// But LoadDiscussComments only loads CommentTypeComment.
|
|
if err := issue.LoadDiscussComments(ctx); err != nil {
|
|
return nil, false, err
|
|
}
|
|
|
|
comments := make([]string, 0, len(issue.Comments))
|
|
for _, comment := range issue.Comments {
|
|
if comment.Content != "" {
|
|
// what ever the comment type is, index the content if it is not empty.
|
|
comments = append(comments, comment.Content)
|
|
}
|
|
}
|
|
|
|
if err := issue.LoadAttributes(ctx); err != nil {
|
|
return nil, false, err
|
|
}
|
|
|
|
labels := make([]int64, 0, len(issue.Labels))
|
|
for _, label := range issue.Labels {
|
|
labels = append(labels, label.ID)
|
|
}
|
|
|
|
mentionIDs, err := issue_model.GetIssueMentionIDs(ctx, issueID)
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
|
|
var (
|
|
reviewedIDs []int64
|
|
reviewRequestedIDs []int64
|
|
)
|
|
{
|
|
reviews, err := issue_model.FindReviews(ctx, issue_model.FindReviewOptions{
|
|
ListOptions: db.ListOptionsAll,
|
|
IssueID: issueID,
|
|
OfficialOnly: false,
|
|
})
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
|
|
reviewedIDsSet := make(container.Set[int64], len(reviews))
|
|
reviewRequestedIDsSet := make(container.Set[int64], len(reviews))
|
|
for _, review := range reviews {
|
|
if review.Type == issue_model.ReviewTypeRequest {
|
|
reviewRequestedIDsSet.Add(review.ReviewerID)
|
|
} else {
|
|
reviewedIDsSet.Add(review.ReviewerID)
|
|
}
|
|
}
|
|
reviewedIDs = reviewedIDsSet.Values()
|
|
reviewRequestedIDs = reviewRequestedIDsSet.Values()
|
|
}
|
|
|
|
subscriberIDs, err := issue_model.GetIssueWatchersIDs(ctx, issue.ID, true)
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
|
|
var projectID int64
|
|
if issue.Project != nil {
|
|
projectID = issue.Project.ID
|
|
}
|
|
|
|
return &internal.IndexerData{
|
|
ID: issue.ID,
|
|
RepoID: issue.RepoID,
|
|
Index: issue.Index,
|
|
IsPublic: !issue.Repo.IsPrivate,
|
|
Title: issue.Title,
|
|
Content: issue.Content,
|
|
Comments: comments,
|
|
IsPull: issue.IsPull,
|
|
IsClosed: issue.IsClosed,
|
|
LabelIDs: labels,
|
|
NoLabel: len(labels) == 0,
|
|
MilestoneID: issue.MilestoneID,
|
|
ProjectID: projectID,
|
|
ProjectColumnID: issue.ProjectColumnID(ctx),
|
|
PosterID: issue.PosterID,
|
|
AssigneeID: issue.AssigneeID,
|
|
MentionIDs: mentionIDs,
|
|
ReviewedIDs: reviewedIDs,
|
|
ReviewRequestedIDs: reviewRequestedIDs,
|
|
SubscriberIDs: subscriberIDs,
|
|
UpdatedUnix: issue.UpdatedUnix,
|
|
CreatedUnix: issue.CreatedUnix,
|
|
DeadlineUnix: issue.DeadlineUnix,
|
|
CommentCount: int64(len(issue.Comments)),
|
|
}, true, nil
|
|
}
|
|
|
|
func updateRepoIndexer(ctx context.Context, repoID int64) error {
|
|
ids, err := issue_model.GetIssueIDsByRepoID(ctx, repoID)
|
|
if err != nil {
|
|
return fmt.Errorf("issue_model.GetIssueIDsByRepoID: %w", err)
|
|
}
|
|
for _, id := range ids {
|
|
if err := updateIssueIndexer(ctx, id); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func updateIssueIndexer(ctx context.Context, issueID int64) error {
|
|
return pushIssueIndexerQueue(ctx, &IndexerMetadata{ID: issueID})
|
|
}
|
|
|
|
func deleteRepoIssueIndexer(ctx context.Context, repoID int64) error {
|
|
var ids []int64
|
|
ids, err := issue_model.GetIssueIDsByRepoID(ctx, repoID)
|
|
if err != nil {
|
|
return fmt.Errorf("issue_model.GetIssueIDsByRepoID: %w", err)
|
|
}
|
|
|
|
if len(ids) == 0 {
|
|
return nil
|
|
}
|
|
return pushIssueIndexerQueue(ctx, &IndexerMetadata{
|
|
IDs: ids,
|
|
IsDelete: true,
|
|
})
|
|
}
|
|
|
|
type keepRetryKey struct{}
|
|
|
|
// contextWithKeepRetry returns a context with a key indicating that the indexer should keep retrying.
|
|
// Please note that it's for background tasks only, and it should not be used for user requests, or it may cause blocking.
|
|
func contextWithKeepRetry(ctx context.Context) context.Context {
|
|
return context.WithValue(ctx, keepRetryKey{}, true)
|
|
}
|
|
|
|
func pushIssueIndexerQueue(ctx context.Context, data *IndexerMetadata) error {
|
|
if issueIndexerQueue == nil {
|
|
// Some unit tests will trigger indexing, but the queue is not initialized.
|
|
// It's OK to ignore it, but log a warning message in case it's not a unit test.
|
|
log.Warn("Trying to push %+v to issue indexer queue, but the queue is not initialized, it's OK if it's a unit test", data)
|
|
return nil
|
|
}
|
|
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
default:
|
|
}
|
|
err := issueIndexerQueue.Push(data)
|
|
if errors.Is(err, queue.ErrAlreadyInQueue) {
|
|
return nil
|
|
}
|
|
if errors.Is(err, context.DeadlineExceeded) { // the queue is full
|
|
log.Warn("It seems that issue indexer is slow and the queue is full. Please check the issue indexer or increase the queue size.")
|
|
if ctx.Value(keepRetryKey{}) == nil {
|
|
return err
|
|
}
|
|
// It will be better to increase the queue size instead of retrying, but users may ignore the previous warning message.
|
|
// However, even it retries, it may still cause index loss when there's a deadline in the context.
|
|
log.Debug("Retry to push %+v to issue indexer queue", data)
|
|
continue
|
|
}
|
|
return err
|
|
}
|
|
}
|