github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/services/searchengine/bleveengine/search.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package bleveengine
     5  
     6  import (
     7  	"net/http"
     8  	"strings"
     9  
    10  	"github.com/mattermost/mattermost-server/v5/mlog"
    11  	"github.com/mattermost/mattermost-server/v5/model"
    12  
    13  	"github.com/blevesearch/bleve"
    14  	"github.com/blevesearch/bleve/search/query"
    15  )
    16  
    17  const DELETE_POSTS_BATCH_SIZE = 500
    18  
    19  func (b *BleveEngine) IndexPost(post *model.Post, teamId string) *model.AppError {
    20  	b.Mutex.RLock()
    21  	defer b.Mutex.RUnlock()
    22  
    23  	blvPost := BLVPostFromPost(post, teamId)
    24  	if err := b.PostIndex.Index(blvPost.Id, blvPost); err != nil {
    25  		return model.NewAppError("Bleveengine.IndexPost", "bleveengine.index_post.error", nil, err.Error(), http.StatusInternalServerError)
    26  	}
    27  	return nil
    28  }
    29  
    30  func (b *BleveEngine) SearchPosts(channels *model.ChannelList, searchParams []*model.SearchParams, page, perPage int) ([]string, model.PostSearchMatches, *model.AppError) {
    31  	channelQueries := []query.Query{}
    32  	for _, channel := range *channels {
    33  		channelIdQ := bleve.NewTermQuery(channel.Id)
    34  		channelIdQ.SetField("ChannelId")
    35  		channelQueries = append(channelQueries, channelIdQ)
    36  	}
    37  	channelDisjunctionQ := bleve.NewDisjunctionQuery(channelQueries...)
    38  
    39  	var termQueries []query.Query
    40  	var notTermQueries []query.Query
    41  	var filters []query.Query
    42  	var notFilters []query.Query
    43  
    44  	typeQ := bleve.NewTermQuery("")
    45  	typeQ.SetField("Type")
    46  	filters = append(filters, typeQ)
    47  
    48  	for i, params := range searchParams {
    49  		var termOperator query.MatchQueryOperator = query.MatchQueryOperatorAnd
    50  		if searchParams[0].OrTerms {
    51  			termOperator = query.MatchQueryOperatorOr
    52  		}
    53  
    54  		// Date, channels and FromUsers filters come in all
    55  		// searchParams iteration, and as they are global to the
    56  		// query, we only need to process them once
    57  		if i == 0 {
    58  			if len(params.InChannels) > 0 {
    59  				inChannels := []query.Query{}
    60  				for _, channelId := range params.InChannels {
    61  					channelQ := bleve.NewTermQuery(channelId)
    62  					channelQ.SetField("ChannelId")
    63  					inChannels = append(inChannels, channelQ)
    64  				}
    65  				filters = append(filters, bleve.NewDisjunctionQuery(inChannels...))
    66  			}
    67  
    68  			if len(params.ExcludedChannels) > 0 {
    69  				excludedChannels := []query.Query{}
    70  				for _, channelId := range params.ExcludedChannels {
    71  					channelQ := bleve.NewTermQuery(channelId)
    72  					channelQ.SetField("ChannelId")
    73  					excludedChannels = append(excludedChannels, channelQ)
    74  				}
    75  				notFilters = append(notFilters, bleve.NewDisjunctionQuery(excludedChannels...))
    76  			}
    77  
    78  			if len(params.FromUsers) > 0 {
    79  				fromUsers := []query.Query{}
    80  				for _, userId := range params.FromUsers {
    81  					userQ := bleve.NewTermQuery(userId)
    82  					userQ.SetField("UserId")
    83  					fromUsers = append(fromUsers, userQ)
    84  				}
    85  				filters = append(filters, bleve.NewDisjunctionQuery(fromUsers...))
    86  			}
    87  
    88  			if len(params.ExcludedUsers) > 0 {
    89  				excludedUsers := []query.Query{}
    90  				for _, userId := range params.ExcludedUsers {
    91  					userQ := bleve.NewTermQuery(userId)
    92  					userQ.SetField("UserId")
    93  					excludedUsers = append(excludedUsers, userQ)
    94  				}
    95  				notFilters = append(notFilters, bleve.NewDisjunctionQuery(excludedUsers...))
    96  			}
    97  
    98  			if params.OnDate != "" {
    99  				before, after := params.GetOnDateMillis()
   100  				beforeFloat64 := float64(before)
   101  				afterFloat64 := float64(after)
   102  				onDateQ := bleve.NewNumericRangeQuery(&beforeFloat64, &afterFloat64)
   103  				onDateQ.SetField("CreateAt")
   104  				filters = append(filters, onDateQ)
   105  			} else {
   106  				if params.AfterDate != "" || params.BeforeDate != "" {
   107  					var min, max *float64
   108  					if params.AfterDate != "" {
   109  						minf := float64(params.GetAfterDateMillis())
   110  						min = &minf
   111  					}
   112  
   113  					if params.BeforeDate != "" {
   114  						maxf := float64(params.GetBeforeDateMillis())
   115  						max = &maxf
   116  					}
   117  
   118  					dateQ := bleve.NewNumericRangeQuery(min, max)
   119  					dateQ.SetField("CreateAt")
   120  					filters = append(filters, dateQ)
   121  				}
   122  
   123  				if params.ExcludedAfterDate != "" {
   124  					minf := float64(params.GetExcludedAfterDateMillis())
   125  					dateQ := bleve.NewNumericRangeQuery(&minf, nil)
   126  					dateQ.SetField("CreateAt")
   127  					notFilters = append(notFilters, dateQ)
   128  				}
   129  
   130  				if params.ExcludedBeforeDate != "" {
   131  					maxf := float64(params.GetExcludedBeforeDateMillis())
   132  					dateQ := bleve.NewNumericRangeQuery(nil, &maxf)
   133  					dateQ.SetField("CreateAt")
   134  					notFilters = append(notFilters, dateQ)
   135  				}
   136  
   137  				if params.ExcludedDate != "" {
   138  					before, after := params.GetExcludedDateMillis()
   139  					beforef := float64(before)
   140  					afterf := float64(after)
   141  					onDateQ := bleve.NewNumericRangeQuery(&beforef, &afterf)
   142  					onDateQ.SetField("CreateAt")
   143  					notFilters = append(notFilters, onDateQ)
   144  				}
   145  			}
   146  		}
   147  
   148  		if params.IsHashtag {
   149  			if params.Terms != "" {
   150  				hashtagQ := bleve.NewMatchQuery(params.Terms)
   151  				hashtagQ.SetField("Hashtags")
   152  				hashtagQ.SetOperator(termOperator)
   153  				termQueries = append(termQueries, hashtagQ)
   154  			} else if params.ExcludedTerms != "" {
   155  				hashtagQ := bleve.NewMatchQuery(params.ExcludedTerms)
   156  				hashtagQ.SetField("Hashtags")
   157  				hashtagQ.SetOperator(termOperator)
   158  				notTermQueries = append(notTermQueries, hashtagQ)
   159  			}
   160  		} else {
   161  			if len(params.Terms) > 0 {
   162  				messageQ := bleve.NewMatchQuery(params.Terms)
   163  				messageQ.SetField("Message")
   164  				messageQ.SetOperator(termOperator)
   165  				termQueries = append(termQueries, messageQ)
   166  			}
   167  
   168  			if len(params.ExcludedTerms) > 0 {
   169  				messageQ := bleve.NewMatchQuery(params.ExcludedTerms)
   170  				messageQ.SetField("Message")
   171  				messageQ.SetOperator(termOperator)
   172  				notTermQueries = append(notTermQueries, messageQ)
   173  			}
   174  		}
   175  	}
   176  
   177  	allTermsQ := bleve.NewBooleanQuery()
   178  	allTermsQ.AddMustNot(notTermQueries...)
   179  	if searchParams[0].OrTerms {
   180  		allTermsQ.AddShould(termQueries...)
   181  	} else {
   182  		allTermsQ.AddMust(termQueries...)
   183  	}
   184  
   185  	query := bleve.NewBooleanQuery()
   186  	query.AddMust(channelDisjunctionQ)
   187  
   188  	if len(termQueries) > 0 || len(notTermQueries) > 0 {
   189  		query.AddMust(allTermsQ)
   190  	}
   191  
   192  	if len(filters) > 0 {
   193  		query.AddMust(bleve.NewConjunctionQuery(filters...))
   194  	}
   195  	if len(notFilters) > 0 {
   196  		query.AddMustNot(notFilters...)
   197  	}
   198  
   199  	search := bleve.NewSearchRequestOptions(query, perPage, page*perPage, false)
   200  	search.SortBy([]string{"-CreateAt"})
   201  	results, err := b.PostIndex.Search(search)
   202  	if err != nil {
   203  		return nil, nil, model.NewAppError("Bleveengine.SearchPosts", "bleveengine.search_posts.error", nil, err.Error(), http.StatusInternalServerError)
   204  	}
   205  
   206  	postIds := []string{}
   207  	matches := model.PostSearchMatches{}
   208  
   209  	for _, r := range results.Hits {
   210  		postIds = append(postIds, r.ID)
   211  	}
   212  
   213  	return postIds, matches, nil
   214  }
   215  
   216  func (b *BleveEngine) deletePosts(searchRequest *bleve.SearchRequest, batchSize int) (int64, error) {
   217  	resultsCount := int64(0)
   218  
   219  	for {
   220  		// As we are deleting the posts after fetching them, we need to keep
   221  		// From fixed always to 0
   222  		searchRequest.From = 0
   223  		searchRequest.Size = batchSize
   224  		results, err := b.PostIndex.Search(searchRequest)
   225  		if err != nil {
   226  			return -1, err
   227  		}
   228  		batch := b.PostIndex.NewBatch()
   229  		for _, post := range results.Hits {
   230  			batch.Delete(post.ID)
   231  		}
   232  		if err := b.PostIndex.Batch(batch); err != nil {
   233  			return -1, err
   234  		}
   235  		resultsCount += int64(results.Hits.Len())
   236  		if results.Hits.Len() < batchSize {
   237  			break
   238  		}
   239  	}
   240  
   241  	return resultsCount, nil
   242  }
   243  
   244  func (b *BleveEngine) DeleteChannelPosts(channelID string) *model.AppError {
   245  	b.Mutex.RLock()
   246  	defer b.Mutex.RUnlock()
   247  
   248  	query := bleve.NewTermQuery(channelID)
   249  	query.SetField("ChannelId")
   250  	search := bleve.NewSearchRequest(query)
   251  	deleted, err := b.deletePosts(search, DELETE_POSTS_BATCH_SIZE)
   252  	if err != nil {
   253  		return model.NewAppError("Bleveengine.DeleteChannelPosts",
   254  			"bleveengine.delete_channel_posts.error", nil,
   255  			err.Error(), http.StatusInternalServerError)
   256  	}
   257  
   258  	mlog.Info("Posts for channel deleted", mlog.String("channel_id", channelID), mlog.Int64("deleted", deleted))
   259  
   260  	return nil
   261  }
   262  
   263  func (b *BleveEngine) DeleteUserPosts(userID string) *model.AppError {
   264  	b.Mutex.RLock()
   265  	defer b.Mutex.RUnlock()
   266  
   267  	query := bleve.NewTermQuery(userID)
   268  	query.SetField("UserId")
   269  	search := bleve.NewSearchRequest(query)
   270  	deleted, err := b.deletePosts(search, DELETE_POSTS_BATCH_SIZE)
   271  	if err != nil {
   272  		return model.NewAppError("Bleveengine.DeleteUserPosts",
   273  			"bleveengine.delete_user_posts.error", nil,
   274  			err.Error(), http.StatusInternalServerError)
   275  	}
   276  
   277  	mlog.Info("Posts for user deleted", mlog.String("user_id", userID), mlog.Int64("deleted", deleted))
   278  
   279  	return nil
   280  }
   281  
   282  func (b *BleveEngine) DeletePost(post *model.Post) *model.AppError {
   283  	b.Mutex.RLock()
   284  	defer b.Mutex.RUnlock()
   285  
   286  	if err := b.PostIndex.Delete(post.Id); err != nil {
   287  		return model.NewAppError("Bleveengine.DeletePost", "bleveengine.delete_post.error", nil, err.Error(), http.StatusInternalServerError)
   288  	}
   289  	return nil
   290  }
   291  
   292  func (b *BleveEngine) IndexChannel(channel *model.Channel) *model.AppError {
   293  	b.Mutex.RLock()
   294  	defer b.Mutex.RUnlock()
   295  
   296  	blvChannel := BLVChannelFromChannel(channel)
   297  	if err := b.ChannelIndex.Index(blvChannel.Id, blvChannel); err != nil {
   298  		return model.NewAppError("Bleveengine.IndexChannel", "bleveengine.index_channel.error", nil, err.Error(), http.StatusInternalServerError)
   299  	}
   300  	return nil
   301  }
   302  
   303  func (b *BleveEngine) SearchChannels(teamId, term string) ([]string, *model.AppError) {
   304  	teamIdQ := bleve.NewTermQuery(teamId)
   305  	teamIdQ.SetField("TeamId")
   306  	queries := []query.Query{teamIdQ}
   307  
   308  	if term != "" {
   309  		nameSuggestQ := bleve.NewPrefixQuery(strings.ToLower(term))
   310  		nameSuggestQ.SetField("NameSuggest")
   311  		queries = append(queries, nameSuggestQ)
   312  	}
   313  
   314  	query := bleve.NewSearchRequest(bleve.NewConjunctionQuery(queries...))
   315  	query.Size = model.CHANNEL_SEARCH_DEFAULT_LIMIT
   316  	results, err := b.ChannelIndex.Search(query)
   317  	if err != nil {
   318  		return nil, model.NewAppError("Bleveengine.SearchChannels", "bleveengine.search_channels.error", nil, err.Error(), http.StatusInternalServerError)
   319  	}
   320  
   321  	channelIds := []string{}
   322  	for _, result := range results.Hits {
   323  		channelIds = append(channelIds, result.ID)
   324  	}
   325  
   326  	return channelIds, nil
   327  }
   328  
   329  func (b *BleveEngine) DeleteChannel(channel *model.Channel) *model.AppError {
   330  	b.Mutex.RLock()
   331  	defer b.Mutex.RUnlock()
   332  
   333  	if err := b.ChannelIndex.Delete(channel.Id); err != nil {
   334  		return model.NewAppError("Bleveengine.DeleteChannel", "bleveengine.delete_channel.error", nil, err.Error(), http.StatusInternalServerError)
   335  	}
   336  	return nil
   337  }
   338  
   339  func (b *BleveEngine) IndexUser(user *model.User, teamsIds, channelsIds []string) *model.AppError {
   340  	b.Mutex.RLock()
   341  	defer b.Mutex.RUnlock()
   342  
   343  	blvUser := BLVUserFromUserAndTeams(user, teamsIds, channelsIds)
   344  	if err := b.UserIndex.Index(blvUser.Id, blvUser); err != nil {
   345  		return model.NewAppError("Bleveengine.IndexUser", "bleveengine.index_user.error", nil, err.Error(), http.StatusInternalServerError)
   346  	}
   347  	return nil
   348  }
   349  
   350  func (b *BleveEngine) SearchUsersInChannel(teamId, channelId string, restrictedToChannels []string, term string, options *model.UserSearchOptions) ([]string, []string, *model.AppError) {
   351  	if restrictedToChannels != nil && len(restrictedToChannels) == 0 {
   352  		return []string{}, []string{}, nil
   353  	}
   354  
   355  	// users in channel
   356  	var queries []query.Query
   357  	if term != "" {
   358  		termQ := bleve.NewPrefixQuery(strings.ToLower(term))
   359  		if options.AllowFullNames {
   360  			termQ.SetField("SuggestionsWithFullname")
   361  		} else {
   362  			termQ.SetField("SuggestionsWithoutFullname")
   363  		}
   364  		queries = append(queries, termQ)
   365  	}
   366  
   367  	channelIdQ := bleve.NewTermQuery(channelId)
   368  	channelIdQ.SetField("ChannelsIds")
   369  	queries = append(queries, channelIdQ)
   370  
   371  	query := bleve.NewConjunctionQuery(queries...)
   372  
   373  	uchanSearch := bleve.NewSearchRequest(query)
   374  	uchanSearch.Size = options.Limit
   375  	uchan, err := b.UserIndex.Search(uchanSearch)
   376  	if err != nil {
   377  		return nil, nil, model.NewAppError("Bleveengine.SearchUsersInChannel", "bleveengine.search_users_in_channel.uchan.error", nil, err.Error(), http.StatusInternalServerError)
   378  	}
   379  
   380  	// users not in channel
   381  	boolQ := bleve.NewBooleanQuery()
   382  
   383  	if term != "" {
   384  		termQ := bleve.NewPrefixQuery(strings.ToLower(term))
   385  		if options.AllowFullNames {
   386  			termQ.SetField("SuggestionsWithFullname")
   387  		} else {
   388  			termQ.SetField("SuggestionsWithoutFullname")
   389  		}
   390  		boolQ.AddMust(termQ)
   391  	}
   392  
   393  	teamIdQ := bleve.NewTermQuery(teamId)
   394  	teamIdQ.SetField("TeamsIds")
   395  	boolQ.AddMust(teamIdQ)
   396  
   397  	outsideChannelIdQ := bleve.NewTermQuery(channelId)
   398  	outsideChannelIdQ.SetField("ChannelsIds")
   399  	boolQ.AddMustNot(outsideChannelIdQ)
   400  
   401  	if len(restrictedToChannels) > 0 {
   402  		restrictedChannelsQ := bleve.NewDisjunctionQuery()
   403  		for _, channelId := range restrictedToChannels {
   404  			restrictedChannelQ := bleve.NewTermQuery(channelId)
   405  			restrictedChannelsQ.AddQuery(restrictedChannelQ)
   406  		}
   407  		boolQ.AddMust(restrictedChannelsQ)
   408  	}
   409  
   410  	nuchanSearch := bleve.NewSearchRequest(boolQ)
   411  	nuchanSearch.Size = options.Limit
   412  	nuchan, err := b.UserIndex.Search(nuchanSearch)
   413  	if err != nil {
   414  		return nil, nil, model.NewAppError("Bleveengine.SearchUsersInChannel", "bleveengine.search_users_in_channel.nuchan.error", nil, err.Error(), http.StatusInternalServerError)
   415  	}
   416  
   417  	uchanIds := []string{}
   418  	for _, result := range uchan.Hits {
   419  		uchanIds = append(uchanIds, result.ID)
   420  	}
   421  
   422  	nuchanIds := []string{}
   423  	for _, result := range nuchan.Hits {
   424  		nuchanIds = append(nuchanIds, result.ID)
   425  	}
   426  
   427  	return uchanIds, nuchanIds, nil
   428  }
   429  
   430  func (b *BleveEngine) SearchUsersInTeam(teamId string, restrictedToChannels []string, term string, options *model.UserSearchOptions) ([]string, *model.AppError) {
   431  	if restrictedToChannels != nil && len(restrictedToChannels) == 0 {
   432  		return []string{}, nil
   433  	}
   434  
   435  	var rootQ query.Query
   436  	if term == "" && teamId == "" && restrictedToChannels == nil {
   437  		rootQ = bleve.NewMatchAllQuery()
   438  	} else {
   439  		boolQ := bleve.NewBooleanQuery()
   440  
   441  		if term != "" {
   442  			termQ := bleve.NewPrefixQuery(strings.ToLower(term))
   443  			if options.AllowFullNames {
   444  				termQ.SetField("SuggestionsWithFullname")
   445  			} else {
   446  				termQ.SetField("SuggestionsWithoutFullname")
   447  			}
   448  			boolQ.AddMust(termQ)
   449  		}
   450  
   451  		if len(restrictedToChannels) > 0 {
   452  			// restricted channels are already filtered by team, so we
   453  			// can search only those matches
   454  			restrictedChannelsQ := []query.Query{}
   455  			for _, channelId := range restrictedToChannels {
   456  				channelIdQ := bleve.NewTermQuery(channelId)
   457  				channelIdQ.SetField("ChannelsIds")
   458  				restrictedChannelsQ = append(restrictedChannelsQ, channelIdQ)
   459  			}
   460  			boolQ.AddMust(bleve.NewDisjunctionQuery(restrictedChannelsQ...))
   461  		} else {
   462  			// this means that we only need to restrict by team
   463  			if teamId != "" {
   464  				teamIdQ := bleve.NewTermQuery(teamId)
   465  				teamIdQ.SetField("TeamsIds")
   466  				boolQ.AddMust(teamIdQ)
   467  			}
   468  		}
   469  
   470  		rootQ = boolQ
   471  	}
   472  
   473  	search := bleve.NewSearchRequest(rootQ)
   474  	search.Size = options.Limit
   475  	results, err := b.UserIndex.Search(search)
   476  	if err != nil {
   477  		return nil, model.NewAppError("Bleveengine.SearchUsersInTeam", "bleveengine.search_users_in_team.error", nil, err.Error(), http.StatusInternalServerError)
   478  	}
   479  
   480  	usersIds := []string{}
   481  	for _, r := range results.Hits {
   482  		usersIds = append(usersIds, r.ID)
   483  	}
   484  
   485  	return usersIds, nil
   486  }
   487  
   488  func (b *BleveEngine) DeleteUser(user *model.User) *model.AppError {
   489  	b.Mutex.RLock()
   490  	defer b.Mutex.RUnlock()
   491  
   492  	if err := b.UserIndex.Delete(user.Id); err != nil {
   493  		return model.NewAppError("Bleveengine.DeleteUser", "bleveengine.delete_user.error", nil, err.Error(), http.StatusInternalServerError)
   494  	}
   495  	return nil
   496  }