github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/store/searchlayer/post_layer.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package searchlayer 5 6 import ( 7 "errors" 8 "net/http" 9 10 "github.com/mattermost/mattermost-server/v5/mlog" 11 "github.com/mattermost/mattermost-server/v5/model" 12 "github.com/mattermost/mattermost-server/v5/services/searchengine" 13 "github.com/mattermost/mattermost-server/v5/store" 14 ) 15 16 type SearchPostStore struct { 17 store.PostStore 18 rootStore *SearchStore 19 } 20 21 func (s SearchPostStore) indexPost(post *model.Post) { 22 for _, engine := range s.rootStore.searchEngine.GetActiveEngines() { 23 if engine.IsIndexingEnabled() { 24 runIndexFn(engine, func(engineCopy searchengine.SearchEngineInterface) { 25 channel, chanErr := s.rootStore.Channel().Get(post.ChannelId, true) 26 if chanErr != nil { 27 mlog.Error("Couldn't get channel for post for SearchEngine indexing.", mlog.String("channel_id", post.ChannelId), mlog.String("search_engine", engineCopy.GetName()), mlog.String("post_id", post.Id), mlog.Err(chanErr)) 28 return 29 } 30 if err := engineCopy.IndexPost(post, channel.TeamId); err != nil { 31 mlog.Error("Encountered error indexing post", mlog.String("post_id", post.Id), mlog.String("search_engine", engineCopy.GetName()), mlog.Err(err)) 32 } 33 mlog.Debug("Indexed post in search engine", mlog.String("search_engine", engineCopy.GetName()), mlog.String("post_id", post.Id)) 34 }) 35 } 36 } 37 } 38 39 func (s SearchPostStore) deletePostIndex(post *model.Post) { 40 for _, engine := range s.rootStore.searchEngine.GetActiveEngines() { 41 if engine.IsIndexingEnabled() { 42 runIndexFn(engine, func(engineCopy searchengine.SearchEngineInterface) { 43 if err := engineCopy.DeletePost(post); err != nil { 44 mlog.Error("Encountered error deleting post", mlog.String("post_id", post.Id), mlog.String("search_engine", engineCopy.GetName()), mlog.Err(err)) 45 } 46 mlog.Debug("Removed post from the index in search engine", mlog.String("search_engine", engineCopy.GetName()), mlog.String("post_id", post.Id)) 47 }) 48 } 49 } 50 } 51 52 func (s SearchPostStore) deleteChannelPostsIndex(channelID string) { 53 for _, engine := range s.rootStore.searchEngine.GetActiveEngines() { 54 if engine.IsIndexingEnabled() { 55 runIndexFn(engine, func(engineCopy searchengine.SearchEngineInterface) { 56 if err := engineCopy.DeleteChannelPosts(channelID); err != nil { 57 mlog.Error("Encountered error deleting channel posts", mlog.String("channel_id", channelID), mlog.String("search_engine", engineCopy.GetName()), mlog.Err(err)) 58 } 59 mlog.Debug("Removed all channel posts from the index in search engine", mlog.String("channel_id", channelID), mlog.String("search_engine", engineCopy.GetName())) 60 }) 61 } 62 } 63 } 64 65 func (s SearchPostStore) deleteUserPostsIndex(userID string) { 66 for _, engine := range s.rootStore.searchEngine.GetActiveEngines() { 67 if engine.IsIndexingEnabled() { 68 runIndexFn(engine, func(engineCopy searchengine.SearchEngineInterface) { 69 if err := engineCopy.DeleteUserPosts(userID); err != nil { 70 mlog.Error("Encountered error deleting user posts", mlog.String("user_id", userID), mlog.String("search_engine", engineCopy.GetName()), mlog.Err(err)) 71 } 72 mlog.Debug("Removed all user posts from the index in search engine", mlog.String("user_id", userID), mlog.String("search_engine", engineCopy.GetName())) 73 }) 74 } 75 } 76 } 77 78 func (s SearchPostStore) Update(newPost, oldPost *model.Post) (*model.Post, *model.AppError) { 79 post, err := s.PostStore.Update(newPost, oldPost) 80 81 if err == nil { 82 s.indexPost(post) 83 } 84 return post, err 85 } 86 87 func (s *SearchPostStore) Overwrite(post *model.Post) (*model.Post, *model.AppError) { 88 post, err := s.PostStore.Overwrite(post) 89 if err == nil { 90 s.indexPost(post) 91 } 92 return post, err 93 } 94 95 func (s SearchPostStore) Save(post *model.Post) (*model.Post, *model.AppError) { 96 npost, err := s.PostStore.Save(post) 97 98 if err == nil { 99 s.indexPost(npost) 100 } 101 return npost, err 102 } 103 104 func (s SearchPostStore) Delete(postId string, date int64, deletedByID string) *model.AppError { 105 err := s.PostStore.Delete(postId, date, deletedByID) 106 107 if err == nil { 108 postList, err2 := s.PostStore.Get(postId, true) 109 if postList != nil && len(postList.Order) > 0 { 110 if err2 != nil { 111 s.deletePostIndex(postList.Posts[postList.Order[0]]) 112 } 113 } 114 } 115 return err 116 } 117 118 func (s SearchPostStore) PermanentDeleteByUser(userID string) *model.AppError { 119 err := s.PostStore.PermanentDeleteByUser(userID) 120 if err == nil { 121 s.deleteUserPostsIndex(userID) 122 } 123 return err 124 } 125 126 func (s SearchPostStore) PermanentDeleteByChannel(channelID string) *model.AppError { 127 err := s.PostStore.PermanentDeleteByChannel(channelID) 128 if err == nil { 129 s.deleteChannelPostsIndex(channelID) 130 } 131 return err 132 } 133 134 func (s SearchPostStore) searchPostsInTeamForUserByEngine(engine searchengine.SearchEngineInterface, paramsList []*model.SearchParams, userId, teamId string, isOrSearch, includeDeletedChannels bool, page, perPage int) (*model.PostSearchResults, *model.AppError) { 135 // We only allow the user to search in channels they are a member of. 136 userChannels, nErr := s.rootStore.Channel().GetChannels(teamId, userId, includeDeletedChannels, 0) 137 if nErr != nil { 138 mlog.Error("error getting channel for user", mlog.Err(nErr)) 139 var nfErr *store.ErrNotFound 140 switch { 141 // TODO: This error key would go away once this store method is migrated to return plain errors 142 case errors.As(nErr, &nfErr): 143 return nil, model.NewAppError("searchPostsInTeamForUserByEngine", "app.channel.get_channels.not_found.app_error", nil, nfErr.Error(), http.StatusNotFound) 144 default: 145 return nil, model.NewAppError("searchPostsInTeamForUserByEngine", "app.channel.get_channels.get.app_error", nil, nErr.Error(), http.StatusInternalServerError) 146 } 147 } 148 149 postIds, matches, err := engine.SearchPosts(userChannels, paramsList, page, perPage) 150 if err != nil { 151 return nil, err 152 } 153 154 // Get the posts 155 postList := model.NewPostList() 156 if len(postIds) > 0 { 157 posts, err := s.PostStore.GetPostsByIds(postIds) 158 if err != nil { 159 return nil, err 160 } 161 for _, p := range posts { 162 if p.DeleteAt == 0 { 163 postList.AddPost(p) 164 postList.AddOrder(p.Id) 165 } 166 } 167 } 168 169 return model.MakePostSearchResults(postList, matches), nil 170 } 171 172 func (s SearchPostStore) SearchPostsInTeamForUser(paramsList []*model.SearchParams, userId, teamId string, isOrSearch, includeDeletedChannels bool, page, perPage int) (*model.PostSearchResults, *model.AppError) { 173 for _, engine := range s.rootStore.searchEngine.GetActiveEngines() { 174 if engine.IsSearchEnabled() { 175 results, err := s.searchPostsInTeamForUserByEngine(engine, paramsList, userId, teamId, isOrSearch, includeDeletedChannels, page, perPage) 176 if err != nil { 177 mlog.Error("Encountered error on SearchPostsInTeamForUser.", mlog.String("search_engine", engine.GetName()), mlog.Err(err)) 178 continue 179 } 180 mlog.Debug("Using the first available search engine", mlog.String("search_engine", engine.GetName())) 181 return results, err 182 } 183 } 184 185 if *s.rootStore.config.SqlSettings.DisableDatabaseSearch { 186 mlog.Debug("Returning empty results for post SearchPostsInTeam as the database search is disabled") 187 return &model.PostSearchResults{PostList: model.NewPostList(), Matches: model.PostSearchMatches{}}, nil 188 } 189 190 mlog.Debug("Using database search because no other search engine is available") 191 return s.PostStore.SearchPostsInTeamForUser(paramsList, userId, teamId, isOrSearch, includeDeletedChannels, page, perPage) 192 }