From 406dcb131122676735c6344dac8d98e396b437cf Mon Sep 17 00:00:00 2001 From: Miguel de la Cruz Date: Tue, 18 Jun 2019 14:43:08 +0100 Subject: [PATCH] [MM-15811] Adds fallback mechanism for elasticsearch queries (#11065) * [MM-15811] Adds fallback mechanism for elasticsearch queries * Rename config helpers --- app/channel.go | 98 ++++++++++---------- app/config.go | 16 ++++ app/post.go | 166 ++++++++++++++++++---------------- app/user.go | 237 +++++++++++++++++++++++++++++++------------------ 4 files changed, 307 insertions(+), 210 deletions(-) diff --git a/app/channel.go b/app/channel.go index 5cc6c574d9..8db29b4796 100644 --- a/app/channel.go +++ b/app/channel.go @@ -107,8 +107,7 @@ func (a *App) JoinDefaultChannels(teamId string, user *model.User, shouldBeAdmin } } - esInterface := a.Elasticsearch - if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing { + if a.IsESIndexingEnabled() { a.Srv.Go(func() { if err = a.indexUser(user); err != nil { mlog.Error("Encountered error indexing user", mlog.String("user_id", user.Id), mlog.Err(err)) @@ -185,8 +184,7 @@ func (a *App) CreateChannelWithUser(channel *model.Channel, userId string) (*mod message.Add("team_id", channel.TeamId) a.Publish(message) - esInterface := a.Elasticsearch - if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing { + if a.IsESIndexingEnabled() { a.Srv.Go(func() { if err := a.indexUser(user); err != nil { mlog.Error("Encountered error indexing user", mlog.String("user_id", user.Id), mlog.Err(err)) @@ -261,11 +259,10 @@ func (a *App) CreateChannel(channel *model.Channel, addMember bool) (*model.Chan }) } - esInterface := a.Elasticsearch - if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing { - if sc.Type == "O" { + if a.IsESIndexingEnabled() { + if sc.Type == model.CHANNEL_OPEN { a.Srv.Go(func() { - if err := esInterface.IndexChannel(sc); err != nil { + if err := a.Elasticsearch.IndexChannel(sc); err != nil { mlog.Error("Encountered error indexing channel", mlog.String("channel_id", sc.Id), mlog.Err(err)) } }) @@ -309,8 +306,7 @@ func (a *App) GetOrCreateDirectChannel(userId, otherUserId string) (*model.Chann }) } - esInterface := a.Elasticsearch - if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing { + if a.IsESIndexingEnabled() { a.Srv.Go(func() { for _, id := range []string{userId, otherUserId} { if err := a.indexUserFromId(id); err != nil { @@ -419,8 +415,7 @@ func (a *App) CreateGroupChannel(userIds []string, creatorId string) (*model.Cha message.Add("teammate_ids", model.ArrayToJson(userIds)) a.Publish(message) - esInterface := a.Elasticsearch - if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing { + if a.IsESIndexingEnabled() { a.Srv.Go(func() { for _, id := range userIds { if err := a.indexUserFromId(id); err != nil { @@ -517,10 +512,9 @@ func (a *App) UpdateChannel(channel *model.Channel) (*model.Channel, *model.AppE messageWs.Add("channel", channel.ToJson()) a.Publish(messageWs) - esInterface := a.Elasticsearch - if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing && channel.Type == "O" { + if a.IsESIndexingEnabled() && channel.Type == model.CHANNEL_OPEN { a.Srv.Go(func() { - if err := esInterface.IndexChannel(channel); err != nil { + if err := a.Elasticsearch.IndexChannel(channel); err != nil { mlog.Error("Encountered error indexing channel", mlog.String("channel_id", channel.Id), mlog.Err(err)) } }) @@ -1003,8 +997,7 @@ func (a *App) AddChannelMember(userId string, channel *model.Channel, userReques }) } - esInterface := a.Elasticsearch - if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing { + if a.IsESIndexingEnabled() { a.Srv.Go(func() { if err := a.indexUser(user); err != nil { mlog.Error("Encountered error indexing user", mlog.String("user_id", user.Id), mlog.Err(err)) @@ -1413,8 +1406,7 @@ func (a *App) JoinChannel(channel *model.Channel, userId string) *model.AppError }) } - esInterface := a.Elasticsearch - if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing { + if a.IsESIndexingEnabled() { a.Srv.Go(func() { if err := a.indexUser(user); err != nil { mlog.Error("Encountered error indexing user", mlog.String("user_id", user.Id), mlog.Err(err)) @@ -1649,8 +1641,7 @@ func (a *App) removeUserFromChannel(userIdToRemove string, removerUserId string, }) } - esInterface := a.Elasticsearch - if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing { + if a.IsESIndexingEnabled() { a.Srv.Go(func() { if err := a.indexUserFromId(userIdToRemove); err != nil { mlog.Error("Encountered error indexing user", mlog.String("user_id", userIdToRemove), mlog.Err(err)) @@ -1745,36 +1736,50 @@ func (a *App) UpdateChannelLastViewedAt(channelIds []string, userId string) *mod return nil } -func (a *App) AutocompleteChannels(teamId string, term string) (*model.ChannelList, *model.AppError) { - includeDeleted := *a.Config().TeamSettings.ExperimentalViewArchivedChannels - term = strings.TrimSpace(term) +func (a *App) esAutocompleteChannels(teamId, term string, includeDeleted bool) (*model.ChannelList, *model.AppError) { + channelIds, err := a.Elasticsearch.SearchChannels(teamId, term) + if err != nil { + return nil, err + } - esInterface := a.Elasticsearch - license := a.License() - if esInterface != nil && *a.Config().ElasticsearchSettings.EnableAutocomplete && license != nil && *license.Features.Elasticsearch { - channelIds, err := a.Elasticsearch.SearchChannels(teamId, term) + channelList := model.ChannelList{} + if len(channelIds) > 0 { + channels, err := a.Srv.Store.Channel().GetChannelsByIds(channelIds) if err != nil { return nil, err } - - channelList := model.ChannelList{} - if len(channelIds) > 0 { - channels, err := a.Srv.Store.Channel().GetChannelsByIds(channelIds) - if err != nil { - return nil, err - } - for _, c := range channels { - if c.DeleteAt > 0 && !includeDeleted { - continue - } - channelList = append(channelList, c) + for _, c := range channels { + if c.DeleteAt > 0 && !includeDeleted { + continue } + channelList = append(channelList, c) } - - return &channelList, nil } - return a.Srv.Store.Channel().AutocompleteInTeam(teamId, term, includeDeleted) + return &channelList, nil +} + +func (a *App) AutocompleteChannels(teamId string, term string) (*model.ChannelList, *model.AppError) { + includeDeleted := *a.Config().TeamSettings.ExperimentalViewArchivedChannels + var channelList *model.ChannelList + var err *model.AppError + term = strings.TrimSpace(term) + + if a.IsESAutocompletionEnabled() { + channelList, err = a.esAutocompleteChannels(teamId, term, includeDeleted) + if err != nil { + mlog.Error("Encountered error on AutocompleteChannels through Elasticsearch. Falling back to default autocompletion.", mlog.Err(err)) + } + } + + if !a.IsESAutocompletionEnabled() || err != nil { + channelList, err = a.Srv.Store.Channel().AutocompleteInTeam(teamId, term, includeDeleted) + if err != nil { + return nil, err + } + } + + return channelList, nil } func (a *App) AutocompleteChannelsForSearch(teamId string, userId string, term string) (*model.ChannelList, *model.AppError) { @@ -1927,8 +1932,7 @@ func (a *App) PermanentDeleteChannel(channel *model.Channel) *model.AppError { return result.Err } - esInterface := a.Elasticsearch - if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing { + if a.IsESIndexingEnabled() { a.Srv.Go(func() { for _, user := range channelUsers.Data.(map[string]*model.User) { if err := a.indexUser(user); err != nil { @@ -1936,9 +1940,9 @@ func (a *App) PermanentDeleteChannel(channel *model.Channel) *model.AppError { } } }) - if channel.Type == "O" { + if channel.Type == model.CHANNEL_OPEN { a.Srv.Go(func() { - if err := esInterface.DeleteChannel(channel); err != nil { + if err := a.Elasticsearch.DeleteChannel(channel); err != nil { mlog.Error("Encountered error deleting channel", mlog.String("channel_id", channel.Id), mlog.Err(err)) } }) diff --git a/app/config.go b/app/config.go index 106afc163d..cebb40d654 100644 --- a/app/config.go +++ b/app/config.go @@ -399,3 +399,19 @@ func (a *App) SaveConfig(newCfg *model.Config, sendConfigChangeClusterMessage bo return nil } + +func (a *App) IsESIndexingEnabled() bool { + return a.Elasticsearch != nil && *a.Config().ElasticsearchSettings.EnableIndexing +} + +func (a *App) IsESSearchEnabled() bool { + esInterface := a.Elasticsearch + license := a.License() + return esInterface != nil && *a.Config().ElasticsearchSettings.EnableSearching && license != nil && *license.Features.Elasticsearch +} + +func (a *App) IsESAutocompletionEnabled() bool { + esInterface := a.Elasticsearch + license := a.License() + return esInterface != nil && *a.Config().ElasticsearchSettings.EnableAutocomplete && license != nil && *license.Features.Elasticsearch +} diff --git a/app/post.go b/app/post.go index b4d65491c3..90f851c7fb 100644 --- a/app/post.go +++ b/app/post.go @@ -274,10 +274,9 @@ func (a *App) CreatePost(post *model.Post, channel *model.Channel, triggerWebhoo }) } - esInterface := a.Elasticsearch - if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing { + if a.IsESIndexingEnabled() { a.Srv.Go(func() { - if err = esInterface.IndexPost(rpost, channel.TeamId); err != nil { + if err = a.Elasticsearch.IndexPost(rpost, channel.TeamId); err != nil { mlog.Error("Encountered error indexing post", mlog.String("post_id", post.Id), mlog.Err(err)) } }) @@ -551,15 +550,14 @@ func (a *App) UpdatePost(post *model.Post, safeUpdate bool) (*model.Post, *model }) } - esInterface := a.Elasticsearch - if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing { + if a.IsESIndexingEnabled() { a.Srv.Go(func() { rchannel := <-a.Srv.Store.Channel().GetForPost(rpost.Id) if rchannel.Err != nil { mlog.Error(fmt.Sprintf("Couldn't get channel %v for post %v for Elasticsearch indexing.", rpost.ChannelId, rpost.Id)) return } - if err := esInterface.IndexPost(rpost, rchannel.Data.(*model.Channel).TeamId); err != nil { + if err := a.Elasticsearch.IndexPost(rpost, rchannel.Data.(*model.Channel).TeamId); err != nil { mlog.Error("Encountered error indexing post", mlog.String("post_id", post.Id), mlog.Err(err)) } }) @@ -708,10 +706,9 @@ func (a *App) DeletePost(postId, deleteByID string) (*model.Post, *model.AppErro a.DeleteFlaggedPosts(post.Id) }) - esInterface := a.Elasticsearch - if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing { + if a.IsESIndexingEnabled() { a.Srv.Go(func() { - if err := esInterface.DeletePost(post); err != nil { + if err := a.Elasticsearch.DeletePost(post); err != nil { mlog.Error("Encountered error deleting post", mlog.String("post_id", post.Id), mlog.Err(err)) } }) @@ -811,75 +808,82 @@ func (a *App) SearchPostsInTeam(teamId string, paramsList []*model.SearchParams) }) } -func (a *App) SearchPostsInTeamForUser(terms string, userId string, teamId string, isOrSearch bool, includeDeletedChannels bool, timeZoneOffset int, page, perPage int) (*model.PostSearchResults, *model.AppError) { - paramsList := model.ParseSearchParams(strings.TrimSpace(terms), timeZoneOffset) +func (a *App) esSearchPostsInTeamForUser(paramsList []*model.SearchParams, userId, teamId string, isOrSearch, includeDeletedChannels bool, page, perPage int) (*model.PostSearchResults, *model.AppError) { + finalParamsList := []*model.SearchParams{} includeDeleted := includeDeletedChannels && *a.Config().TeamSettings.ExperimentalViewArchivedChannels - esInterface := a.Elasticsearch - license := a.License() - if esInterface != nil && *a.Config().ElasticsearchSettings.EnableSearching && license != nil && *license.Features.Elasticsearch { - finalParamsList := []*model.SearchParams{} - - for _, params := range paramsList { - params.OrTerms = isOrSearch - // Don't allow users to search for "*" - if params.Terms != "*" { - // Convert channel names to channel IDs - for idx, channelName := range params.InChannels { - channel, err := a.parseAndFetchChannelIdByNameFromInFilter(channelName, userId, teamId, includeDeletedChannels) - if err != nil { - mlog.Error(fmt.Sprint(err)) - continue - } - params.InChannels[idx] = channel.Id + for _, params := range paramsList { + params.OrTerms = isOrSearch + // Don't allow users to search for "*" + if params.Terms != "*" { + // Convert channel names to channel IDs + for idx, channelName := range params.InChannels { + channel, err := a.parseAndFetchChannelIdByNameFromInFilter(channelName, userId, teamId, includeDeletedChannels) + if err != nil { + mlog.Error(fmt.Sprint(err)) + continue } - - // Convert usernames to user IDs - for idx, username := range params.FromUsers { - if user, err := a.GetUserByUsername(username); err != nil { - mlog.Error(fmt.Sprint(err)) - } else { - params.FromUsers[idx] = user.Id - } - } - - finalParamsList = append(finalParamsList, params) + params.InChannels[idx] = channel.Id } - } - // If the processed search params are empty, return empty search results. - if len(finalParamsList) == 0 { - return model.MakePostSearchResults(model.NewPostList(), nil), nil - } + // Convert usernames to user IDs + for idx, username := range params.FromUsers { + if user, err := a.GetUserByUsername(username); err != nil { + mlog.Error(fmt.Sprint(err)) + } else { + params.FromUsers[idx] = user.Id + } + } - // We only allow the user to search in channels they are a member of. - userChannels, err := a.GetChannelsForUser(teamId, userId, includeDeleted) - if err != nil { - mlog.Error(fmt.Sprint(err)) - return nil, err + finalParamsList = append(finalParamsList, params) } + } - postIds, matches, err := a.Elasticsearch.SearchPosts(userChannels, finalParamsList, page, perPage) + // If the processed search params are empty, return empty search results. + if len(finalParamsList) == 0 { + return model.MakePostSearchResults(model.NewPostList(), nil), nil + } + + // We only allow the user to search in channels they are a member of. + userChannels, err := a.GetChannelsForUser(teamId, userId, includeDeleted) + if err != nil { + mlog.Error(fmt.Sprint(err)) + return nil, err + } + + postIds, matches, err := a.Elasticsearch.SearchPosts(userChannels, finalParamsList, page, perPage) + if err != nil { + return nil, err + } + + // Get the posts + postList := model.NewPostList() + if len(postIds) > 0 { + posts, err := a.Srv.Store.Post().GetPostsByIds(postIds) if err != nil { return nil, err } - - // Get the posts - postList := model.NewPostList() - if len(postIds) > 0 { - posts, err := a.Srv.Store.Post().GetPostsByIds(postIds) - if err != nil { - return nil, err - } - for _, p := range posts { - if p.DeleteAt == 0 { - postList.AddPost(p) - postList.AddOrder(p.Id) - } + for _, p := range posts { + if p.DeleteAt == 0 { + postList.AddPost(p) + postList.AddOrder(p.Id) } } + } - return model.MakePostSearchResults(postList, matches), nil + return model.MakePostSearchResults(postList, matches), nil +} + +func (a *App) SearchPostsInTeamForUser(terms string, userId string, teamId string, isOrSearch bool, includeDeletedChannels bool, timeZoneOffset int, page, perPage int) (*model.PostSearchResults, *model.AppError) { + var postSearchResults *model.PostSearchResults + var err *model.AppError + paramsList := model.ParseSearchParams(strings.TrimSpace(terms), timeZoneOffset) + + if a.IsESSearchEnabled() { + postSearchResults, err = a.esSearchPostsInTeamForUser(paramsList, userId, teamId, isOrSearch, includeDeletedChannels, page, perPage) + if err != nil { + mlog.Error("Encountered error on SearchPostsInTeamForUser through Elasticsearch. Falling back to default search.", mlog.Err(err)) + } } if !*a.Config().ServiceSettings.EnablePostSearch { @@ -891,24 +895,30 @@ func (a *App) SearchPostsInTeamForUser(terms string, userId string, teamId strin return model.MakePostSearchResults(model.NewPostList(), nil), nil } - posts, err := a.searchPostsInTeam(teamId, userId, paramsList, func(params *model.SearchParams) { - params.IncludeDeletedChannels = includeDeleted - params.OrTerms = isOrSearch - for idx, channelName := range params.InChannels { - if strings.HasPrefix(channelName, "@") { - channel, err := a.parseAndFetchChannelIdByNameFromInFilter(channelName, userId, teamId, includeDeletedChannels) - if err != nil { - mlog.Error(fmt.Sprint(err)) - continue + if !a.IsESSearchEnabled() || err != nil { + includeDeleted := includeDeletedChannels && *a.Config().TeamSettings.ExperimentalViewArchivedChannels + posts, err := a.searchPostsInTeam(teamId, userId, paramsList, func(params *model.SearchParams) { + params.IncludeDeletedChannels = includeDeleted + params.OrTerms = isOrSearch + for idx, channelName := range params.InChannels { + if strings.HasPrefix(channelName, "@") { + channel, err := a.parseAndFetchChannelIdByNameFromInFilter(channelName, userId, teamId, includeDeletedChannels) + if err != nil { + mlog.Error(fmt.Sprint(err)) + continue + } + params.InChannels[idx] = channel.Name } - params.InChannels[idx] = channel.Name } + }) + if err != nil { + return nil, err } - }) - if err != nil { - return nil, err + + postSearchResults = model.MakePostSearchResults(posts, nil) } - return model.MakePostSearchResults(posts, nil), nil + + return postSearchResults, nil } func (a *App) GetFileInfosForPostWithMigration(postId string) ([]*model.FileInfo, *model.AppError) { diff --git a/app/user.go b/app/user.go index 4290c56ae2..b14e8d86ad 100644 --- a/app/user.go +++ b/app/user.go @@ -280,8 +280,7 @@ func (a *App) createUserOrGuest(user *model.User, guest bool) (*model.User, *mod }) } - esInterface := a.Elasticsearch - if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing { + if a.IsESIndexingEnabled() { a.Srv.Go(func() { if err := a.indexUser(user); err != nil { mlog.Error("Encountered error indexing user", mlog.String("user_id", user.Id), mlog.Err(err)) @@ -1160,8 +1159,7 @@ func (a *App) UpdateUser(user *model.User, sendNotifications bool) (*model.User, a.InvalidateCacheForUser(user.Id) - esInterface := a.Elasticsearch - if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing { + if a.IsESIndexingEnabled() { a.Srv.Go(func() { if err := a.indexUser(user); err != nil { mlog.Error("Encountered error indexing user", mlog.String("user_id", user.Id), mlog.Err(err)) @@ -1516,8 +1514,7 @@ func (a *App) PermanentDeleteUser(user *model.User) *model.AppError { mlog.Warn(fmt.Sprintf("Permanently deleted account %v id=%v", user.Email, user.Id), mlog.String("user_id", user.Id)) - esInterface := a.Elasticsearch - if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing { + if a.IsESIndexingEnabled() { a.Srv.Go(func() { if err := a.Elasticsearch.DeleteUser(user); err != nil { mlog.Error("Encountered error deleting user", mlog.String("user_id", user.Id), mlog.Err(err)) @@ -1709,35 +1706,25 @@ func (a *App) SearchUsersNotInChannel(teamId string, channelId string, term stri return users, nil } -func (a *App) SearchUsersInTeam(teamId string, term string, options *model.UserSearchOptions) ([]*model.User, *model.AppError) { - var result store.StoreResult - - term = strings.TrimSpace(term) - - esInterface := a.Elasticsearch - license := a.License() - if esInterface != nil && *a.Config().ElasticsearchSettings.EnableAutocomplete && license != nil && *license.Features.Elasticsearch { - listOfAllowedChannels, err := a.GetViewUsersRestrictionsForTeam(a.Session.UserId, teamId) - if err != nil { - return nil, err - } - if listOfAllowedChannels != nil && len(listOfAllowedChannels) == 0 { - return []*model.User{}, nil - } - - usersIds, err := a.Elasticsearch.SearchUsersInTeam(teamId, listOfAllowedChannels, term, options) - if err != nil { - return nil, err - } - - result = <-a.Srv.Store.User().GetProfileByIds(usersIds, false, nil) - } else { - result = <-a.Srv.Store.User().Search(teamId, term, options) +func (a *App) esSearchUsersInTeam(teamId, term string, options *model.UserSearchOptions) ([]*model.User, *model.AppError) { + listOfAllowedChannels, err := a.GetViewUsersRestrictionsForTeam(a.Session.UserId, teamId) + if err != nil { + return nil, err + } + if listOfAllowedChannels != nil && len(listOfAllowedChannels) == 0 { + return []*model.User{}, nil } + usersIds, err := a.Elasticsearch.SearchUsersInTeam(teamId, listOfAllowedChannels, term, options) + if err != nil { + return nil, err + } + + result := <-a.Srv.Store.User().GetProfileByIds(usersIds, false, nil) if result.Err != nil { return nil, result.Err } + users := result.Data.([]*model.User) for _, user := range users { @@ -1747,6 +1734,34 @@ func (a *App) SearchUsersInTeam(teamId string, term string, options *model.UserS return users, nil } +func (a *App) SearchUsersInTeam(teamId, term string, options *model.UserSearchOptions) ([]*model.User, *model.AppError) { + var users []*model.User + var err *model.AppError + term = strings.TrimSpace(term) + + if a.IsESAutocompletionEnabled() { + users, err = a.esSearchUsersInTeam(teamId, term, options) + if err != nil { + mlog.Error("Encountered error on SearchUsersInTeam through Elasticsearch. Falling back to default search.", mlog.Err(err)) + } + } + + if !a.IsESAutocompletionEnabled() || err != nil { + result := <-a.Srv.Store.User().Search(teamId, term, options) + if result.Err != nil { + return nil, result.Err + } + + users = result.Data.([]*model.User) + + for _, user := range users { + a.SanitizeProfile(user, options.IsAdmin) + } + } + + return users, nil +} + func (a *App) SearchUsersNotInTeam(notInTeamId string, term string, options *model.UserSearchOptions) ([]*model.User, *model.AppError) { term = strings.TrimSpace(term) result := <-a.Srv.Store.User().SearchNotInTeam(notInTeamId, term, options) @@ -1777,38 +1792,26 @@ func (a *App) SearchUsersWithoutTeam(term string, options *model.UserSearchOptio return users, nil } -func (a *App) AutocompleteUsersInChannel(teamId string, channelId string, term string, options *model.UserSearchOptions) (*model.UserAutocompleteInChannel, *model.AppError) { - var uchan, nuchan store.StoreChannel - - term = strings.TrimSpace(term) - - esInterface := a.Elasticsearch - license := a.License() - if esInterface != nil && *a.Config().ElasticsearchSettings.EnableAutocomplete && license != nil && *license.Features.Elasticsearch { - listOfAllowedChannels, err := a.getListOfAllowedChannelsForTeam(teamId, options.ViewRestrictions) - if err != nil { - return nil, err - } - if len(listOfAllowedChannels) == 0 { - return &model.UserAutocompleteInChannel{}, nil - } - uchanIds := []string{} - nuchanIds := []string{} - if !strings.Contains(strings.Join(listOfAllowedChannels, "."), channelId) { - nuchanIds, err = a.Elasticsearch.SearchUsersInTeam(teamId, listOfAllowedChannels, term, options) - } else { - uchanIds, nuchanIds, err = a.Elasticsearch.SearchUsersInChannel(teamId, channelId, listOfAllowedChannels, term, options) - } - if err != nil { - return nil, err - } - uchan = a.Srv.Store.User().GetProfileByIds(uchanIds, false, nil) - nuchan = a.Srv.Store.User().GetProfileByIds(nuchanIds, false, nil) - } else { - uchan = a.Srv.Store.User().SearchInChannel(channelId, term, options) - nuchan = a.Srv.Store.User().SearchNotInChannel(teamId, channelId, term, options) +func (a *App) esAutocompleteUsersInChannel(teamId, channelId, term string, options *model.UserSearchOptions) (*model.UserAutocompleteInChannel, *model.AppError) { + listOfAllowedChannels, err := a.getListOfAllowedChannelsForTeam(teamId, options.ViewRestrictions) + if err != nil { + return nil, err } - + if len(listOfAllowedChannels) == 0 { + return &model.UserAutocompleteInChannel{}, nil + } + uchanIds := []string{} + nuchanIds := []string{} + if !strings.Contains(strings.Join(listOfAllowedChannels, "."), channelId) { + nuchanIds, err = a.Elasticsearch.SearchUsersInTeam(teamId, listOfAllowedChannels, term, options) + } else { + uchanIds, nuchanIds, err = a.Elasticsearch.SearchUsersInChannel(teamId, channelId, listOfAllowedChannels, term, options) + } + if err != nil { + return nil, err + } + uchan := a.Srv.Store.User().GetProfileByIds(uchanIds, false, nil) + nuchan := a.Srv.Store.User().GetProfileByIds(nuchanIds, false, nil) autocomplete := &model.UserAutocompleteInChannel{} result := <-uchan @@ -1838,47 +1841,112 @@ func (a *App) AutocompleteUsersInChannel(teamId string, channelId string, term s return autocomplete, nil } -func (a *App) AutocompleteUsersInTeam(teamId string, term string, options *model.UserSearchOptions) (*model.UserAutocompleteInTeam, *model.AppError) { - autocomplete := &model.UserAutocompleteInTeam{} - var result store.StoreResult - +func (a *App) AutocompleteUsersInChannel(teamId string, channelId string, term string, options *model.UserSearchOptions) (*model.UserAutocompleteInChannel, *model.AppError) { + var autocomplete *model.UserAutocompleteInChannel + var err *model.AppError term = strings.TrimSpace(term) - esInterface := a.Elasticsearch - license := a.License() - if esInterface != nil && *a.Config().ElasticsearchSettings.EnableAutocomplete && license != nil && *license.Features.Elasticsearch { - listOfAllowedChannels, err := a.getListOfAllowedChannelsForTeam(teamId, options.ViewRestrictions) + if a.IsESAutocompletionEnabled() { + autocomplete, err = a.esAutocompleteUsersInChannel(teamId, channelId, term, options) if err != nil { - return nil, err + mlog.Error("Encountered error on AutocompleteUsersInChannel through Elasticsearch. Falling back to default autocompletion.", mlog.Err(err)) } - if len(listOfAllowedChannels) == 0 { - return &model.UserAutocompleteInTeam{}, nil - } - - usersIds, err := a.Elasticsearch.SearchUsersInTeam(teamId, listOfAllowedChannels, term, options) - if err != nil { - return nil, err - } - - result = <-a.Srv.Store.User().GetProfileByIds(usersIds, false, nil) - } else { - result = <-a.Srv.Store.User().Search(teamId, term, options) } + if !a.IsESAutocompletionEnabled() || err != nil { + autocomplete = &model.UserAutocompleteInChannel{} + uchan := a.Srv.Store.User().SearchInChannel(channelId, term, options) + nuchan := a.Srv.Store.User().SearchNotInChannel(teamId, channelId, term, options) + + result := <-uchan + if result.Err != nil { + return nil, result.Err + } + users := result.Data.([]*model.User) + + for _, user := range users { + a.SanitizeProfile(user, options.IsAdmin) + } + + autocomplete.InChannel = users + + result = <-nuchan + if result.Err != nil { + return nil, result.Err + } + users = result.Data.([]*model.User) + + for _, user := range users { + a.SanitizeProfile(user, options.IsAdmin) + } + + autocomplete.OutOfChannel = users + } + + return autocomplete, nil +} + +func (a *App) esAutocompleteUsersInTeam(teamId, term string, options *model.UserSearchOptions) (*model.UserAutocompleteInTeam, *model.AppError) { + listOfAllowedChannels, err := a.getListOfAllowedChannelsForTeam(teamId, options.ViewRestrictions) + if err != nil { + return nil, err + } + if len(listOfAllowedChannels) == 0 { + return &model.UserAutocompleteInTeam{}, nil + } + + usersIds, err := a.Elasticsearch.SearchUsersInTeam(teamId, listOfAllowedChannels, term, options) + if err != nil { + return nil, err + } + + result := <-a.Srv.Store.User().GetProfileByIds(usersIds, false, nil) if result.Err != nil { return nil, result.Err } - users := result.Data.([]*model.User) + users := result.Data.([]*model.User) for _, user := range users { a.SanitizeProfile(user, options.IsAdmin) } + autocomplete := &model.UserAutocompleteInTeam{} autocomplete.InTeam = users return autocomplete, nil } +func (a *App) AutocompleteUsersInTeam(teamId string, term string, options *model.UserSearchOptions) (*model.UserAutocompleteInTeam, *model.AppError) { + var autocomplete *model.UserAutocompleteInTeam + var err *model.AppError + + term = strings.TrimSpace(term) + + if a.IsESAutocompletionEnabled() { + autocomplete, err = a.esAutocompleteUsersInTeam(teamId, term, options) + if err != nil { + mlog.Error("Encountered error on AutocompleteUsersInTeam through Elasticsearch. Falling back to default autocompletion.", mlog.Err(err)) + } + } + + if !a.IsESAutocompletionEnabled() || err != nil { + autocomplete = &model.UserAutocompleteInTeam{} + result := <-a.Srv.Store.User().Search(teamId, term, options) + if result.Err != nil { + return nil, result.Err + } + + users := result.Data.([]*model.User) + for _, user := range users { + a.SanitizeProfile(user, options.IsAdmin) + } + + autocomplete.InTeam = users + } + + return autocomplete, nil +} + func (a *App) UpdateOAuthUserAttrs(userData io.Reader, user *model.User, provider einterfaces.OauthProvider, service string) *model.AppError { oauthUser := provider.GetUserFromJson(userData) if oauthUser == nil { @@ -1922,8 +1990,7 @@ func (a *App) UpdateOAuthUserAttrs(userData io.Reader, user *model.User, provide user = users.New a.InvalidateCacheForUser(user.Id) - esInterface := a.Elasticsearch - if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing { + if a.IsESIndexingEnabled() { a.Srv.Go(func() { if err := a.indexUser(user); err != nil { mlog.Error("Encountered error indexing user", mlog.String("user_id", user.Id), mlog.Err(err))