diff --git a/app/channel.go b/app/channel.go index 50ce699546..2c9bf1498e 100644 --- a/app/channel.go +++ b/app/channel.go @@ -1774,11 +1774,7 @@ func (a *App) AutocompleteChannelsForSearch(teamId string, userId string, term s term = strings.TrimSpace(term) - result := <-a.Srv.Store.Channel().AutocompleteInTeamForSearch(teamId, userId, term, includeDeleted) - if result.Err != nil { - return nil, result.Err - } - return result.Data.(*model.ChannelList), nil + return a.Srv.Store.Channel().AutocompleteInTeamForSearch(teamId, userId, term, includeDeleted) } func (a *App) SearchAllChannels(term string, opts model.ChannelSearchOpts) (*model.ChannelListWithTeamData, *model.AppError) { diff --git a/store/sqlstore/channel_store.go b/store/sqlstore/channel_store.go index 10e90909a0..a675dbd972 100644 --- a/store/sqlstore/channel_store.go +++ b/store/sqlstore/channel_store.go @@ -1974,59 +1974,56 @@ func (s SqlChannelStore) AutocompleteInTeam(teamId string, term string, includeD return &channels, nil } -func (s SqlChannelStore) AutocompleteInTeamForSearch(teamId string, userId string, term string, includeDeleted bool) store.StoreChannel { - return store.Do(func(result *store.StoreResult) { - deleteFilter := "AND DeleteAt = 0" - if includeDeleted { - deleteFilter = "" +func (s SqlChannelStore) AutocompleteInTeamForSearch(teamId string, userId string, term string, includeDeleted bool) (*model.ChannelList, *model.AppError) { + deleteFilter := "AND DeleteAt = 0" + if includeDeleted { + deleteFilter = "" + } + + queryFormat := ` + SELECT + C.* + FROM + Channels AS C + JOIN + ChannelMembers AS CM ON CM.ChannelId = C.Id + WHERE + (C.TeamId = :TeamId OR (C.TeamId = '' AND C.Type = 'G')) + AND CM.UserId = :UserId + ` + deleteFilter + ` + %v + LIMIT 50` + + var channels model.ChannelList + + if likeClause, likeTerm := s.buildLIKEClause(term, "Name, DisplayName, Purpose"); likeClause == "" { + if _, err := s.GetReplica().Select(&channels, fmt.Sprintf(queryFormat, ""), map[string]interface{}{"TeamId": teamId, "UserId": userId}); err != nil { + return nil, model.NewAppError("SqlChannelStore.AutocompleteInTeamForSearch", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError) } + } else { + // Using a UNION results in index_merge and fulltext queries and is much faster than the ref + // query you would get using an OR of the LIKE and full-text clauses. + fulltextClause, fulltextTerm := s.buildFulltextClause(term, "Name, DisplayName, Purpose") + likeQuery := fmt.Sprintf(queryFormat, "AND "+likeClause) + fulltextQuery := fmt.Sprintf(queryFormat, "AND "+fulltextClause) + query := fmt.Sprintf("(%v) UNION (%v) LIMIT 50", likeQuery, fulltextQuery) - queryFormat := ` - SELECT - C.* - FROM - Channels AS C - JOIN - ChannelMembers AS CM ON CM.ChannelId = C.Id - WHERE - (C.TeamId = :TeamId OR (C.TeamId = '' AND C.Type = 'G')) - AND CM.UserId = :UserId - ` + deleteFilter + ` - %v - LIMIT 50` - - var channels model.ChannelList - - if likeClause, likeTerm := s.buildLIKEClause(term, "Name, DisplayName, Purpose"); likeClause == "" { - if _, err := s.GetReplica().Select(&channels, fmt.Sprintf(queryFormat, ""), map[string]interface{}{"TeamId": teamId, "UserId": userId}); err != nil { - result.Err = model.NewAppError("SqlChannelStore.AutocompleteInTeamForSearch", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError) - } - } else { - // Using a UNION results in index_merge and fulltext queries and is much faster than the ref - // query you would get using an OR of the LIKE and full-text clauses. - fulltextClause, fulltextTerm := s.buildFulltextClause(term, "Name, DisplayName, Purpose") - likeQuery := fmt.Sprintf(queryFormat, "AND "+likeClause) - fulltextQuery := fmt.Sprintf(queryFormat, "AND "+fulltextClause) - query := fmt.Sprintf("(%v) UNION (%v) LIMIT 50", likeQuery, fulltextQuery) - - if _, err := s.GetReplica().Select(&channels, query, map[string]interface{}{"TeamId": teamId, "UserId": userId, "LikeTerm": likeTerm, "FulltextTerm": fulltextTerm}); err != nil { - result.Err = model.NewAppError("SqlChannelStore.AutocompleteInTeamForSearch", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError) - } + if _, err := s.GetReplica().Select(&channels, query, map[string]interface{}{"TeamId": teamId, "UserId": userId, "LikeTerm": likeTerm, "FulltextTerm": fulltextTerm}); err != nil { + return nil, model.NewAppError("SqlChannelStore.AutocompleteInTeamForSearch", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError) } + } - directChannels, err := s.autocompleteInTeamForSearchDirectMessages(userId, term) - if err != nil { - result.Err = err - return - } + directChannels, err := s.autocompleteInTeamForSearchDirectMessages(userId, term) + if err != nil { + return nil, err + } - channels = append(channels, directChannels...) + channels = append(channels, directChannels...) - sort.Slice(channels, func(a, b int) bool { - return strings.ToLower(channels[a].DisplayName) < strings.ToLower(channels[b].DisplayName) - }) - result.Data = &channels + sort.Slice(channels, func(a, b int) bool { + return strings.ToLower(channels[a].DisplayName) < strings.ToLower(channels[b].DisplayName) }) + return &channels, nil } func (s SqlChannelStore) autocompleteInTeamForSearchDirectMessages(userId string, term string) ([]*model.Channel, *model.AppError) { diff --git a/store/store.go b/store/store.go index 60f7886da2..6f161d51e3 100644 --- a/store/store.go +++ b/store/store.go @@ -182,7 +182,7 @@ type ChannelStore interface { GetMembersForUser(teamId string, userId string) (*model.ChannelMembers, *model.AppError) GetMembersForUserWithPagination(teamId, userId string, page, perPage int) StoreChannel AutocompleteInTeam(teamId string, term string, includeDeleted bool) (*model.ChannelList, *model.AppError) - AutocompleteInTeamForSearch(teamId string, userId string, term string, includeDeleted bool) StoreChannel + AutocompleteInTeamForSearch(teamId string, userId string, term string, includeDeleted bool) (*model.ChannelList, *model.AppError) SearchAllChannels(term string, opts ChannelSearchOpts) (*model.ChannelListWithTeamData, *model.AppError) SearchInTeam(teamId string, term string, includeDeleted bool) (*model.ChannelList, *model.AppError) SearchMore(userId string, teamId string, term string) (*model.ChannelList, *model.AppError) diff --git a/store/storetest/channel_store.go b/store/storetest/channel_store.go index b992db51af..a6163f0eb2 100644 --- a/store/storetest/channel_store.go +++ b/store/storetest/channel_store.go @@ -2672,9 +2672,8 @@ func testChannelStoreAutocompleteInTeamForSearch(t *testing.T, ss store.Store, s for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { - result := <-ss.Channel().AutocompleteInTeamForSearch(o1.TeamId, m1.UserId, "ChannelA", false) - require.Nil(t, result.Err) - channels := result.Data.(*model.ChannelList) + channels, err := ss.Channel().AutocompleteInTeamForSearch(o1.TeamId, m1.UserId, "ChannelA", false) + require.Nil(t, err) require.Len(t, *channels, 2) }) } diff --git a/store/storetest/mocks/ChannelStore.go b/store/storetest/mocks/ChannelStore.go index 2fe242fe5d..ad1e8b299c 100644 --- a/store/storetest/mocks/ChannelStore.go +++ b/store/storetest/mocks/ChannelStore.go @@ -85,19 +85,28 @@ func (_m *ChannelStore) AutocompleteInTeam(teamId string, term string, includeDe } // AutocompleteInTeamForSearch provides a mock function with given fields: teamId, userId, term, includeDeleted -func (_m *ChannelStore) AutocompleteInTeamForSearch(teamId string, userId string, term string, includeDeleted bool) store.StoreChannel { +func (_m *ChannelStore) AutocompleteInTeamForSearch(teamId string, userId string, term string, includeDeleted bool) (*model.ChannelList, *model.AppError) { ret := _m.Called(teamId, userId, term, includeDeleted) - var r0 store.StoreChannel - if rf, ok := ret.Get(0).(func(string, string, string, bool) store.StoreChannel); ok { + var r0 *model.ChannelList + if rf, ok := ret.Get(0).(func(string, string, string, bool) *model.ChannelList); ok { r0 = rf(teamId, userId, term, includeDeleted) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(store.StoreChannel) + r0 = ret.Get(0).(*model.ChannelList) } } - return r0 + var r1 *model.AppError + if rf, ok := ret.Get(1).(func(string, string, string, bool) *model.AppError); ok { + r1 = rf(teamId, userId, term, includeDeleted) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } + } + + return r0, r1 } // ClearAllCustomRoleAssignments provides a mock function with given fields: