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  }