diff --git a/pkg/models/task_collection_test.go b/pkg/models/task_collection_test.go index fc163ab8be0..14e9860663c 100644 --- a/pkg/models/task_collection_test.go +++ b/pkg/models/task_collection_test.go @@ -1022,6 +1022,19 @@ func TestTaskCollection_ReadAll(t *testing.T) { }, wantErr: false, }, + { + name: "search for task index", + fields: fields{}, + args: args{ + search: "number #17", + a: &user.User{ID: 1}, + page: 0, + }, + want: []*Task{ + task33, // has the index 17 + }, + wantErr: false, + }, } for _, tt := range tests { diff --git a/pkg/models/tasks.go b/pkg/models/tasks.go index ae3df859b51..db8eb30cdd2 100644 --- a/pkg/models/tasks.go +++ b/pkg/models/tasks.go @@ -18,8 +18,10 @@ package models import ( "math" + "regexp" "sort" "strconv" + "strings" "time" "code.vikunja.io/api/pkg/events" @@ -237,6 +239,15 @@ func getFilterCondForSeparateTable(table string, concat taskFilterConcatinator, ) } +func getTaskIndexFromSearchString(s string) (index int64) { + re := regexp.MustCompile("#([0-9]+)") + in := re.FindString(s) + + stringIndex := strings.ReplaceAll(in, "#", "") + index, _ = strconv.ParseInt(stringIndex, 10, 64) + return +} + //nolint:gocyclo func getRawTasksForLists(s *xorm.Session, lists []*List, a web.Auth, opts *taskOptions) (tasks []*Task, resultCount int, totalItems int64, err error) { @@ -356,7 +367,7 @@ func getRawTasksForLists(s *xorm.Session, lists []*List, a web.Auth, opts *taskO // Then return all tasks for that lists var where builder.Cond - if len(opts.search) > 0 { + if opts.search != "" { // Postgres' is case sensitive by default. // To work around this, we're using ILIKE as opposed to normal LIKE statements. // ILIKE is preferred over LOWER(text) LIKE for performance reasons. @@ -367,6 +378,11 @@ func getRawTasksForLists(s *xorm.Session, lists []*List, a web.Auth, opts *taskO } else { where = &builder.Like{"title", "%" + opts.search + "%"} } + + searchIndex := getTaskIndexFromSearchString(opts.search) + if searchIndex > 0 { + where = builder.Or(where, builder.Eq{"`index`": searchIndex}) + } } var listIDCond builder.Cond diff --git a/pkg/models/tasks_test.go b/pkg/models/tasks_test.go index 371a98fd4f5..7a5dc69ffb1 100644 --- a/pkg/models/tasks_test.go +++ b/pkg/models/tasks_test.go @@ -761,3 +761,37 @@ func TestTask_ReadOne(t *testing.T) { assert.False(t, task.IsFavorite) }) } + +func Test_getTaskIndexFromSearchString(t *testing.T) { + type args struct { + s string + } + tests := []struct { + name string + args args + wantIndex int64 + }{ + { + name: "task index in text", + args: args{s: "Task #12"}, + wantIndex: 12, + }, + { + name: "no task index", + args: args{s: "Task"}, + wantIndex: 0, + }, + { + name: "not numeric but with prefix", + args: args{s: "Task #aaaaa"}, + wantIndex: 0, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if gotIndex := getTaskIndexFromSearchString(tt.args.s); gotIndex != tt.wantIndex { + t.Errorf("getTaskIndexFromSearchString() = %v, want %v", gotIndex, tt.wantIndex) + } + }) + } +}