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 }