diff --git a/api4/channel.go b/api4/channel.go index b8eac37c98..b8064e5e8b 100644 --- a/api4/channel.go +++ b/api4/channel.go @@ -1158,7 +1158,6 @@ func searchAllChannels(c *Context, w http.ResponseWriter, r *http.Request) { includeDeleted, _ := strconv.ParseBool(r.URL.Query().Get("include_deleted")) includeDeleted = includeDeleted || props.IncludeDeleted - opts := model.ChannelSearchOpts{ NotAssociatedToGroup: props.NotAssociatedToGroup, ExcludeDefaultChannels: props.ExcludeDefaultChannels, @@ -1166,6 +1165,7 @@ func searchAllChannels(c *Context, w http.ResponseWriter, r *http.Request) { GroupConstrained: props.GroupConstrained, ExcludeGroupConstrained: props.ExcludeGroupConstrained, ExcludePolicyConstrained: props.ExcludePolicyConstrained, + IncludeSearchById: props.IncludeSearchById, Public: props.Public, Private: props.Private, IncludeDeleted: includeDeleted, diff --git a/app/channel.go b/app/channel.go index ad78141260..c1df9e0beb 100644 --- a/app/channel.go +++ b/app/channel.go @@ -2780,6 +2780,7 @@ func (a *App) SearchAllChannels(term string, opts model.ChannelSearchOpts) (mode ExcludeGroupConstrained: opts.ExcludeGroupConstrained, PolicyID: opts.PolicyID, IncludePolicyID: opts.IncludePolicyID, + IncludeSearchById: opts.IncludeSearchById, ExcludePolicyConstrained: opts.ExcludePolicyConstrained, Public: opts.Public, Private: opts.Private, diff --git a/model/channel.go b/model/channel.go index 32742582c1..eb0e220a20 100644 --- a/model/channel.go +++ b/model/channel.go @@ -123,6 +123,7 @@ type ChannelModeratedRolesPatch struct { // ExcludeDefaultChannels will exclude the configured default channels (ex 'town-square' and 'off-topic'). // IncludeDeleted will include channel records where DeleteAt != 0. // ExcludeChannelNames will exclude channels from the results by name. +// IncludeSearchById will include searching matches against channel IDs in the results // Paginate whether to paginate the results. // Page page requested, if results are paginated. // PerPage number of results per page, if paginated. @@ -139,6 +140,7 @@ type ChannelSearchOpts struct { PolicyID string ExcludePolicyConstrained bool IncludePolicyID bool + IncludeSearchById bool Public bool Private bool Page *int diff --git a/model/channel_search.go b/model/channel_search.go index 530deb2022..7c933cf574 100644 --- a/model/channel_search.go +++ b/model/channel_search.go @@ -16,6 +16,7 @@ type ChannelSearch struct { Public bool `json:"public"` Private bool `json:"private"` IncludeDeleted bool `json:"include_deleted"` + IncludeSearchById bool `json:"include_search_by_id"` Deleted bool `json:"deleted"` Page *int `json:"page,omitempty"` PerPage *int `json:"per_page,omitempty"` diff --git a/store/sqlstore/channel_store.go b/store/sqlstore/channel_store.go index fde764f40b..7ba6eebcfb 100644 --- a/store/sqlstore/channel_store.go +++ b/store/sqlstore/channel_store.go @@ -3292,14 +3292,26 @@ func (s SqlChannelStore) channelSearchQuery(opts *store.ChannelSearchOpts) sq.Se LeftJoin("RetentionPoliciesChannels ON c.Id = RetentionPoliciesChannels.ChannelId") } - likeClause, likeTerm := s.buildLIKEClause(opts.Term, "c.Name, c.DisplayName, c.Purpose") + likeFields := "c.Name, c.DisplayName, c.Purpose" + if opts.IncludeSearchById { + likeFields = likeFields + ", c.Id" + } + + likeClause, likeTerm := s.buildLIKEClause(opts.Term, likeFields) + if likeTerm != "" { + // Keep the number of likeTerms same as the number of columns + // (c.Name, c.DisplayName, c.Purpose, c.Id?) + likeTerms := make([]interface{}, len(strings.Split(likeFields, ","))) + for i := 0; i < len(likeTerms); i++ { + likeTerms[i] = likeTerm + } likeClause = strings.ReplaceAll(likeClause, ":LikeTerm", "?") fulltextClause, fulltextTerm := s.buildFulltextClause(opts.Term, "c.Name, c.DisplayName, c.Purpose") fulltextClause = strings.ReplaceAll(fulltextClause, ":FulltextTerm", "?") + query = query.Where(sq.Or{ - sq.Expr(likeClause, likeTerm, likeTerm, likeTerm), // Keep the number of likeTerms same as the number - // of columns (c.Name, c.DisplayName, c.Purpose) + sq.Expr(likeClause, likeTerms...), sq.Expr(fulltextClause, fulltextTerm), }) } diff --git a/store/store.go b/store/store.go index 7a47597150..619f8936bd 100644 --- a/store/store.go +++ b/store/store.go @@ -939,6 +939,7 @@ type SharedChannelStore interface { // NotAssociatedToGroup will exclude channels that have associated, active GroupChannels records. // IncludeDeleted will include channel records where DeleteAt != 0. // ExcludeChannelNames will exclude channels from the results by name. +// IncludeSearchById will include searching matches against channel IDs in the results // Paginate whether to paginate the results. // Page page requested, if results are paginated. // PerPage number of results per page, if paginated. @@ -956,6 +957,7 @@ type ChannelSearchOpts struct { ExcludePolicyConstrained bool IncludePolicyID bool IncludeTeamInfo bool + IncludeSearchById bool CountOnly bool Public bool Private bool diff --git a/store/storetest/channel_store.go b/store/storetest/channel_store.go index 5c87ff4e98..a567ddff7d 100644 --- a/store/storetest/channel_store.go +++ b/store/storetest/channel_store.go @@ -6448,6 +6448,7 @@ func testChannelStoreSearchAllChannels(t *testing.T, ss store.Store) { {"Filter team 1 and team 2, public and private and group constrained", "", store.ChannelSearchOpts{IncludeDeleted: false, TeamIds: []string{t1.Id, t2.Id}, Public: true, Private: true, GroupConstrained: true, Page: model.NewInt(0), PerPage: model.NewInt(5)}, model.ChannelList{&o5}, 1}, {"Filter team 1 and team 2, public and private and exclude group constrained", "", store.ChannelSearchOpts{IncludeDeleted: false, TeamIds: []string{t1.Id, t2.Id}, Public: true, Private: true, ExcludeGroupConstrained: true, Page: model.NewInt(0), PerPage: model.NewInt(5)}, model.ChannelList{&o1, &o2, &o3, &o4, &o6}, 12}, {"Filter deleted returns only deleted channels", "", store.ChannelSearchOpts{Deleted: true, Page: model.NewInt(0), PerPage: model.NewInt(5)}, model.ChannelList{&o13}, 1}, + {"Search ChannelA by id", o1.Id, store.ChannelSearchOpts{IncludeDeleted: false, Page: model.NewInt(0), PerPage: model.NewInt(5), IncludeSearchById: true}, model.ChannelList{&o1}, 1}, } for _, testCase := range testCases {