mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2026-05-12 22:10:25 +00:00
feat: support filtering for issues with multiple assignees (#10552)
Stores the entire list of AssigneeIDs for each issue in the indexer. This fixes the bug where there were missing entries for issues with assignees while filtering. Note: Will re-index all issues Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10552 Reviewed-by: Gusted <gusted@noreply.codeberg.org> Co-authored-by: Shiny Nematoda <snematoda.751k2@aleeas.com> Co-committed-by: Shiny Nematoda <snematoda.751k2@aleeas.com>
This commit is contained in:
parent
cc47a4057f
commit
239d7168e1
6 changed files with 33 additions and 16 deletions
|
|
@ -23,7 +23,7 @@ import (
|
|||
const (
|
||||
issueIndexerAnalyzer = "issueIndexer"
|
||||
issueIndexerDocType = "issueIndexerDocType"
|
||||
issueIndexerLatestVersion = 6
|
||||
issueIndexerLatestVersion = 7
|
||||
)
|
||||
|
||||
const unicodeNormalizeName = "unicodeNormalize"
|
||||
|
|
@ -82,7 +82,7 @@ func generateIssueIndexMapping() (mapping.IndexMapping, error) {
|
|||
docMapping.AddFieldMappingsAt("project_id", numberFieldMapping)
|
||||
docMapping.AddFieldMappingsAt("project_board_id", numberFieldMapping)
|
||||
docMapping.AddFieldMappingsAt("poster_id", numberFieldMapping)
|
||||
docMapping.AddFieldMappingsAt("assignee_id", numberFieldMapping)
|
||||
docMapping.AddFieldMappingsAt("assignee_ids", numberFieldMapping)
|
||||
docMapping.AddFieldMappingsAt("mention_ids", numberFieldMapping)
|
||||
docMapping.AddFieldMappingsAt("reviewed_ids", numberFieldMapping)
|
||||
docMapping.AddFieldMappingsAt("review_requested_ids", numberFieldMapping)
|
||||
|
|
@ -245,7 +245,7 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
|
|||
"project_id": options.ProjectID,
|
||||
"project_board_id": options.ProjectColumnID,
|
||||
"poster_id": options.PosterID,
|
||||
"assignee_id": options.AssigneeID,
|
||||
"assignee_ids": options.AssigneeID,
|
||||
"mention_ids": options.MentionID,
|
||||
"reviewed_ids": options.ReviewedID,
|
||||
"review_requested_ids": options.ReviewRequestedID,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
issueIndexerLatestVersion = 2
|
||||
issueIndexerLatestVersion = 3
|
||||
// multi-match-types, currently only 2 types are used
|
||||
// Reference: https://www.elastic.co/guide/en/elasticsearch/reference/7.0/query-dsl-multi-match-query.html#multi-match-types
|
||||
esMultiMatchTypeBestFields = "best_fields"
|
||||
|
|
@ -69,7 +69,7 @@ const (
|
|||
"project_id": { "type": "long", "index": true },
|
||||
"project_board_id": { "type": "long", "index": true },
|
||||
"poster_id": { "type": "long", "index": true },
|
||||
"assignee_id": { "type": "long", "index": true },
|
||||
"assignee_ids": { "type": "long", "index": true },
|
||||
"mention_ids": { "type": "long", "index": true },
|
||||
"reviewed_ids": { "type": "long", "index": true },
|
||||
"review_requested_ids": { "type": "long", "index": true },
|
||||
|
|
@ -233,7 +233,7 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
|
|||
}
|
||||
|
||||
if options.AssigneeID.Has() {
|
||||
query.Must(elastic.NewTermQuery("assignee_id", options.AssigneeID.Value()))
|
||||
query.Must(elastic.NewTermQuery("assignee_ids", options.AssigneeID.Value()))
|
||||
}
|
||||
|
||||
if options.MentionID.Has() {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ type IndexerData struct {
|
|||
ProjectID int64 `json:"project_id"`
|
||||
ProjectColumnID int64 `json:"project_board_id"` // the key should be kept as project_board_id to keep compatible
|
||||
PosterID int64 `json:"poster_id"`
|
||||
AssigneeID int64 `json:"assignee_id"`
|
||||
AssigneeIDs []int64 `json:"assignee_ids"`
|
||||
MentionIDs []int64 `json:"mention_ids"`
|
||||
ReviewedIDs []int64 `json:"reviewed_ids"`
|
||||
ReviewRequestedIDs []int64 `json:"review_requested_ids"`
|
||||
|
|
|
|||
|
|
@ -461,10 +461,10 @@ var cases = []*testIndexerCase{
|
|||
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
|
||||
assert.Len(t, result.Hits, 5)
|
||||
for _, v := range result.Hits {
|
||||
assert.Equal(t, int64(1), data[v.ID].AssigneeID)
|
||||
assert.Contains(t, data[v.ID].AssigneeIDs, int64(1))
|
||||
}
|
||||
assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool {
|
||||
return v.AssigneeID == 1
|
||||
return slices.Contains(v.AssigneeIDs, 1)
|
||||
}), result.Total)
|
||||
},
|
||||
},
|
||||
|
|
@ -479,10 +479,10 @@ var cases = []*testIndexerCase{
|
|||
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
|
||||
assert.Len(t, result.Hits, 5)
|
||||
for _, v := range result.Hits {
|
||||
assert.Equal(t, int64(0), data[v.ID].AssigneeID)
|
||||
assert.Equal(t, []int64{0}, data[v.ID].AssigneeIDs)
|
||||
}
|
||||
assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool {
|
||||
return v.AssigneeID == 0
|
||||
return slices.Contains(v.AssigneeIDs, 0)
|
||||
}), result.Total)
|
||||
},
|
||||
},
|
||||
|
|
@ -840,6 +840,14 @@ func generateDefaultIndexerData() []*internal.IndexerData {
|
|||
subscriberIDs[i] = int64(i) + 1 // SubscriberID should not be 0
|
||||
}
|
||||
|
||||
assigneeIDs := make([]int64, 0, 2)
|
||||
{
|
||||
if issueIndex%7 == 0 { // If divisible by 7 we insert 1 too to test multiple assignees
|
||||
assigneeIDs = append(assigneeIDs, 1)
|
||||
}
|
||||
assigneeIDs = append(assigneeIDs, issueIndex%10)
|
||||
}
|
||||
|
||||
data = append(data, &internal.IndexerData{
|
||||
ID: id,
|
||||
Index: issueIndex,
|
||||
|
|
@ -856,7 +864,7 @@ func generateDefaultIndexerData() []*internal.IndexerData {
|
|||
ProjectID: issueIndex % 5,
|
||||
ProjectColumnID: issueIndex % 6,
|
||||
PosterID: id%10 + 1, // PosterID should not be 0
|
||||
AssigneeID: issueIndex % 10,
|
||||
AssigneeIDs: assigneeIDs,
|
||||
MentionIDs: mentionIDs,
|
||||
ReviewedIDs: reviewedIDs,
|
||||
ReviewRequestedIDs: reviewRequestedIDs,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
issueIndexerLatestVersion = 3
|
||||
issueIndexerLatestVersion = 4
|
||||
|
||||
// TODO: make this configurable if necessary
|
||||
maxTotalHits = 10000
|
||||
|
|
@ -67,7 +67,7 @@ func NewIndexer(url, apiKey, indexerName string) *Indexer {
|
|||
"project_id",
|
||||
"project_board_id",
|
||||
"poster_id",
|
||||
"assignee_id",
|
||||
"assignee_ids",
|
||||
"mention_ids",
|
||||
"reviewed_ids",
|
||||
"review_requested_ids",
|
||||
|
|
@ -183,7 +183,7 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
|
|||
}
|
||||
|
||||
if options.AssigneeID.Has() {
|
||||
query.And(inner_meilisearch.NewFilterEq("assignee_id", options.AssigneeID.Value()))
|
||||
query.And(inner_meilisearch.NewFilterEq("assignee_ids", options.AssigneeID.Value()))
|
||||
}
|
||||
|
||||
if options.MentionID.Has() {
|
||||
|
|
|
|||
|
|
@ -50,6 +50,15 @@ func getIssueIndexerData(ctx context.Context, issueID int64) (*internal.IndexerD
|
|||
labels = append(labels, label.ID)
|
||||
}
|
||||
|
||||
assigneeIDs := make([]int64, 0, len(issue.Assignees))
|
||||
if len(issue.Assignees) != 0 {
|
||||
for _, assignee := range issue.Assignees {
|
||||
assigneeIDs = append(assigneeIDs, assignee.ID)
|
||||
}
|
||||
} else {
|
||||
assigneeIDs = append(assigneeIDs, 0)
|
||||
}
|
||||
|
||||
mentionIDs, err := issues_model.GetIssueMentionIDs(ctx, issueID)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
|
|
@ -108,7 +117,7 @@ func getIssueIndexerData(ctx context.Context, issueID int64) (*internal.IndexerD
|
|||
ProjectID: projectID,
|
||||
ProjectColumnID: issue.ProjectColumnID(ctx),
|
||||
PosterID: issue.PosterID,
|
||||
AssigneeID: issue.AssigneeID,
|
||||
AssigneeIDs: assigneeIDs,
|
||||
MentionIDs: mentionIDs,
|
||||
ReviewedIDs: reviewedIDs,
|
||||
ReviewRequestedIDs: reviewRequestedIDs,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue