From d9103449b31202abc7324e0bfcc76ffb9420b39e Mon Sep 17 00:00:00 2001
From: 6543 <6543@obermui.de>
Date: Wed, 13 Mar 2024 09:25:53 +0100
Subject: [PATCH] Refactor to use optional.Option for issue index search option
 (#29739)

Signed-off-by: 6543 <6543@obermui.de>
(cherry picked from commit 7fd0a5b276aadcf88dcc012fcd364fe160a58810)
---
 modules/indexer/internal/bleve/query.go       | 12 ++--
 modules/indexer/issues/bleve/bleve.go         | 39 ++++++-----
 modules/indexer/issues/db/options.go          | 32 ++++-----
 modules/indexer/issues/dboptions.go           | 12 ++--
 .../issues/elasticsearch/elasticsearch.go     | 42 ++++++------
 modules/indexer/issues/indexer_test.go        | 65 ++++++++-----------
 modules/indexer/issues/internal/model.go      | 20 +++---
 .../indexer/issues/internal/tests/tests.go    | 65 ++++---------------
 .../indexer/issues/meilisearch/meilisearch.go | 40 ++++++------
 routers/api/v1/repo/issue.go                  | 24 +++----
 routers/web/repo/issue.go                     | 32 ++++-----
 routers/web/user/home.go                      | 20 +++---
 12 files changed, 178 insertions(+), 225 deletions(-)

diff --git a/modules/indexer/internal/bleve/query.go b/modules/indexer/internal/bleve/query.go
index 2a427c4020..b96875343e 100644
--- a/modules/indexer/internal/bleve/query.go
+++ b/modules/indexer/internal/bleve/query.go
@@ -4,6 +4,8 @@
 package bleve
 
 import (
+	"code.gitea.io/gitea/modules/optional"
+
 	"github.com/blevesearch/bleve/v2"
 	"github.com/blevesearch/bleve/v2/search/query"
 )
@@ -39,18 +41,18 @@ func BoolFieldQuery(value bool, field string) *query.BoolFieldQuery {
 	return q
 }
 
-func NumericRangeInclusiveQuery(min, max *int64, field string) *query.NumericRangeQuery {
+func NumericRangeInclusiveQuery(min, max optional.Option[int64], field string) *query.NumericRangeQuery {
 	var minF, maxF *float64
 	var minI, maxI *bool
-	if min != nil {
+	if min.Has() {
 		minF = new(float64)
-		*minF = float64(*min)
+		*minF = float64(min.Value())
 		minI = new(bool)
 		*minI = true
 	}
-	if max != nil {
+	if max.Has() {
 		maxF = new(float64)
-		*maxF = float64(*max)
+		*maxF = float64(max.Value())
 		maxI = new(bool)
 		*maxI = true
 	}
diff --git a/modules/indexer/issues/bleve/bleve.go b/modules/indexer/issues/bleve/bleve.go
index aaea854efa..927ad58cd4 100644
--- a/modules/indexer/issues/bleve/bleve.go
+++ b/modules/indexer/issues/bleve/bleve.go
@@ -224,38 +224,41 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
 		queries = append(queries, bleve.NewDisjunctionQuery(milestoneQueries...))
 	}
 
-	if options.ProjectID != nil {
-		queries = append(queries, inner_bleve.NumericEqualityQuery(*options.ProjectID, "project_id"))
+	if options.ProjectID.Has() {
+		queries = append(queries, inner_bleve.NumericEqualityQuery(options.ProjectID.Value(), "project_id"))
 	}
-	if options.ProjectBoardID != nil {
-		queries = append(queries, inner_bleve.NumericEqualityQuery(*options.ProjectBoardID, "project_board_id"))
+	if options.ProjectBoardID.Has() {
+		queries = append(queries, inner_bleve.NumericEqualityQuery(options.ProjectBoardID.Value(), "project_board_id"))
 	}
 
-	if options.PosterID != nil {
-		queries = append(queries, inner_bleve.NumericEqualityQuery(*options.PosterID, "poster_id"))
+	if options.PosterID.Has() {
+		queries = append(queries, inner_bleve.NumericEqualityQuery(options.PosterID.Value(), "poster_id"))
 	}
 
-	if options.AssigneeID != nil {
-		queries = append(queries, inner_bleve.NumericEqualityQuery(*options.AssigneeID, "assignee_id"))
+	if options.AssigneeID.Has() {
+		queries = append(queries, inner_bleve.NumericEqualityQuery(options.AssigneeID.Value(), "assignee_id"))
 	}
 
-	if options.MentionID != nil {
-		queries = append(queries, inner_bleve.NumericEqualityQuery(*options.MentionID, "mention_ids"))
+	if options.MentionID.Has() {
+		queries = append(queries, inner_bleve.NumericEqualityQuery(options.MentionID.Value(), "mention_ids"))
 	}
 
-	if options.ReviewedID != nil {
-		queries = append(queries, inner_bleve.NumericEqualityQuery(*options.ReviewedID, "reviewed_ids"))
+	if options.ReviewedID.Has() {
+		queries = append(queries, inner_bleve.NumericEqualityQuery(options.ReviewedID.Value(), "reviewed_ids"))
 	}
-	if options.ReviewRequestedID != nil {
-		queries = append(queries, inner_bleve.NumericEqualityQuery(*options.ReviewRequestedID, "review_requested_ids"))
+	if options.ReviewRequestedID.Has() {
+		queries = append(queries, inner_bleve.NumericEqualityQuery(options.ReviewRequestedID.Value(), "review_requested_ids"))
 	}
 
-	if options.SubscriberID != nil {
-		queries = append(queries, inner_bleve.NumericEqualityQuery(*options.SubscriberID, "subscriber_ids"))
+	if options.SubscriberID.Has() {
+		queries = append(queries, inner_bleve.NumericEqualityQuery(options.SubscriberID.Value(), "subscriber_ids"))
 	}
 
-	if options.UpdatedAfterUnix != nil || options.UpdatedBeforeUnix != nil {
-		queries = append(queries, inner_bleve.NumericRangeInclusiveQuery(options.UpdatedAfterUnix, options.UpdatedBeforeUnix, "updated_unix"))
+	if options.UpdatedAfterUnix.Has() || options.UpdatedBeforeUnix.Has() {
+		queries = append(queries, inner_bleve.NumericRangeInclusiveQuery(
+			options.UpdatedAfterUnix,
+			options.UpdatedBeforeUnix,
+			"updated_unix"))
 	}
 
 	var indexerQuery query.Query = bleve.NewConjunctionQuery(queries...)
diff --git a/modules/indexer/issues/db/options.go b/modules/indexer/issues/db/options.go
index 69146573a8..eeaf1696ad 100644
--- a/modules/indexer/issues/db/options.go
+++ b/modules/indexer/issues/db/options.go
@@ -15,22 +15,6 @@ import (
 )
 
 func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issue_model.IssuesOptions, error) {
-	// See the comment of issues_model.SearchOptions for the reason why we need to convert
-	convertID := func(id *int64) int64 {
-		if id == nil {
-			return 0
-		}
-		if *id == 0 {
-			return db.NoConditionID
-		}
-		return *id
-	}
-	convertInt64 := func(i *int64) int64 {
-		if i == nil {
-			return 0
-		}
-		return *i
-	}
 	var sortType string
 	switch options.SortBy {
 	case internal.SortByCreatedAsc:
@@ -53,6 +37,18 @@ func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issue_m
 		sortType = "newest"
 	}
 
+	// See the comment of issues_model.SearchOptions for the reason why we need to convert
+	convertID := func(id optional.Option[int64]) int64 {
+		if !id.Has() {
+			return 0
+		}
+		value := id.Value()
+		if value == 0 {
+			return db.NoConditionID
+		}
+		return value
+	}
+
 	opts := &issue_model.IssuesOptions{
 		Paginator:          options.Paginator,
 		RepoIDs:            options.RepoIDs,
@@ -73,8 +69,8 @@ func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issue_m
 		IncludeMilestones:  nil,
 		SortType:           sortType,
 		IssueIDs:           nil,
-		UpdatedAfterUnix:   convertInt64(options.UpdatedAfterUnix),
-		UpdatedBeforeUnix:  convertInt64(options.UpdatedBeforeUnix),
+		UpdatedAfterUnix:   options.UpdatedAfterUnix.Value(),
+		UpdatedBeforeUnix:  options.UpdatedBeforeUnix.Value(),
 		PriorityRepoID:     0,
 		IsArchived:         optional.None[bool](),
 		Org:                nil,
diff --git a/modules/indexer/issues/dboptions.go b/modules/indexer/issues/dboptions.go
index 80e233e29a..4a98b4588a 100644
--- a/modules/indexer/issues/dboptions.go
+++ b/modules/indexer/issues/dboptions.go
@@ -6,6 +6,7 @@ package issues
 import (
 	"code.gitea.io/gitea/models/db"
 	issues_model "code.gitea.io/gitea/models/issues"
+	"code.gitea.io/gitea/modules/optional"
 )
 
 func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOptions {
@@ -38,13 +39,12 @@ func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOp
 	}
 
 	// See the comment of issues_model.SearchOptions for the reason why we need to convert
-	convertID := func(id int64) *int64 {
+	convertID := func(id int64) optional.Option[int64] {
 		if id > 0 {
-			return &id
+			return optional.Some(id)
 		}
 		if id == db.NoConditionID {
-			var zero int64
-			return &zero
+			return optional.None[int64]()
 		}
 		return nil
 	}
@@ -59,10 +59,10 @@ func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOp
 	searchOpt.SubscriberID = convertID(opts.SubscriberID)
 
 	if opts.UpdatedAfterUnix > 0 {
-		searchOpt.UpdatedAfterUnix = &opts.UpdatedAfterUnix
+		searchOpt.UpdatedAfterUnix = optional.Some(opts.UpdatedAfterUnix)
 	}
 	if opts.UpdatedBeforeUnix > 0 {
-		searchOpt.UpdatedBeforeUnix = &opts.UpdatedBeforeUnix
+		searchOpt.UpdatedBeforeUnix = optional.Some(opts.UpdatedBeforeUnix)
 	}
 
 	searchOpt.Paginator = opts.Paginator
diff --git a/modules/indexer/issues/elasticsearch/elasticsearch.go b/modules/indexer/issues/elasticsearch/elasticsearch.go
index 0077da263a..53b383c8d5 100644
--- a/modules/indexer/issues/elasticsearch/elasticsearch.go
+++ b/modules/indexer/issues/elasticsearch/elasticsearch.go
@@ -195,43 +195,43 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
 		query.Must(elastic.NewTermsQuery("milestone_id", toAnySlice(options.MilestoneIDs)...))
 	}
 
-	if options.ProjectID != nil {
-		query.Must(elastic.NewTermQuery("project_id", *options.ProjectID))
+	if options.ProjectID.Has() {
+		query.Must(elastic.NewTermQuery("project_id", options.ProjectID.Value()))
 	}
-	if options.ProjectBoardID != nil {
-		query.Must(elastic.NewTermQuery("project_board_id", *options.ProjectBoardID))
+	if options.ProjectBoardID.Has() {
+		query.Must(elastic.NewTermQuery("project_board_id", options.ProjectBoardID.Value()))
 	}
 
-	if options.PosterID != nil {
-		query.Must(elastic.NewTermQuery("poster_id", *options.PosterID))
+	if options.PosterID.Has() {
+		query.Must(elastic.NewTermQuery("poster_id", options.PosterID.Value()))
 	}
 
-	if options.AssigneeID != nil {
-		query.Must(elastic.NewTermQuery("assignee_id", *options.AssigneeID))
+	if options.AssigneeID.Has() {
+		query.Must(elastic.NewTermQuery("assignee_id", options.AssigneeID.Value()))
 	}
 
-	if options.MentionID != nil {
-		query.Must(elastic.NewTermQuery("mention_ids", *options.MentionID))
+	if options.MentionID.Has() {
+		query.Must(elastic.NewTermQuery("mention_ids", options.MentionID.Value()))
 	}
 
-	if options.ReviewedID != nil {
-		query.Must(elastic.NewTermQuery("reviewed_ids", *options.ReviewedID))
+	if options.ReviewedID.Has() {
+		query.Must(elastic.NewTermQuery("reviewed_ids", options.ReviewedID.Value()))
 	}
-	if options.ReviewRequestedID != nil {
-		query.Must(elastic.NewTermQuery("review_requested_ids", *options.ReviewRequestedID))
+	if options.ReviewRequestedID.Has() {
+		query.Must(elastic.NewTermQuery("review_requested_ids", options.ReviewRequestedID.Value()))
 	}
 
-	if options.SubscriberID != nil {
-		query.Must(elastic.NewTermQuery("subscriber_ids", *options.SubscriberID))
+	if options.SubscriberID.Has() {
+		query.Must(elastic.NewTermQuery("subscriber_ids", options.SubscriberID.Value()))
 	}
 
-	if options.UpdatedAfterUnix != nil || options.UpdatedBeforeUnix != nil {
+	if options.UpdatedAfterUnix.Has() || options.UpdatedBeforeUnix.Has() {
 		q := elastic.NewRangeQuery("updated_unix")
-		if options.UpdatedAfterUnix != nil {
-			q.Gte(*options.UpdatedAfterUnix)
+		if options.UpdatedAfterUnix.Has() {
+			q.Gte(options.UpdatedAfterUnix.Value())
 		}
-		if options.UpdatedBeforeUnix != nil {
-			q.Lte(*options.UpdatedBeforeUnix)
+		if options.UpdatedBeforeUnix.Has() {
+			q.Lte(options.UpdatedBeforeUnix.Value())
 		}
 		query.Must(q)
 	}
diff --git a/modules/indexer/issues/indexer_test.go b/modules/indexer/issues/indexer_test.go
index 10ffa7cbe6..0d0cfc8516 100644
--- a/modules/indexer/issues/indexer_test.go
+++ b/modules/indexer/issues/indexer_test.go
@@ -134,63 +134,60 @@ func searchIssueInRepo(t *testing.T) {
 }
 
 func searchIssueByID(t *testing.T) {
-	int64Pointer := func(x int64) *int64 {
-		return &x
-	}
 	tests := []struct {
 		opts        SearchOptions
 		expectedIDs []int64
 	}{
 		{
-			SearchOptions{
-				PosterID: int64Pointer(1),
+			opts: SearchOptions{
+				PosterID: optional.Some(int64(1)),
 			},
-			[]int64{11, 6, 3, 2, 1},
+			expectedIDs: []int64{11, 6, 3, 2, 1},
 		},
 		{
-			SearchOptions{
-				AssigneeID: int64Pointer(1),
+			opts: SearchOptions{
+				AssigneeID: optional.Some(int64(1)),
 			},
-			[]int64{6, 1},
+			expectedIDs: []int64{6, 1},
 		},
 		{
-			SearchOptions{
-				MentionID: int64Pointer(4),
+			opts: SearchOptions{
+				MentionID: optional.Some(int64(4)),
 			},
-			[]int64{1},
+			expectedIDs: []int64{1},
 		},
 		{
-			SearchOptions{
-				ReviewedID: int64Pointer(1),
+			opts: SearchOptions{
+				ReviewedID: optional.Some(int64(1)),
 			},
-			[]int64{},
+			expectedIDs: []int64{},
 		},
 		{
-			SearchOptions{
-				ReviewRequestedID: int64Pointer(1),
+			opts: SearchOptions{
+				ReviewRequestedID: optional.Some(int64(1)),
 			},
-			[]int64{12},
+			expectedIDs: []int64{12},
 		},
 		{
-			SearchOptions{
-				SubscriberID: int64Pointer(1),
+			opts: SearchOptions{
+				SubscriberID: optional.Some(int64(1)),
 			},
-			[]int64{11, 6, 5, 3, 2, 1},
+			expectedIDs: []int64{11, 6, 5, 3, 2, 1},
 		},
 		{
 			// issue 20 request user 15 and team 5 which user 15 belongs to
 			// the review request number of issue 20 should be 1
-			SearchOptions{
-				ReviewRequestedID: int64Pointer(15),
+			opts: SearchOptions{
+				ReviewRequestedID: optional.Some(int64(15)),
 			},
-			[]int64{12, 20},
+			expectedIDs: []int64{12, 20},
 		},
 		{
 			// user 20 approved the issue 20, so return nothing
-			SearchOptions{
-				ReviewRequestedID: int64Pointer(20),
+			opts: SearchOptions{
+				ReviewRequestedID: optional.Some(int64(20)),
 			},
-			[]int64{},
+			expectedIDs: []int64{},
 		},
 	}
 
@@ -318,16 +315,13 @@ func searchIssueByLabelID(t *testing.T) {
 }
 
 func searchIssueByTime(t *testing.T) {
-	int64Pointer := func(i int64) *int64 {
-		return &i
-	}
 	tests := []struct {
 		opts        SearchOptions
 		expectedIDs []int64
 	}{
 		{
 			SearchOptions{
-				UpdatedAfterUnix: int64Pointer(0),
+				UpdatedAfterUnix: optional.Some(int64(0)),
 			},
 			[]int64{22, 21, 17, 16, 15, 14, 13, 12, 11, 20, 6, 5, 19, 18, 10, 7, 4, 9, 8, 3, 2, 1},
 		},
@@ -363,28 +357,25 @@ func searchIssueWithOrder(t *testing.T) {
 }
 
 func searchIssueInProject(t *testing.T) {
-	int64Pointer := func(i int64) *int64 {
-		return &i
-	}
 	tests := []struct {
 		opts        SearchOptions
 		expectedIDs []int64
 	}{
 		{
 			SearchOptions{
-				ProjectID: int64Pointer(1),
+				ProjectID: optional.Some(int64(1)),
 			},
 			[]int64{5, 3, 2, 1},
 		},
 		{
 			SearchOptions{
-				ProjectBoardID: int64Pointer(1),
+				ProjectBoardID: optional.Some(int64(1)),
 			},
 			[]int64{1},
 		},
 		{
 			SearchOptions{
-				ProjectBoardID: int64Pointer(0), // issue with in default board
+				ProjectBoardID: optional.Some(int64(0)), // issue with in default board
 			},
 			[]int64{2},
 		},
diff --git a/modules/indexer/issues/internal/model.go b/modules/indexer/issues/internal/model.go
index d41fec4aba..b7102c35af 100644
--- a/modules/indexer/issues/internal/model.go
+++ b/modules/indexer/issues/internal/model.go
@@ -89,22 +89,22 @@ type SearchOptions struct {
 
 	MilestoneIDs []int64 // milestones the issues have
 
-	ProjectID      *int64 // project the issues belong to
-	ProjectBoardID *int64 // project board the issues belong to
+	ProjectID      optional.Option[int64] // project the issues belong to
+	ProjectBoardID optional.Option[int64] // project board the issues belong to
 
-	PosterID *int64 // poster of the issues
+	PosterID optional.Option[int64] // poster of the issues
 
-	AssigneeID *int64 // assignee of the issues, zero means no assignee
+	AssigneeID optional.Option[int64] // assignee of the issues, zero means no assignee
 
-	MentionID *int64 // mentioned user of the issues
+	MentionID optional.Option[int64] // mentioned user of the issues
 
-	ReviewedID        *int64 // reviewer of the issues
-	ReviewRequestedID *int64 // requested reviewer of the issues
+	ReviewedID        optional.Option[int64] // reviewer of the issues
+	ReviewRequestedID optional.Option[int64] // requested reviewer of the issues
 
-	SubscriberID *int64 // subscriber of the issues
+	SubscriberID optional.Option[int64] // subscriber of the issues
 
-	UpdatedAfterUnix  *int64
-	UpdatedBeforeUnix *int64
+	UpdatedAfterUnix  optional.Option[int64]
+	UpdatedBeforeUnix optional.Option[int64]
 
 	db.Paginator
 
diff --git a/modules/indexer/issues/internal/tests/tests.go b/modules/indexer/issues/internal/tests/tests.go
index 6724471539..91aafd589c 100644
--- a/modules/indexer/issues/internal/tests/tests.go
+++ b/modules/indexer/issues/internal/tests/tests.go
@@ -300,10 +300,7 @@ var cases = []*testIndexerCase{
 			Paginator: &db.ListOptions{
 				PageSize: 5,
 			},
-			ProjectID: func() *int64 {
-				id := int64(1)
-				return &id
-			}(),
+			ProjectID: optional.Some(int64(1)),
 		},
 		Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
 			assert.Equal(t, 5, len(result.Hits))
@@ -321,10 +318,7 @@ var cases = []*testIndexerCase{
 			Paginator: &db.ListOptions{
 				PageSize: 5,
 			},
-			ProjectID: func() *int64 {
-				id := int64(0)
-				return &id
-			}(),
+			ProjectID: optional.Some(int64(0)),
 		},
 		Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
 			assert.Equal(t, 5, len(result.Hits))
@@ -342,10 +336,7 @@ var cases = []*testIndexerCase{
 			Paginator: &db.ListOptions{
 				PageSize: 5,
 			},
-			ProjectBoardID: func() *int64 {
-				id := int64(1)
-				return &id
-			}(),
+			ProjectBoardID: optional.Some(int64(1)),
 		},
 		Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
 			assert.Equal(t, 5, len(result.Hits))
@@ -363,10 +354,7 @@ var cases = []*testIndexerCase{
 			Paginator: &db.ListOptions{
 				PageSize: 5,
 			},
-			ProjectBoardID: func() *int64 {
-				id := int64(0)
-				return &id
-			}(),
+			ProjectBoardID: optional.Some(int64(0)),
 		},
 		Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
 			assert.Equal(t, 5, len(result.Hits))
@@ -384,10 +372,7 @@ var cases = []*testIndexerCase{
 			Paginator: &db.ListOptions{
 				PageSize: 5,
 			},
-			PosterID: func() *int64 {
-				id := int64(1)
-				return &id
-			}(),
+			PosterID: optional.Some(int64(1)),
 		},
 		Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
 			assert.Equal(t, 5, len(result.Hits))
@@ -405,10 +390,7 @@ var cases = []*testIndexerCase{
 			Paginator: &db.ListOptions{
 				PageSize: 5,
 			},
-			AssigneeID: func() *int64 {
-				id := int64(1)
-				return &id
-			}(),
+			AssigneeID: optional.Some(int64(1)),
 		},
 		Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
 			assert.Equal(t, 5, len(result.Hits))
@@ -426,10 +408,7 @@ var cases = []*testIndexerCase{
 			Paginator: &db.ListOptions{
 				PageSize: 5,
 			},
-			AssigneeID: func() *int64 {
-				id := int64(0)
-				return &id
-			}(),
+			AssigneeID: optional.Some(int64(0)),
 		},
 		Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
 			assert.Equal(t, 5, len(result.Hits))
@@ -447,10 +426,7 @@ var cases = []*testIndexerCase{
 			Paginator: &db.ListOptions{
 				PageSize: 5,
 			},
-			MentionID: func() *int64 {
-				id := int64(1)
-				return &id
-			}(),
+			MentionID: optional.Some(int64(1)),
 		},
 		Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
 			assert.Equal(t, 5, len(result.Hits))
@@ -468,10 +444,7 @@ var cases = []*testIndexerCase{
 			Paginator: &db.ListOptions{
 				PageSize: 5,
 			},
-			ReviewedID: func() *int64 {
-				id := int64(1)
-				return &id
-			}(),
+			ReviewedID: optional.Some(int64(1)),
 		},
 		Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
 			assert.Equal(t, 5, len(result.Hits))
@@ -489,10 +462,7 @@ var cases = []*testIndexerCase{
 			Paginator: &db.ListOptions{
 				PageSize: 5,
 			},
-			ReviewRequestedID: func() *int64 {
-				id := int64(1)
-				return &id
-			}(),
+			ReviewRequestedID: optional.Some(int64(1)),
 		},
 		Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
 			assert.Equal(t, 5, len(result.Hits))
@@ -510,10 +480,7 @@ var cases = []*testIndexerCase{
 			Paginator: &db.ListOptions{
 				PageSize: 5,
 			},
-			SubscriberID: func() *int64 {
-				id := int64(1)
-				return &id
-			}(),
+			SubscriberID: optional.Some(int64(1)),
 		},
 		Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
 			assert.Equal(t, 5, len(result.Hits))
@@ -531,14 +498,8 @@ var cases = []*testIndexerCase{
 			Paginator: &db.ListOptions{
 				PageSize: 5,
 			},
-			UpdatedAfterUnix: func() *int64 {
-				var t int64 = 20
-				return &t
-			}(),
-			UpdatedBeforeUnix: func() *int64 {
-				var t int64 = 30
-				return &t
-			}(),
+			UpdatedAfterUnix:  optional.Some(int64(20)),
+			UpdatedBeforeUnix: optional.Some(int64(30)),
 		},
 		Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
 			assert.Equal(t, 5, len(result.Hits))
diff --git a/modules/indexer/issues/meilisearch/meilisearch.go b/modules/indexer/issues/meilisearch/meilisearch.go
index c429920065..34066bf559 100644
--- a/modules/indexer/issues/meilisearch/meilisearch.go
+++ b/modules/indexer/issues/meilisearch/meilisearch.go
@@ -170,41 +170,41 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
 		query.And(inner_meilisearch.NewFilterIn("milestone_id", options.MilestoneIDs...))
 	}
 
-	if options.ProjectID != nil {
-		query.And(inner_meilisearch.NewFilterEq("project_id", *options.ProjectID))
+	if options.ProjectID.Has() {
+		query.And(inner_meilisearch.NewFilterEq("project_id", options.ProjectID.Value()))
 	}
-	if options.ProjectBoardID != nil {
-		query.And(inner_meilisearch.NewFilterEq("project_board_id", *options.ProjectBoardID))
+	if options.ProjectBoardID.Has() {
+		query.And(inner_meilisearch.NewFilterEq("project_board_id", options.ProjectBoardID.Value()))
 	}
 
-	if options.PosterID != nil {
-		query.And(inner_meilisearch.NewFilterEq("poster_id", *options.PosterID))
+	if options.PosterID.Has() {
+		query.And(inner_meilisearch.NewFilterEq("poster_id", options.PosterID.Value()))
 	}
 
-	if options.AssigneeID != nil {
-		query.And(inner_meilisearch.NewFilterEq("assignee_id", *options.AssigneeID))
+	if options.AssigneeID.Has() {
+		query.And(inner_meilisearch.NewFilterEq("assignee_id", options.AssigneeID.Value()))
 	}
 
-	if options.MentionID != nil {
-		query.And(inner_meilisearch.NewFilterEq("mention_ids", *options.MentionID))
+	if options.MentionID.Has() {
+		query.And(inner_meilisearch.NewFilterEq("mention_ids", options.MentionID.Value()))
 	}
 
-	if options.ReviewedID != nil {
-		query.And(inner_meilisearch.NewFilterEq("reviewed_ids", *options.ReviewedID))
+	if options.ReviewedID.Has() {
+		query.And(inner_meilisearch.NewFilterEq("reviewed_ids", options.ReviewedID.Value()))
 	}
-	if options.ReviewRequestedID != nil {
-		query.And(inner_meilisearch.NewFilterEq("review_requested_ids", *options.ReviewRequestedID))
+	if options.ReviewRequestedID.Has() {
+		query.And(inner_meilisearch.NewFilterEq("review_requested_ids", options.ReviewRequestedID.Value()))
 	}
 
-	if options.SubscriberID != nil {
-		query.And(inner_meilisearch.NewFilterEq("subscriber_ids", *options.SubscriberID))
+	if options.SubscriberID.Has() {
+		query.And(inner_meilisearch.NewFilterEq("subscriber_ids", options.SubscriberID.Value()))
 	}
 
-	if options.UpdatedAfterUnix != nil {
-		query.And(inner_meilisearch.NewFilterGte("updated_unix", *options.UpdatedAfterUnix))
+	if options.UpdatedAfterUnix.Has() {
+		query.And(inner_meilisearch.NewFilterGte("updated_unix", options.UpdatedAfterUnix.Value()))
 	}
-	if options.UpdatedBeforeUnix != nil {
-		query.And(inner_meilisearch.NewFilterLte("updated_unix", *options.UpdatedBeforeUnix))
+	if options.UpdatedBeforeUnix.Has() {
+		query.And(inner_meilisearch.NewFilterLte("updated_unix", options.UpdatedBeforeUnix.Value()))
 	}
 
 	if options.SortBy == "" {
diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go
index 67ede23887..843da55139 100644
--- a/routers/api/v1/repo/issue.go
+++ b/routers/api/v1/repo/issue.go
@@ -269,28 +269,28 @@ func SearchIssues(ctx *context.APIContext) {
 	}
 
 	if since != 0 {
-		searchOpt.UpdatedAfterUnix = &since
+		searchOpt.UpdatedAfterUnix = optional.Some(since)
 	}
 	if before != 0 {
-		searchOpt.UpdatedBeforeUnix = &before
+		searchOpt.UpdatedBeforeUnix = optional.Some(before)
 	}
 
 	if ctx.IsSigned {
 		ctxUserID := ctx.Doer.ID
 		if ctx.FormBool("created") {
-			searchOpt.PosterID = &ctxUserID
+			searchOpt.PosterID = optional.Some(ctxUserID)
 		}
 		if ctx.FormBool("assigned") {
-			searchOpt.AssigneeID = &ctxUserID
+			searchOpt.AssigneeID = optional.Some(ctxUserID)
 		}
 		if ctx.FormBool("mentioned") {
-			searchOpt.MentionID = &ctxUserID
+			searchOpt.MentionID = optional.Some(ctxUserID)
 		}
 		if ctx.FormBool("review_requested") {
-			searchOpt.ReviewRequestedID = &ctxUserID
+			searchOpt.ReviewRequestedID = optional.Some(ctxUserID)
 		}
 		if ctx.FormBool("reviewed") {
-			searchOpt.ReviewedID = &ctxUserID
+			searchOpt.ReviewedID = optional.Some(ctxUserID)
 		}
 	}
 
@@ -502,10 +502,10 @@ func ListIssues(ctx *context.APIContext) {
 		SortBy:    issue_indexer.SortByCreatedDesc,
 	}
 	if since != 0 {
-		searchOpt.UpdatedAfterUnix = &since
+		searchOpt.UpdatedAfterUnix = optional.Some(since)
 	}
 	if before != 0 {
-		searchOpt.UpdatedBeforeUnix = &before
+		searchOpt.UpdatedBeforeUnix = optional.Some(before)
 	}
 	if len(labelIDs) == 1 && labelIDs[0] == 0 {
 		searchOpt.NoLabelOnly = true
@@ -526,13 +526,13 @@ func ListIssues(ctx *context.APIContext) {
 	}
 
 	if createdByID > 0 {
-		searchOpt.PosterID = &createdByID
+		searchOpt.PosterID = optional.Some(createdByID)
 	}
 	if assignedByID > 0 {
-		searchOpt.AssigneeID = &assignedByID
+		searchOpt.AssigneeID = optional.Some(assignedByID)
 	}
 	if mentionedByID > 0 {
-		searchOpt.MentionID = &mentionedByID
+		searchOpt.MentionID = optional.Some(mentionedByID)
 	}
 
 	ids, total, err := issue_indexer.SearchIssues(ctx, searchOpt)
diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index 2e2d12672e..e4193e9b79 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -2636,9 +2636,9 @@ func SearchIssues(ctx *context.Context) {
 		}
 	}
 
-	var projectID *int64
+	projectID := optional.None[int64]()
 	if v := ctx.FormInt64("project"); v > 0 {
-		projectID = &v
+		projectID = optional.Some(v)
 	}
 
 	// this api is also used in UI,
@@ -2667,28 +2667,28 @@ func SearchIssues(ctx *context.Context) {
 	}
 
 	if since != 0 {
-		searchOpt.UpdatedAfterUnix = &since
+		searchOpt.UpdatedAfterUnix = optional.Some(since)
 	}
 	if before != 0 {
-		searchOpt.UpdatedBeforeUnix = &before
+		searchOpt.UpdatedBeforeUnix = optional.Some(before)
 	}
 
 	if ctx.IsSigned {
 		ctxUserID := ctx.Doer.ID
 		if ctx.FormBool("created") {
-			searchOpt.PosterID = &ctxUserID
+			searchOpt.PosterID = optional.Some(ctxUserID)
 		}
 		if ctx.FormBool("assigned") {
-			searchOpt.AssigneeID = &ctxUserID
+			searchOpt.AssigneeID = optional.Some(ctxUserID)
 		}
 		if ctx.FormBool("mentioned") {
-			searchOpt.MentionID = &ctxUserID
+			searchOpt.MentionID = optional.Some(ctxUserID)
 		}
 		if ctx.FormBool("review_requested") {
-			searchOpt.ReviewRequestedID = &ctxUserID
+			searchOpt.ReviewRequestedID = optional.Some(ctxUserID)
 		}
 		if ctx.FormBool("reviewed") {
-			searchOpt.ReviewedID = &ctxUserID
+			searchOpt.ReviewedID = optional.Some(ctxUserID)
 		}
 	}
 
@@ -2795,9 +2795,9 @@ func ListIssues(ctx *context.Context) {
 		}
 	}
 
-	var projectID *int64
+	projectID := optional.None[int64]()
 	if v := ctx.FormInt64("project"); v > 0 {
-		projectID = &v
+		projectID = optional.Some(v)
 	}
 
 	isPull := optional.None[bool]()
@@ -2835,10 +2835,10 @@ func ListIssues(ctx *context.Context) {
 		SortBy:         issue_indexer.SortByCreatedDesc,
 	}
 	if since != 0 {
-		searchOpt.UpdatedAfterUnix = &since
+		searchOpt.UpdatedAfterUnix = optional.Some(since)
 	}
 	if before != 0 {
-		searchOpt.UpdatedBeforeUnix = &before
+		searchOpt.UpdatedBeforeUnix = optional.Some(before)
 	}
 	if len(labelIDs) == 1 && labelIDs[0] == 0 {
 		searchOpt.NoLabelOnly = true
@@ -2859,13 +2859,13 @@ func ListIssues(ctx *context.Context) {
 	}
 
 	if createdByID > 0 {
-		searchOpt.PosterID = &createdByID
+		searchOpt.PosterID = optional.Some(createdByID)
 	}
 	if assignedByID > 0 {
-		searchOpt.AssigneeID = &assignedByID
+		searchOpt.AssigneeID = optional.Some(assignedByID)
 	}
 	if mentionedByID > 0 {
-		searchOpt.MentionID = &mentionedByID
+		searchOpt.MentionID = optional.Some(mentionedByID)
 	}
 
 	ids, total, err := issue_indexer.SearchIssues(ctx, searchOpt)
diff --git a/routers/web/user/home.go b/routers/web/user/home.go
index 31ee0e40df..e58fb95131 100644
--- a/routers/web/user/home.go
+++ b/routers/web/user/home.go
@@ -791,15 +791,15 @@ func getUserIssueStats(ctx *context.Context, ctxUser *user_model.User, filterMod
 		case issues_model.FilterModeYourRepositories:
 			openClosedOpts.AllPublic = false
 		case issues_model.FilterModeAssign:
-			openClosedOpts.AssigneeID = &doerID
+			openClosedOpts.AssigneeID = optional.Some(doerID)
 		case issues_model.FilterModeCreate:
-			openClosedOpts.PosterID = &doerID
+			openClosedOpts.PosterID = optional.Some(doerID)
 		case issues_model.FilterModeMention:
-			openClosedOpts.MentionID = &doerID
+			openClosedOpts.MentionID = optional.Some(doerID)
 		case issues_model.FilterModeReviewRequested:
-			openClosedOpts.ReviewRequestedID = &doerID
+			openClosedOpts.ReviewRequestedID = optional.Some(doerID)
 		case issues_model.FilterModeReviewed:
-			openClosedOpts.ReviewedID = &doerID
+			openClosedOpts.ReviewedID = optional.Some(doerID)
 		}
 		openClosedOpts.IsClosed = optional.Some(false)
 		ret.OpenCount, err = issue_indexer.CountIssues(ctx, openClosedOpts)
@@ -817,23 +817,23 @@ func getUserIssueStats(ctx *context.Context, ctxUser *user_model.User, filterMod
 	if err != nil {
 		return nil, err
 	}
-	ret.AssignCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.AssigneeID = &doerID }))
+	ret.AssignCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.AssigneeID = optional.Some(doerID) }))
 	if err != nil {
 		return nil, err
 	}
-	ret.CreateCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.PosterID = &doerID }))
+	ret.CreateCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.PosterID = optional.Some(doerID) }))
 	if err != nil {
 		return nil, err
 	}
-	ret.MentionCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.MentionID = &doerID }))
+	ret.MentionCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.MentionID = optional.Some(doerID) }))
 	if err != nil {
 		return nil, err
 	}
-	ret.ReviewRequestedCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.ReviewRequestedID = &doerID }))
+	ret.ReviewRequestedCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.ReviewRequestedID = optional.Some(doerID) }))
 	if err != nil {
 		return nil, err
 	}
-	ret.ReviewedCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.ReviewedID = &doerID }))
+	ret.ReviewedCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.ReviewedID = optional.Some(doerID) }))
 	if err != nil {
 		return nil, err
 	}