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 }