Skip to content

Commit b40e298

Browse files
committed
feat: CountIssues
1 parent 23addde commit b40e298

File tree

3 files changed

+148
-55
lines changed

3 files changed

+148
-55
lines changed

models/issues/issue_stats.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ func getIssueStatsChunk(opts *IssuesOptions, issueIDs []int64) (*IssueStats, err
181181
}
182182

183183
// GetUserIssueStats returns issue statistic information for dashboard by given conditions.
184+
// TBC: remove it
184185
func GetUserIssueStats(filterMode int, opts IssuesOptions) (*IssueStats, error) {
185186
if opts.User == nil {
186187
return nil, errors.New("issue stats without user")

modules/indexer/issues/indexer.go

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,6 @@ const (
291291
)
292292

293293
// SearchIssues search issues by options.
294-
// It returns issue ids and a bool value indicates if the result is imprecise.
295294
func SearchIssues(ctx context.Context, opts *SearchOptions) ([]int64, int64, error) {
296295
indexer := *globalIndexer.Load()
297296

@@ -317,3 +316,49 @@ func SearchIssues(ctx context.Context, opts *SearchOptions) ([]int64, int64, err
317316

318317
return ret, result.Total, nil
319318
}
319+
320+
// CountIssues counts issues by options. It is a shortcut of SearchIssues(ctx, opts) but only returns the total count.
321+
func CountIssues(ctx context.Context, opts *SearchOptions) (int64, error) {
322+
paginator := opts.Paginator
323+
opts.Paginator = &db_model.ListOptions{
324+
PageSize: 0,
325+
}
326+
defer func() {
327+
opts.Paginator = paginator
328+
}()
329+
330+
_, total, err := SearchIssues(ctx, opts)
331+
return total, err
332+
}
333+
334+
// CountIssuesByRepo counts issues by options and group by repo id.
335+
// It's not a complete implementation, since it requires the caller should provide the repo ids.
336+
// That means opts.RepoIDs must be specified, and opts.AllPublic must be false.
337+
// It's good enough for the current usage, and it can be improved if needed.
338+
// TODO: use "group by" of the indexer engines to implement it.
339+
func CountIssuesByRepo(ctx context.Context, opts *SearchOptions) (map[int64]int64, error) {
340+
if len(opts.RepoIDs) == 0 {
341+
return nil, fmt.Errorf("opts.RepoIDs must be specified")
342+
}
343+
if opts.AllPublic {
344+
return nil, fmt.Errorf("opts.AllPublic must be false")
345+
}
346+
347+
resoIDs := opts.RepoIDs
348+
defer func() {
349+
opts.RepoIDs = resoIDs
350+
}()
351+
352+
ret := make(map[int64]int64, len(opts.RepoIDs))
353+
// TODO: it could be faster if do it in parallel for some indexer engines. Improve it if users report it's slow.
354+
for _, repoID := range resoIDs {
355+
opts.RepoIDs = []int64{repoID}
356+
count, err := CountIssues(ctx, opts)
357+
if err != nil {
358+
return nil, err
359+
}
360+
ret[repoID] = count
361+
}
362+
363+
return ret, nil
364+
}

routers/web/user/home.go

Lines changed: 101 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -507,22 +507,10 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
507507

508508
// Filter repos and count issues in them. Count will be used later.
509509
// USING NON-FINAL STATE OF opts FOR A QUERY.
510-
var issueCountByRepo map[int64]int64
511-
{
512-
issueIDs, err := issueIDsFromSearch(ctx, keyword, opts)
513-
if err != nil {
514-
ctx.ServerError("issueIDsFromSearch", err)
515-
return
516-
}
517-
if len(issueIDs) > 0 { // else, no issues found, just leave issueCountByRepo empty
518-
opts.IssueIDs = issueIDs
519-
issueCountByRepo, err = issues_model.CountIssuesByRepo(ctx, opts)
520-
if err != nil {
521-
ctx.ServerError("CountIssuesByRepo", err)
522-
return
523-
}
524-
opts.IssueIDs = nil // reset, the opts will be used later
525-
}
510+
issueCountByRepo, err := issue_indexer.CountIssuesByRepo(ctx, issue_indexer.ToSearchOptions(keyword, opts))
511+
if err != nil {
512+
ctx.ServerError("CountIssuesByRepo", err)
513+
return
526514
}
527515

528516
// Make sure page number is at least 1. Will be posted to ctx.Data.
@@ -615,44 +603,10 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
615603
// -------------------------------
616604
// Fill stats to post to ctx.Data.
617605
// -------------------------------
618-
var issueStats *issues_model.IssueStats
619-
{
620-
statsOpts := issues_model.IssuesOptions{
621-
RepoIDs: repoIDs,
622-
User: ctx.Doer,
623-
IsPull: util.OptionalBoolOf(isPullList),
624-
IsClosed: util.OptionalBoolOf(isShowClosed),
625-
IssueIDs: nil,
626-
IsArchived: util.OptionalBoolFalse,
627-
LabelIDs: opts.LabelIDs,
628-
Org: org,
629-
Team: team,
630-
RepoCond: opts.RepoCond,
631-
}
632-
633-
if keyword != "" {
634-
statsOpts.RepoIDs = opts.RepoIDs
635-
allIssueIDs, err := issueIDsFromSearch(ctx, keyword, &statsOpts)
636-
if err != nil {
637-
ctx.ServerError("issueIDsFromSearch", err)
638-
return
639-
}
640-
statsOpts.IssueIDs = allIssueIDs
641-
}
642-
643-
if keyword != "" && len(statsOpts.IssueIDs) == 0 {
644-
// So it did search with the keyword, but no issue found.
645-
// Just set issueStats to empty.
646-
issueStats = &issues_model.IssueStats{}
647-
} else {
648-
// So it did search with the keyword, and found some issues. It needs to get issueStats of these issues.
649-
// Or the keyword is empty, so it doesn't need issueIDs as filter, just get issueStats with statsOpts.
650-
issueStats, err = issues_model.GetUserIssueStats(filterMode, statsOpts)
651-
if err != nil {
652-
ctx.ServerError("GetUserIssueStats", err)
653-
return
654-
}
655-
}
606+
issueStats, err := getUserIssueStats(ctx, issue_indexer.ToSearchOptions(keyword, opts), ctx.Doer.ID)
607+
if err != nil {
608+
ctx.ServerError("getUserIssueStats", err)
609+
return
656610
}
657611

658612
// Will be posted to ctx.Data.
@@ -777,6 +731,7 @@ func getRepoIDs(reposQuery string) []int64 {
777731
return repoIDs
778732
}
779733

734+
// TBC: remove it
780735
func issueIDsFromSearch(ctx *context.Context, keyword string, opts *issues_model.IssuesOptions) ([]int64, error) {
781736
ids, _, err := issue_indexer.SearchIssues(ctx, issue_indexer.ToSearchOptions(keyword, opts))
782737
if err != nil {
@@ -913,3 +868,95 @@ func UsernameSubRoute(ctx *context.Context) {
913868
}
914869
}
915870
}
871+
872+
func getUserIssueStats(ctx *context.Context, opts *issue_indexer.SearchOptions, doerID int64) (*issues_model.IssueStats, error) {
873+
isClosed := opts.IsClosed
874+
assigneeID := opts.AssigneeID
875+
posterID := opts.PosterID
876+
mentionID := opts.MentionID
877+
reviewRequestedID := opts.ReviewRequestedID
878+
reviewedID := opts.ReviewedID
879+
defer func() {
880+
opts.IsClosed = isClosed
881+
opts.AssigneeID = assigneeID
882+
opts.PosterID = posterID
883+
opts.MentionID = mentionID
884+
opts.ReviewRequestedID = reviewRequestedID
885+
opts.ReviewedID = reviewedID
886+
}()
887+
opts.IsClosed = util.OptionalBoolNone
888+
opts.AssigneeID = nil
889+
opts.PosterID = nil
890+
opts.MentionID = nil
891+
opts.ReviewRequestedID = nil
892+
opts.ReviewedID = nil
893+
894+
var (
895+
err error
896+
ret = &issues_model.IssueStats{}
897+
)
898+
899+
{
900+
opts.IsClosed = util.OptionalBoolFalse
901+
ret.OpenCount, err = issue_indexer.CountIssues(ctx, opts)
902+
if err != nil {
903+
return nil, err
904+
}
905+
opts.IsClosed = util.OptionalBoolNone
906+
}
907+
{
908+
opts.IsClosed = util.OptionalBoolTrue
909+
ret.ClosedCount, err = issue_indexer.CountIssues(ctx, opts)
910+
if err != nil {
911+
return nil, err
912+
}
913+
opts.IsClosed = util.OptionalBoolNone
914+
}
915+
{
916+
ret.YourRepositoriesCount, err = issue_indexer.CountIssues(ctx, opts)
917+
if err != nil {
918+
return nil, err
919+
}
920+
}
921+
{
922+
opts.AssigneeID = &doerID
923+
ret.AssignCount, err = issue_indexer.CountIssues(ctx, opts)
924+
if err != nil {
925+
return nil, err
926+
}
927+
opts.AssigneeID = nil
928+
}
929+
{
930+
opts.PosterID = &doerID
931+
ret.CreateCount, err = issue_indexer.CountIssues(ctx, opts)
932+
if err != nil {
933+
return nil, err
934+
}
935+
opts.PosterID = nil
936+
}
937+
{
938+
opts.MentionID = &doerID
939+
ret.MentionCount, err = issue_indexer.CountIssues(ctx, opts)
940+
if err != nil {
941+
return nil, err
942+
}
943+
opts.MentionID = nil
944+
}
945+
{
946+
opts.ReviewRequestedID = &doerID
947+
ret.ReviewRequestedCount, err = issue_indexer.CountIssues(ctx, opts)
948+
if err != nil {
949+
return nil, err
950+
}
951+
opts.ReviewRequestedID = nil
952+
}
953+
{
954+
opts.ReviewedID = &doerID
955+
ret.ReviewedCount, err = issue_indexer.CountIssues(ctx, opts)
956+
if err != nil {
957+
return nil, err
958+
}
959+
opts.ReviewedID = nil
960+
}
961+
return ret, nil
962+
}

0 commit comments

Comments
 (0)