github.com/adacta-ru/mattermost-server/v6@v6.0.0/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/adacta-ru/mattermost-server/v6/mlog"
    11  	"github.com/adacta-ru/mattermost-server/v6/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  				terms := []string{}
   163  				for _, term := range strings.Split(params.Terms, " ") {
   164  					if strings.HasSuffix(term, "*") {
   165  						messageQ := bleve.NewWildcardQuery(term)
   166  						messageQ.SetField("Message")
   167  						termQueries = append(termQueries, messageQ)
   168  					} else {
   169  						terms = append(terms, term)
   170  					}
   171  				}
   172  
   173  				if len(terms) > 0 {
   174  					messageQ := bleve.NewMatchQuery(strings.Join(terms, " "))
   175  					messageQ.SetField("Message")
   176  					messageQ.SetOperator(termOperator)
   177  					termQueries = append(termQueries, messageQ)
   178  				}
   179  			}
   180  
   181  			if len(params.ExcludedTerms) > 0 {
   182  				messageQ := bleve.NewMatchQuery(params.ExcludedTerms)
   183  				messageQ.SetField("Message")
   184  				messageQ.SetOperator(termOperator)
   185  				notTermQueries = append(notTermQueries, messageQ)
   186  			}
   187  		}
   188  	}
   189  
   190  	allTermsQ := bleve.NewBooleanQuery()
   191  	allTermsQ.AddMustNot(notTermQueries...)
   192  	if searchParams[0].OrTerms {
   193  		allTermsQ.AddShould(termQueries...)
   194  	} else {
   195  		allTermsQ.AddMust(termQueries...)
   196  	}
   197  
   198  	query := bleve.NewBooleanQuery()
   199  	query.AddMust(channelDisjunctionQ)
   200  
   201  	if len(termQueries) > 0 || len(notTermQueries) > 0 {
   202  		query.AddMust(allTermsQ)
   203  	}
   204  
   205  	if len(filters) > 0 {
   206  		query.AddMust(bleve.NewConjunctionQuery(filters...))
   207  	}
   208  	if len(notFilters) > 0 {
   209  		query.AddMustNot(notFilters...)
   210  	}
   211  
   212  	search := bleve.NewSearchRequestOptions(query, perPage, page*perPage, false)
   213  	search.SortBy([]string{"-CreateAt"})
   214  	results, err := b.PostIndex.Search(search)
   215  	if err != nil {
   216  		return nil, nil, model.NewAppError("Bleveengine.SearchPosts", "bleveengine.search_posts.error", nil, err.Error(), http.StatusInternalServerError)
   217  	}
   218  
   219  	postIds := []string{}
   220  	matches := model.PostSearchMatches{}
   221  
   222  	for _, r := range results.Hits {
   223  		postIds = append(postIds, r.ID)
   224  	}
   225  
   226  	return postIds, matches, nil
   227  }
   228  
   229  func (b *BleveEngine) deletePosts(searchRequest *bleve.SearchRequest, batchSize int) (int64, error) {
   230  	resultsCount := int64(0)
   231  
   232  	for {
   233  		// As we are deleting the posts after fetching them, we need to keep
   234  		// From fixed always to 0
   235  		searchRequest.From = 0
   236  		searchRequest.Size = batchSize
   237  		results, err := b.PostIndex.Search(searchRequest)
   238  		if err != nil {
   239  			return -1, err
   240  		}
   241  		batch := b.PostIndex.NewBatch()
   242  		for _, post := range results.Hits {
   243  			batch.Delete(post.ID)
   244  		}
   245  		if err := b.PostIndex.Batch(batch); err != nil {
   246  			return -1, err
   247  		}
   248  		resultsCount += int64(results.Hits.Len())
   249  		if results.Hits.Len() < batchSize {
   250  			break
   251  		}
   252  	}
   253  
   254  	return resultsCount, nil
   255  }
   256  
   257  func (b *BleveEngine) DeleteChannelPosts(channelID string) *model.AppError {
   258  	b.Mutex.RLock()
   259  	defer b.Mutex.RUnlock()
   260  
   261  	query := bleve.NewTermQuery(channelID)
   262  	query.SetField("ChannelId")
   263  	search := bleve.NewSearchRequest(query)
   264  	deleted, err := b.deletePosts(search, DELETE_POSTS_BATCH_SIZE)
   265  	if err != nil {
   266  		return model.NewAppError("Bleveengine.DeleteChannelPosts",
   267  			"bleveengine.delete_channel_posts.error", nil,
   268  			err.Error(), http.StatusInternalServerError)
   269  	}
   270  
   271  	mlog.Info("Posts for channel deleted", mlog.String("channel_id", channelID), mlog.Int64("deleted", deleted))
   272  
   273  	return nil
   274  }
   275  
   276  func (b *BleveEngine) DeleteUserPosts(userID string) *model.AppError {
   277  	b.Mutex.RLock()
   278  	defer b.Mutex.RUnlock()
   279  
   280  	query := bleve.NewTermQuery(userID)
   281  	query.SetField("UserId")
   282  	search := bleve.NewSearchRequest(query)
   283  	deleted, err := b.deletePosts(search, DELETE_POSTS_BATCH_SIZE)
   284  	if err != nil {
   285  		return model.NewAppError("Bleveengine.DeleteUserPosts",
   286  			"bleveengine.delete_user_posts.error", nil,
   287  			err.Error(), http.StatusInternalServerError)
   288  	}
   289  
   290  	mlog.Info("Posts for user deleted", mlog.String("user_id", userID), mlog.Int64("deleted", deleted))
   291  
   292  	return nil
   293  }
   294  
   295  func (b *BleveEngine) DeletePost(post *model.Post) *model.AppError {
   296  	b.Mutex.RLock()
   297  	defer b.Mutex.RUnlock()
   298  
   299  	if err := b.PostIndex.Delete(post.Id); err != nil {
   300  		return model.NewAppError("Bleveengine.DeletePost", "bleveengine.delete_post.error", nil, err.Error(), http.StatusInternalServerError)
   301  	}
   302  	return nil
   303  }
   304  
   305  func (b *BleveEngine) IndexChannel(channel *model.Channel) *model.AppError {
   306  	b.Mutex.RLock()
   307  	defer b.Mutex.RUnlock()
   308  
   309  	blvChannel := BLVChannelFromChannel(channel)
   310  	if err := b.ChannelIndex.Index(blvChannel.Id, blvChannel); err != nil {
   311  		return model.NewAppError("Bleveengine.IndexChannel", "bleveengine.index_channel.error", nil, err.Error(), http.StatusInternalServerError)
   312  	}
   313  	return nil
   314  }
   315  
   316  func (b *BleveEngine) SearchChannels(teamId, term string) ([]string, *model.AppError) {
   317  	teamIdQ := bleve.NewTermQuery(teamId)
   318  	teamIdQ.SetField("TeamId")
   319  	queries := []query.Query{teamIdQ}
   320  
   321  	if term != "" {
   322  		nameSuggestQ := bleve.NewPrefixQuery(strings.ToLower(term))
   323  		nameSuggestQ.SetField("NameSuggest")
   324  		queries = append(queries, nameSuggestQ)
   325  	}
   326  
   327  	query := bleve.NewSearchRequest(bleve.NewConjunctionQuery(queries...))
   328  	query.Size = model.CHANNEL_SEARCH_DEFAULT_LIMIT
   329  	results, err := b.ChannelIndex.Search(query)
   330  	if err != nil {
   331  		return nil, model.NewAppError("Bleveengine.SearchChannels", "bleveengine.search_channels.error", nil, err.Error(), http.StatusInternalServerError)
   332  	}
   333  
   334  	channelIds := []string{}
   335  	for _, result := range results.Hits {
   336  		channelIds = append(channelIds, result.ID)
   337  	}
   338  
   339  	return channelIds, nil
   340  }
   341  
   342  func (b *BleveEngine) DeleteChannel(channel *model.Channel) *model.AppError {
   343  	b.Mutex.RLock()
   344  	defer b.Mutex.RUnlock()
   345  
   346  	if err := b.ChannelIndex.Delete(channel.Id); err != nil {
   347  		return model.NewAppError("Bleveengine.DeleteChannel", "bleveengine.delete_channel.error", nil, err.Error(), http.StatusInternalServerError)
   348  	}
   349  	return nil
   350  }
   351  
   352  func (b *BleveEngine) IndexUser(user *model.User, teamsIds, channelsIds []string) *model.AppError {
   353  	b.Mutex.RLock()
   354  	defer b.Mutex.RUnlock()
   355  
   356  	blvUser := BLVUserFromUserAndTeams(user, teamsIds, channelsIds)
   357  	if err := b.UserIndex.Index(blvUser.Id, blvUser); err != nil {
   358  		return model.NewAppError("Bleveengine.IndexUser", "bleveengine.index_user.error", nil, err.Error(), http.StatusInternalServerError)
   359  	}
   360  	return nil
   361  }
   362  
   363  func (b *BleveEngine) SearchUsersInChannel(teamId, channelId string, restrictedToChannels []string, term string, options *model.UserSearchOptions) ([]string, []string, *model.AppError) {
   364  	if restrictedToChannels != nil && len(restrictedToChannels) == 0 {
   365  		return []string{}, []string{}, nil
   366  	}
   367  
   368  	// users in channel
   369  	var queries []query.Query
   370  	if term != "" {
   371  		termQ := bleve.NewPrefixQuery(strings.ToLower(term))
   372  		if options.AllowFullNames {
   373  			termQ.SetField("SuggestionsWithFullname")
   374  		} else {
   375  			termQ.SetField("SuggestionsWithoutFullname")
   376  		}
   377  		queries = append(queries, termQ)
   378  	}
   379  
   380  	channelIdQ := bleve.NewTermQuery(channelId)
   381  	channelIdQ.SetField("ChannelsIds")
   382  	queries = append(queries, channelIdQ)
   383  
   384  	query := bleve.NewConjunctionQuery(queries...)
   385  
   386  	uchanSearch := bleve.NewSearchRequest(query)
   387  	uchanSearch.Size = options.Limit
   388  	uchan, err := b.UserIndex.Search(uchanSearch)
   389  	if err != nil {
   390  		return nil, nil, model.NewAppError("Bleveengine.SearchUsersInChannel", "bleveengine.search_users_in_channel.uchan.error", nil, err.Error(), http.StatusInternalServerError)
   391  	}
   392  
   393  	// users not in channel
   394  	boolQ := bleve.NewBooleanQuery()
   395  
   396  	if term != "" {
   397  		termQ := bleve.NewPrefixQuery(strings.ToLower(term))
   398  		if options.AllowFullNames {
   399  			termQ.SetField("SuggestionsWithFullname")
   400  		} else {
   401  			termQ.SetField("SuggestionsWithoutFullname")
   402  		}
   403  		boolQ.AddMust(termQ)
   404  	}
   405  
   406  	teamIdQ := bleve.NewTermQuery(teamId)
   407  	teamIdQ.SetField("TeamsIds")
   408  	boolQ.AddMust(teamIdQ)
   409  
   410  	outsideChannelIdQ := bleve.NewTermQuery(channelId)
   411  	outsideChannelIdQ.SetField("ChannelsIds")
   412  	boolQ.AddMustNot(outsideChannelIdQ)
   413  
   414  	if len(restrictedToChannels) > 0 {
   415  		restrictedChannelsQ := bleve.NewDisjunctionQuery()
   416  		for _, channelId := range restrictedToChannels {
   417  			restrictedChannelQ := bleve.NewTermQuery(channelId)
   418  			restrictedChannelsQ.AddQuery(restrictedChannelQ)
   419  		}
   420  		boolQ.AddMust(restrictedChannelsQ)
   421  	}
   422  
   423  	nuchanSearch := bleve.NewSearchRequest(boolQ)
   424  	nuchanSearch.Size = options.Limit
   425  	nuchan, err := b.UserIndex.Search(nuchanSearch)
   426  	if err != nil {
   427  		return nil, nil, model.NewAppError("Bleveengine.SearchUsersInChannel", "bleveengine.search_users_in_channel.nuchan.error", nil, err.Error(), http.StatusInternalServerError)
   428  	}
   429  
   430  	uchanIds := []string{}
   431  	for _, result := range uchan.Hits {
   432  		uchanIds = append(uchanIds, result.ID)
   433  	}
   434  
   435  	nuchanIds := []string{}
   436  	for _, result := range nuchan.Hits {
   437  		nuchanIds = append(nuchanIds, result.ID)
   438  	}
   439  
   440  	return uchanIds, nuchanIds, nil
   441  }
   442  
   443  func (b *BleveEngine) SearchUsersInTeam(teamId string, restrictedToChannels []string, term string, options *model.UserSearchOptions) ([]string, *model.AppError) {
   444  	if restrictedToChannels != nil && len(restrictedToChannels) == 0 {
   445  		return []string{}, nil
   446  	}
   447  
   448  	var rootQ query.Query
   449  	if term == "" && teamId == "" && restrictedToChannels == nil {
   450  		rootQ = bleve.NewMatchAllQuery()
   451  	} else {
   452  		boolQ := bleve.NewBooleanQuery()
   453  
   454  		if term != "" {
   455  			termQ := bleve.NewPrefixQuery(strings.ToLower(term))
   456  			if options.AllowFullNames {
   457  				termQ.SetField("SuggestionsWithFullname")
   458  			} else {
   459  				termQ.SetField("SuggestionsWithoutFullname")
   460  			}
   461  			boolQ.AddMust(termQ)
   462  		}
   463  
   464  		if len(restrictedToChannels) > 0 {
   465  			// restricted channels are already filtered by team, so we
   466  			// can search only those matches
   467  			restrictedChannelsQ := []query.Query{}
   468  			for _, channelId := range restrictedToChannels {
   469  				channelIdQ := bleve.NewTermQuery(channelId)
   470  				channelIdQ.SetField("ChannelsIds")
   471  				restrictedChannelsQ = append(restrictedChannelsQ, channelIdQ)
   472  			}
   473  			boolQ.AddMust(bleve.NewDisjunctionQuery(restrictedChannelsQ...))
   474  		} else {
   475  			// this means that we only need to restrict by team
   476  			if teamId != "" {
   477  				teamIdQ := bleve.NewTermQuery(teamId)
   478  				teamIdQ.SetField("TeamsIds")
   479  				boolQ.AddMust(teamIdQ)
   480  			}
   481  		}
   482  
   483  		rootQ = boolQ
   484  	}
   485  
   486  	search := bleve.NewSearchRequest(rootQ)
   487  	search.Size = options.Limit
   488  	results, err := b.UserIndex.Search(search)
   489  	if err != nil {
   490  		return nil, model.NewAppError("Bleveengine.SearchUsersInTeam", "bleveengine.search_users_in_team.error", nil, err.Error(), http.StatusInternalServerError)
   491  	}
   492  
   493  	usersIds := []string{}
   494  	for _, r := range results.Hits {
   495  		usersIds = append(usersIds, r.ID)
   496  	}
   497  
   498  	return usersIds, nil
   499  }
   500  
   501  func (b *BleveEngine) DeleteUser(user *model.User) *model.AppError {
   502  	b.Mutex.RLock()
   503  	defer b.Mutex.RUnlock()
   504  
   505  	if err := b.UserIndex.Delete(user.Id); err != nil {
   506  		return model.NewAppError("Bleveengine.DeleteUser", "bleveengine.delete_user.error", nil, err.Error(), http.StatusInternalServerError)
   507  	}
   508  	return nil
   509  }