diff --git a/modules/indexer/issues/indexer_test.go b/modules/indexer/issues/indexer_test.go
index c3a6d88685..0e36d21313 100644
--- a/modules/indexer/issues/indexer_test.go
+++ b/modules/indexer/issues/indexer_test.go
@@ -5,14 +5,12 @@ package issues
 
 import (
 	"context"
-	"fmt"
-	"path"
 	"path/filepath"
 	"testing"
-	"time"
 
+	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/unittest"
-	"code.gitea.io/gitea/modules/indexer/issues/bleve"
+	"code.gitea.io/gitea/modules/indexer/issues/internal"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/util"
 
@@ -29,122 +27,71 @@ func TestMain(m *testing.M) {
 	})
 }
 
-func TestBleveSearchIssues(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-	setting.CfgProvider, _ = setting.NewConfigProviderFromData("")
-
-	tmpIndexerDir := t.TempDir()
-
-	setting.CfgProvider.Section("queue.issue_indexer").Key("DATADIR").MustString(path.Join(tmpIndexerDir, "issues.queue"))
-
-	oldIssuePath := setting.Indexer.IssuePath
-	setting.Indexer.IssuePath = path.Join(tmpIndexerDir, "issues.queue")
-	defer func() {
-		setting.Indexer.IssuePath = oldIssuePath
-	}()
-
-	setting.Indexer.IssueType = "bleve"
-	setting.LoadQueueSettings()
-	InitIssueIndexer(true)
-	defer func() {
-		if bleveIndexer, ok := (*globalIndexer.Load()).(*bleve.Indexer); ok {
-			bleveIndexer.Close()
-		}
-	}()
-
-	time.Sleep(5 * time.Second)
-
-	t.Run("issue2", func(t *testing.T) {
-		ids, _, err := SearchIssues(context.TODO(), &SearchOptions{
-			Keyword: "issue2",
-			RepoIDs: []int64{1},
-		})
-		assert.NoError(t, err)
-		assert.EqualValues(t, []int64{2}, ids)
-	})
-
-	t.Run("first", func(t *testing.T) {
-		ids, _, err := SearchIssues(context.TODO(), &SearchOptions{
-			Keyword: "first",
-			RepoIDs: []int64{1},
-		})
-		assert.NoError(t, err)
-		assert.EqualValues(t, []int64{1}, ids)
-	})
-
-	t.Run("for", func(t *testing.T) {
-		ids, _, err := SearchIssues(context.TODO(), &SearchOptions{
-			Keyword: "for",
-			RepoIDs: []int64{1},
-		})
-		assert.NoError(t, err)
-		assert.ElementsMatch(t, []int64{1, 2, 3, 5, 11}, ids)
-	})
-
-	t.Run("good", func(t *testing.T) {
-		ids, _, err := SearchIssues(context.TODO(), &SearchOptions{
-			Keyword: "good",
-			RepoIDs: []int64{1},
-		})
-		assert.NoError(t, err)
-		assert.EqualValues(t, []int64{1}, ids)
-	})
-}
-
-func TestDBSearchIssuesWithKeyword(t *testing.T) {
+func TestDBSearchIssues(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
 	setting.Indexer.IssueType = "db"
 	InitIssueIndexer(true)
 
-	t.Run("issue2", func(t *testing.T) {
-		ids, _, err := SearchIssues(context.TODO(), &SearchOptions{
-			Keyword: "issue2",
-			RepoIDs: []int64{1},
-		})
-		assert.NoError(t, err)
-		assert.EqualValues(t, []int64{2}, ids)
-	})
-
-	t.Run("first", func(t *testing.T) {
-		ids, _, err := SearchIssues(context.TODO(), &SearchOptions{
-			Keyword: "first",
-			RepoIDs: []int64{1},
-		})
-		assert.NoError(t, err)
-		assert.EqualValues(t, []int64{1}, ids)
-	})
-
-	t.Run("for", func(t *testing.T) {
-		ids, _, err := SearchIssues(context.TODO(), &SearchOptions{
-			Keyword: "for",
-			RepoIDs: []int64{1},
-		})
-		assert.NoError(t, err)
-		assert.ElementsMatch(t, []int64{1, 2, 3, 5, 11}, ids)
-	})
-
-	t.Run("good", func(t *testing.T) {
-		ids, _, err := SearchIssues(context.TODO(), &SearchOptions{
-			Keyword: "good",
-			RepoIDs: []int64{1},
-		})
-		assert.NoError(t, err)
-		assert.EqualValues(t, []int64{1}, ids)
-	})
+	t.Run("search issues with keyword", searchIssueWithKeyword)
+	t.Run("search issues in repo", searchIssueInRepo)
+	t.Run("search issues by ID", searchIssueByID)
+	t.Run("search issues is pr", searchIssueIsPull)
+	t.Run("search issues is closed", searchIssueIsClosed)
+	t.Run("search issues by milestone", searchIssueByMilestoneID)
+	t.Run("search issues by label", searchIssueByLabelID)
+	t.Run("search issues by time", searchIssueByTime)
+	t.Run("search issues with order", searchIssueWithOrder)
+	t.Run("search issues in project", searchIssueInProject)
+	t.Run("search issues with paginator", searchIssueWithPaginator)
 }
 
-// TODO: add more tests
-func TestDBSearchIssueWithoutKeyword(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-
-	setting.Indexer.IssueType = "db"
-	InitIssueIndexer(true)
-
-	int64Pointer := func(x int64) *int64 {
-		return &x
+func searchIssueWithKeyword(t *testing.T) {
+	tests := []struct {
+		opts        SearchOptions
+		expectedIDs []int64
+	}{
+		{
+			SearchOptions{
+				Keyword: "issue2",
+				RepoIDs: []int64{1},
+			},
+			[]int64{2},
+		},
+		{
+			SearchOptions{
+				Keyword: "first",
+				RepoIDs: []int64{1},
+			},
+			[]int64{1},
+		},
+		{
+			SearchOptions{
+				Keyword: "for",
+				RepoIDs: []int64{1},
+			},
+			[]int64{11, 5, 3, 2, 1},
+		},
+		{
+			SearchOptions{
+				Keyword: "good",
+				RepoIDs: []int64{1},
+			},
+			[]int64{1},
+		},
 	}
-	for _, test := range []struct {
+
+	for _, test := range tests {
+		issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
+		if !assert.NoError(t, err) {
+			return
+		}
+		assert.Equal(t, test.expectedIDs, issueIDs)
+	}
+}
+
+func searchIssueInRepo(t *testing.T) {
+	tests := []struct {
 		opts        SearchOptions
 		expectedIDs []int64
 	}{
@@ -156,59 +103,305 @@ func TestDBSearchIssueWithoutKeyword(t *testing.T) {
 		},
 		{
 			SearchOptions{
-				RepoIDs:    []int64{1},
-				AssigneeID: int64Pointer(1),
+				RepoIDs: []int64{2},
 			},
-			[]int64{1},
+			[]int64{7, 4},
 		},
 		{
 			SearchOptions{
-				RepoIDs:  []int64{1},
-				PosterID: int64Pointer(1),
+				RepoIDs: []int64{3},
 			},
-			[]int64{11, 3, 2, 1},
+			[]int64{12, 6},
 		},
 		{
 			SearchOptions{
-				RepoIDs:  []int64{1},
-				IsClosed: util.OptionalBoolFalse,
+				RepoIDs: []int64{4},
 			},
-			[]int64{11, 3, 2, 1},
+			[]int64{},
 		},
 		{
 			SearchOptions{
-				RepoIDs:  []int64{1},
-				IsClosed: util.OptionalBoolTrue,
+				RepoIDs: []int64{5},
 			},
-			[]int64{5},
+			[]int64{15},
 		},
-		{
-			SearchOptions{
-				RepoIDs: []int64{1},
-			},
-			[]int64{11, 5, 3, 2, 1},
-		},
-		{
-			SearchOptions{
-				RepoIDs:    []int64{1},
-				AssigneeID: int64Pointer(1),
-			},
-			[]int64{1},
-		},
-		{
-			SearchOptions{
-				RepoIDs:  []int64{1},
-				PosterID: int64Pointer(1),
-			},
-			[]int64{11, 3, 2, 1},
-		},
-	} {
-		t.Run(fmt.Sprintf("%#v", test.opts), func(t *testing.T) {
-			issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
-			if !assert.NoError(t, err) {
-				return
-			}
-			assert.Equal(t, test.expectedIDs, issueIDs)
-		})
+	}
+
+	for _, test := range tests {
+		issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
+		if !assert.NoError(t, err) {
+			return
+		}
+		assert.Equal(t, test.expectedIDs, issueIDs)
+	}
+}
+
+func searchIssueByID(t *testing.T) {
+	int64Pointer := func(x int64) *int64 {
+		return &x
+	}
+	tests := []struct {
+		opts        SearchOptions
+		expectedIDs []int64
+	}{
+		{
+			SearchOptions{
+				PosterID: int64Pointer(1),
+			},
+			[]int64{11, 6, 3, 2, 1},
+		},
+		{
+			SearchOptions{
+				AssigneeID: int64Pointer(1),
+			},
+			[]int64{6, 1},
+		},
+		{
+			SearchOptions{
+				MentionID: int64Pointer(4),
+			},
+			[]int64{1},
+		},
+		{
+			SearchOptions{
+				ReviewedID: int64Pointer(1),
+			},
+			[]int64{},
+		},
+		{
+			SearchOptions{
+				ReviewRequestedID: int64Pointer(1),
+			},
+			[]int64{12},
+		},
+		{
+			SearchOptions{
+				SubscriberID: int64Pointer(1),
+			},
+			[]int64{11, 6, 5, 3, 2, 1},
+		},
+	}
+
+	for _, test := range tests {
+		issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
+		if !assert.NoError(t, err) {
+			return
+		}
+		assert.Equal(t, test.expectedIDs, issueIDs)
+	}
+}
+
+func searchIssueIsPull(t *testing.T) {
+	tests := []struct {
+		opts        SearchOptions
+		expectedIDs []int64
+	}{
+		{
+			SearchOptions{
+				IsPull: util.OptionalBoolFalse,
+			},
+			[]int64{17, 16, 15, 14, 13, 6, 5, 18, 10, 7, 4, 1},
+		},
+		{
+			SearchOptions{
+				IsPull: util.OptionalBoolTrue,
+			},
+			[]int64{12, 11, 19, 9, 8, 3, 2},
+		},
+	}
+	for _, test := range tests {
+		issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
+		if !assert.NoError(t, err) {
+			return
+		}
+		assert.Equal(t, test.expectedIDs, issueIDs)
+	}
+}
+
+func searchIssueIsClosed(t *testing.T) {
+	tests := []struct {
+		opts        SearchOptions
+		expectedIDs []int64
+	}{
+		{
+			SearchOptions{
+				IsClosed: util.OptionalBoolFalse,
+			},
+			[]int64{17, 16, 15, 14, 13, 12, 11, 6, 19, 18, 10, 7, 9, 8, 3, 2, 1},
+		},
+		{
+			SearchOptions{
+				IsClosed: util.OptionalBoolTrue,
+			},
+			[]int64{5, 4},
+		},
+	}
+	for _, test := range tests {
+		issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
+		if !assert.NoError(t, err) {
+			return
+		}
+		assert.Equal(t, test.expectedIDs, issueIDs)
+	}
+}
+
+func searchIssueByMilestoneID(t *testing.T) {
+	tests := []struct {
+		opts        SearchOptions
+		expectedIDs []int64
+	}{
+		{
+			SearchOptions{
+				MilestoneIDs: []int64{1},
+			},
+			[]int64{2},
+		},
+		{
+			SearchOptions{
+				MilestoneIDs: []int64{3},
+			},
+			[]int64{3},
+		},
+	}
+	for _, test := range tests {
+		issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
+		if !assert.NoError(t, err) {
+			return
+		}
+		assert.Equal(t, test.expectedIDs, issueIDs)
+	}
+}
+
+func searchIssueByLabelID(t *testing.T) {
+	tests := []struct {
+		opts        SearchOptions
+		expectedIDs []int64
+	}{
+		{
+			SearchOptions{
+				IncludedLabelIDs: []int64{1},
+			},
+			[]int64{2, 1},
+		},
+		{
+			SearchOptions{
+				IncludedLabelIDs: []int64{4},
+			},
+			[]int64{2},
+		},
+		{
+			SearchOptions{
+				ExcludedLabelIDs: []int64{1},
+			},
+			[]int64{17, 16, 15, 14, 13, 12, 11, 6, 5, 19, 18, 10, 7, 4, 9, 8, 3},
+		},
+	}
+	for _, test := range tests {
+		issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
+		if !assert.NoError(t, err) {
+			return
+		}
+		assert.Equal(t, test.expectedIDs, issueIDs)
+	}
+}
+
+func searchIssueByTime(t *testing.T) {
+	int64Pointer := func(i int64) *int64 {
+		return &i
+	}
+	tests := []struct {
+		opts        SearchOptions
+		expectedIDs []int64
+	}{
+		{
+			SearchOptions{
+				UpdatedAfterUnix: int64Pointer(0),
+			},
+			[]int64{17, 16, 15, 14, 13, 12, 11, 6, 5, 19, 18, 10, 7, 4, 9, 8, 3, 2, 1},
+		},
+	}
+	for _, test := range tests {
+		issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
+		if !assert.NoError(t, err) {
+			return
+		}
+		assert.Equal(t, test.expectedIDs, issueIDs)
+	}
+}
+
+func searchIssueWithOrder(t *testing.T) {
+	tests := []struct {
+		opts        SearchOptions
+		expectedIDs []int64
+	}{
+		{
+			SearchOptions{
+				SortBy: internal.SortByCreatedAsc,
+			},
+			[]int64{1, 2, 3, 8, 9, 4, 7, 10, 18, 19, 5, 6, 11, 12, 13, 14, 15, 16, 17},
+		},
+	}
+	for _, test := range tests {
+		issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
+		if !assert.NoError(t, err) {
+			return
+		}
+		assert.Equal(t, test.expectedIDs, issueIDs)
+	}
+}
+
+func searchIssueInProject(t *testing.T) {
+	int64Pointer := func(i int64) *int64 {
+		return &i
+	}
+	tests := []struct {
+		opts        SearchOptions
+		expectedIDs []int64
+	}{
+		{
+			SearchOptions{
+				ProjectID: int64Pointer(1),
+			},
+			[]int64{5, 3, 2, 1},
+		},
+		{
+			SearchOptions{
+				ProjectBoardID: int64Pointer(1),
+			},
+			[]int64{1},
+		},
+	}
+	for _, test := range tests {
+		issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
+		if !assert.NoError(t, err) {
+			return
+		}
+		assert.Equal(t, test.expectedIDs, issueIDs)
+	}
+}
+
+func searchIssueWithPaginator(t *testing.T) {
+	tests := []struct {
+		opts          SearchOptions
+		expectedIDs   []int64
+		expectedTotal int64
+	}{
+		{
+			SearchOptions{
+				Paginator: &db.ListOptions{
+					PageSize: 5,
+				},
+			},
+			[]int64{17, 16, 15, 14, 13},
+			19,
+		},
+	}
+	for _, test := range tests {
+		issueIDs, total, err := SearchIssues(context.TODO(), &test.opts)
+		if !assert.NoError(t, err) {
+			return
+		}
+		assert.Equal(t, test.expectedIDs, issueIDs)
+		assert.Equal(t, test.expectedTotal, total)
 	}
 }