Files
Agniva De Sarker afbd9d64c3 [AI assisted]: MM-62295: Search and index archived channels as well. (#29796)
We add a new field delete_at in the channels template.

This field is then searched in the SearchChannels function.

Also added tests to verify that archived channels are searched
properly, and also indexed correctly.

https://mattermost.atlassian.net/browse/MM-62295

```release-note
- Now archived channels are searchable with ES/OS if TeamSettings.ExperimentalViewArchivedChannels is enabled.
- If there are old channels which were archived before a bulk index was run, users would need to purge indexes, and do bulk index again. Because those old archived channels are removed from the index when a bulk index is run.
```


Co-authored-by: Mattermost Build <build@mattermost.com>
2025-01-28 19:44:55 +05:30

881 строка
27 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package bleveengine
import (
"net/http"
"strings"
"github.com/blevesearch/bleve/v2"
"github.com/blevesearch/bleve/v2/search/query"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/shared/mlog"
"github.com/mattermost/mattermost/server/public/shared/request"
)
const DeletePostsBatchSize = 500
const DeleteFilesBatchSize = 500
func (b *BleveEngine) IndexPost(post *model.Post, teamId string) *model.AppError {
b.Mutex.RLock()
defer b.Mutex.RUnlock()
blvPost := BLVPostFromPost(post, teamId)
if err := b.PostIndex.Index(blvPost.Id, blvPost); err != nil {
return model.NewAppError("Bleveengine.IndexPost", "bleveengine.index_post.error", nil, "", http.StatusInternalServerError).Wrap(err)
}
return nil
}
func (b *BleveEngine) SearchPosts(channels model.ChannelList, searchParams []*model.SearchParams, page, perPage int) ([]string, model.PostSearchMatches, *model.AppError) {
channelQueries := []query.Query{}
for _, channel := range channels {
channelIdQ := bleve.NewTermQuery(channel.Id)
channelIdQ.SetField("ChannelId")
channelQueries = append(channelQueries, channelIdQ)
}
channelDisjunctionQ := bleve.NewDisjunctionQuery(channelQueries...)
var termQueries []query.Query
var notTermQueries []query.Query
var filters []query.Query
var notFilters []query.Query
typeQ := bleve.NewTermQuery("")
typeQ.SetField("Type")
filters = append(filters, typeQ)
for i, params := range searchParams {
var termOperator query.MatchQueryOperator = query.MatchQueryOperatorAnd
if searchParams[0].OrTerms {
termOperator = query.MatchQueryOperatorOr
}
// Date, channels and FromUsers filters come in all
// searchParams iteration, and as they are global to the
// query, we only need to process them once
if i == 0 {
if len(params.InChannels) > 0 {
inChannels := []query.Query{}
for _, channelId := range params.InChannels {
channelQ := bleve.NewTermQuery(channelId)
channelQ.SetField("ChannelId")
inChannels = append(inChannels, channelQ)
}
filters = append(filters, bleve.NewDisjunctionQuery(inChannels...))
}
if len(params.ExcludedChannels) > 0 {
excludedChannels := []query.Query{}
for _, channelId := range params.ExcludedChannels {
channelQ := bleve.NewTermQuery(channelId)
channelQ.SetField("ChannelId")
excludedChannels = append(excludedChannels, channelQ)
}
notFilters = append(notFilters, bleve.NewDisjunctionQuery(excludedChannels...))
}
if len(params.FromUsers) > 0 {
fromUsers := []query.Query{}
for _, userId := range params.FromUsers {
userQ := bleve.NewTermQuery(userId)
userQ.SetField("UserId")
fromUsers = append(fromUsers, userQ)
}
filters = append(filters, bleve.NewDisjunctionQuery(fromUsers...))
}
if len(params.ExcludedUsers) > 0 {
excludedUsers := []query.Query{}
for _, userId := range params.ExcludedUsers {
userQ := bleve.NewTermQuery(userId)
userQ.SetField("UserId")
excludedUsers = append(excludedUsers, userQ)
}
notFilters = append(notFilters, bleve.NewDisjunctionQuery(excludedUsers...))
}
if params.OnDate != "" {
before, after := params.GetOnDateMillis()
beforeFloat64 := float64(before)
afterFloat64 := float64(after)
onDateQ := bleve.NewNumericRangeQuery(&beforeFloat64, &afterFloat64)
onDateQ.SetField("CreateAt")
filters = append(filters, onDateQ)
} else {
if params.AfterDate != "" || params.BeforeDate != "" {
var min, max *float64
if params.AfterDate != "" {
minf := float64(params.GetAfterDateMillis())
min = &minf
}
if params.BeforeDate != "" {
maxf := float64(params.GetBeforeDateMillis())
max = &maxf
}
dateQ := bleve.NewNumericRangeQuery(min, max)
dateQ.SetField("CreateAt")
filters = append(filters, dateQ)
}
if params.ExcludedAfterDate != "" {
minf := float64(params.GetExcludedAfterDateMillis())
dateQ := bleve.NewNumericRangeQuery(&minf, nil)
dateQ.SetField("CreateAt")
notFilters = append(notFilters, dateQ)
}
if params.ExcludedBeforeDate != "" {
maxf := float64(params.GetExcludedBeforeDateMillis())
dateQ := bleve.NewNumericRangeQuery(nil, &maxf)
dateQ.SetField("CreateAt")
notFilters = append(notFilters, dateQ)
}
if params.ExcludedDate != "" {
before, after := params.GetExcludedDateMillis()
beforef := float64(before)
afterf := float64(after)
onDateQ := bleve.NewNumericRangeQuery(&beforef, &afterf)
onDateQ.SetField("CreateAt")
notFilters = append(notFilters, onDateQ)
}
}
}
if params.IsHashtag {
if params.Terms != "" {
hashtagQ := bleve.NewMatchQuery(params.Terms)
hashtagQ.SetField("Hashtags")
hashtagQ.SetOperator(termOperator)
termQueries = append(termQueries, hashtagQ)
} else if params.ExcludedTerms != "" {
hashtagQ := bleve.NewMatchQuery(params.ExcludedTerms)
hashtagQ.SetField("Hashtags")
hashtagQ.SetOperator(termOperator)
notTermQueries = append(notTermQueries, hashtagQ)
}
} else {
if params.Terms != "" {
terms := []string{}
for _, term := range strings.Split(params.Terms, " ") {
if strings.HasSuffix(term, "*") {
messageQ := bleve.NewWildcardQuery(term)
messageQ.SetField("Message")
termQueries = append(termQueries, messageQ)
} else {
terms = append(terms, term)
}
}
if len(terms) > 0 {
messageQ := bleve.NewMatchQuery(strings.Join(terms, " "))
messageQ.SetField("Message")
messageQ.SetOperator(termOperator)
termQueries = append(termQueries, messageQ)
}
}
if params.ExcludedTerms != "" {
messageQ := bleve.NewMatchQuery(params.ExcludedTerms)
messageQ.SetField("Message")
messageQ.SetOperator(termOperator)
notTermQueries = append(notTermQueries, messageQ)
}
}
}
allTermsQ := bleve.NewBooleanQuery()
allTermsQ.AddMustNot(notTermQueries...)
if searchParams[0].OrTerms {
allTermsQ.AddShould(termQueries...)
} else {
allTermsQ.AddMust(termQueries...)
}
query := bleve.NewBooleanQuery()
query.AddMust(channelDisjunctionQ)
if len(termQueries) > 0 || len(notTermQueries) > 0 {
query.AddMust(allTermsQ)
}
if len(filters) > 0 {
query.AddMust(bleve.NewConjunctionQuery(filters...))
}
if len(notFilters) > 0 {
query.AddMustNot(notFilters...)
}
search := bleve.NewSearchRequestOptions(query, perPage, page*perPage, false)
search.SortBy([]string{"-CreateAt"})
results, err := b.PostIndex.Search(search)
if err != nil {
return nil, nil, model.NewAppError("Bleveengine.SearchPosts", "bleveengine.search_posts.error", nil, "", http.StatusInternalServerError).Wrap(err)
}
postIds := []string{}
matches := model.PostSearchMatches{}
for _, r := range results.Hits {
postIds = append(postIds, r.ID)
}
return postIds, matches, nil
}
func (b *BleveEngine) deletePosts(searchRequest *bleve.SearchRequest, batchSize int) (int64, error) {
resultsCount := int64(0)
for {
// As we are deleting the posts after fetching them, we need to keep
// From fixed always to 0
searchRequest.From = 0
searchRequest.Size = batchSize
results, err := b.PostIndex.Search(searchRequest)
if err != nil {
return -1, err
}
batch := b.PostIndex.NewBatch()
for _, post := range results.Hits {
batch.Delete(post.ID)
}
if err := b.PostIndex.Batch(batch); err != nil {
return -1, err
}
resultsCount += int64(results.Hits.Len())
if results.Hits.Len() < batchSize {
break
}
}
return resultsCount, nil
}
func (b *BleveEngine) DeleteChannelPosts(rctx request.CTX, channelID string) *model.AppError {
b.Mutex.RLock()
defer b.Mutex.RUnlock()
query := bleve.NewTermQuery(channelID)
query.SetField("ChannelId")
search := bleve.NewSearchRequest(query)
deleted, err := b.deletePosts(search, DeletePostsBatchSize)
if err != nil {
return model.NewAppError("Bleveengine.DeleteChannelPosts",
"bleveengine.delete_channel_posts.error", nil,
err.Error(), http.StatusInternalServerError)
}
rctx.Logger().Info("Posts for channel deleted", mlog.String("channel_id", channelID), mlog.Int("deleted", deleted))
return nil
}
func (b *BleveEngine) DeleteUserPosts(rctx request.CTX, userID string) *model.AppError {
b.Mutex.RLock()
defer b.Mutex.RUnlock()
query := bleve.NewTermQuery(userID)
query.SetField("UserId")
search := bleve.NewSearchRequest(query)
deleted, err := b.deletePosts(search, DeletePostsBatchSize)
if err != nil {
return model.NewAppError("Bleveengine.DeleteUserPosts",
"bleveengine.delete_user_posts.error", nil,
err.Error(), http.StatusInternalServerError)
}
rctx.Logger().Info("Posts for user deleted", mlog.String("user_id", userID), mlog.Int("deleted", deleted))
return nil
}
func (b *BleveEngine) DeletePost(post *model.Post) *model.AppError {
b.Mutex.RLock()
defer b.Mutex.RUnlock()
if err := b.PostIndex.Delete(post.Id); err != nil {
return model.NewAppError("Bleveengine.DeletePost", "bleveengine.delete_post.error", nil, "", http.StatusInternalServerError).Wrap(err)
}
return nil
}
func (b *BleveEngine) IndexChannel(_ request.CTX, channel *model.Channel, userIDs, teamMemberIDs []string) *model.AppError {
b.Mutex.RLock()
defer b.Mutex.RUnlock()
blvChannel := BLVChannelFromChannel(channel, userIDs, teamMemberIDs)
if err := b.ChannelIndex.Index(blvChannel.Id, blvChannel); err != nil {
return model.NewAppError("Bleveengine.IndexChannel", "bleveengine.index_channel.error", nil, "", http.StatusInternalServerError).Wrap(err)
}
return nil
}
func (b *BleveEngine) SearchChannels(teamId, userID, term string, isGuest, _ bool) ([]string, *model.AppError) {
// This query essentially boils down to (if teamID is passed):
// match teamID == <>
// AND
// match term == <>
// AND
// match (channelType != 'P' || (<> in userIDs && channelType == 'P'))
// (or if teamID is not passed)
// <> in teamMemberIds
// AND
// match term == <>
// AND
// match (channelType != 'P' || (<> in userIDs && channelType == 'P'))
// (or if isGuest is true)
// <> in teamMemberIds
// AND
// match term == <>
// AND
// match (<> in userIDs)
queries := []query.Query{}
if teamId != "" {
teamIdQ := bleve.NewTermQuery(teamId)
teamIdQ.SetField("TeamId")
queries = append(queries, teamIdQ)
} else {
teamMemberQ := bleve.NewTermQuery(userID)
teamMemberQ.SetField("TeamMemberIDs")
queries = append(queries, teamMemberQ)
}
if isGuest {
userQ := bleve.NewBooleanQuery()
userIDQ := bleve.NewTermQuery(userID)
userIDQ.SetField("UserIDs")
userQ.AddMust(userIDQ)
queries = append(queries, userIDQ)
} else {
boolNotPrivate := bleve.NewBooleanQuery()
privateQ := bleve.NewTermQuery(string(model.ChannelTypePrivate))
privateQ.SetField("Type")
boolNotPrivate.AddMustNot(privateQ)
userQ := bleve.NewBooleanQuery()
userIDQ := bleve.NewTermQuery(userID)
userIDQ.SetField("UserIDs")
userQ.AddMust(userIDQ)
userQ.AddMust(privateQ)
channelTypeQ := bleve.NewDisjunctionQuery()
channelTypeQ.AddQuery(boolNotPrivate)
channelTypeQ.AddQuery(userQ) // userID && 'p'
queries = append(queries, channelTypeQ)
}
if term != "" {
nameSuggestQ := bleve.NewPrefixQuery(strings.ToLower(term))
nameSuggestQ.SetField("NameSuggest")
queries = append(queries, nameSuggestQ)
}
query := bleve.NewSearchRequest(bleve.NewConjunctionQuery(queries...))
query.Size = model.ChannelSearchDefaultLimit
results, err := b.ChannelIndex.Search(query)
if err != nil {
return nil, model.NewAppError("Bleveengine.SearchChannels", "bleveengine.search_channels.error", nil, "", http.StatusInternalServerError).Wrap(err)
}
channelIds := []string{}
for _, result := range results.Hits {
channelIds = append(channelIds, result.ID)
}
return channelIds, nil
}
func (b *BleveEngine) DeleteChannel(channel *model.Channel) *model.AppError {
b.Mutex.RLock()
defer b.Mutex.RUnlock()
if err := b.ChannelIndex.Delete(channel.Id); err != nil {
return model.NewAppError("Bleveengine.DeleteChannel", "bleveengine.delete_channel.error", nil, "", http.StatusInternalServerError).Wrap(err)
}
return nil
}
func (b *BleveEngine) IndexUser(_ request.CTX, user *model.User, teamsIds, channelsIds []string) *model.AppError {
b.Mutex.RLock()
defer b.Mutex.RUnlock()
blvUser := BLVUserFromUserAndTeams(user, teamsIds, channelsIds)
if err := b.UserIndex.Index(blvUser.Id, blvUser); err != nil {
return model.NewAppError("Bleveengine.IndexUser", "bleveengine.index_user.error", nil, "", http.StatusInternalServerError).Wrap(err)
}
return nil
}
func (b *BleveEngine) SearchUsersInChannel(teamId, channelId string, restrictedToChannels []string, term string, options *model.UserSearchOptions) ([]string, []string, *model.AppError) {
if restrictedToChannels != nil && len(restrictedToChannels) == 0 {
return []string{}, []string{}, nil
}
// users in channel
var queries []query.Query
if term != "" {
termQ := bleve.NewPrefixQuery(strings.ToLower(term))
if options.AllowFullNames {
termQ.SetField("SuggestionsWithFullname")
} else {
termQ.SetField("SuggestionsWithoutFullname")
}
queries = append(queries, termQ)
}
channelIdQ := bleve.NewTermQuery(channelId)
channelIdQ.SetField("ChannelsIds")
queries = append(queries, channelIdQ)
query := bleve.NewConjunctionQuery(queries...)
uchanSearch := bleve.NewSearchRequest(query)
uchanSearch.Size = options.Limit
uchan, err := b.UserIndex.Search(uchanSearch)
if err != nil {
return nil, nil, model.NewAppError("Bleveengine.SearchUsersInChannel", "bleveengine.search_users_in_channel.uchan.error", nil, "", http.StatusInternalServerError).Wrap(err)
}
// users not in channel
boolQ := bleve.NewBooleanQuery()
if term != "" {
termQ := bleve.NewPrefixQuery(strings.ToLower(term))
if options.AllowFullNames {
termQ.SetField("SuggestionsWithFullname")
} else {
termQ.SetField("SuggestionsWithoutFullname")
}
boolQ.AddMust(termQ)
}
teamIdQ := bleve.NewTermQuery(teamId)
teamIdQ.SetField("TeamsIds")
boolQ.AddMust(teamIdQ)
outsideChannelIdQ := bleve.NewTermQuery(channelId)
outsideChannelIdQ.SetField("ChannelsIds")
boolQ.AddMustNot(outsideChannelIdQ)
if len(restrictedToChannels) > 0 {
restrictedChannelsQ := bleve.NewDisjunctionQuery()
for _, channelId := range restrictedToChannels {
restrictedChannelQ := bleve.NewTermQuery(channelId)
restrictedChannelsQ.AddQuery(restrictedChannelQ)
}
boolQ.AddMust(restrictedChannelsQ)
}
nuchanSearch := bleve.NewSearchRequest(boolQ)
nuchanSearch.Size = options.Limit
nuchan, err := b.UserIndex.Search(nuchanSearch)
if err != nil {
return nil, nil, model.NewAppError("Bleveengine.SearchUsersInChannel", "bleveengine.search_users_in_channel.nuchan.error", nil, "", http.StatusInternalServerError).Wrap(err)
}
uchanIds := []string{}
for _, result := range uchan.Hits {
uchanIds = append(uchanIds, result.ID)
}
nuchanIds := []string{}
for _, result := range nuchan.Hits {
nuchanIds = append(nuchanIds, result.ID)
}
return uchanIds, nuchanIds, nil
}
func (b *BleveEngine) SearchUsersInTeam(teamId string, restrictedToChannels []string, term string, options *model.UserSearchOptions) ([]string, *model.AppError) {
if restrictedToChannels != nil && len(restrictedToChannels) == 0 {
return []string{}, nil
}
var rootQ query.Query
if term == "" && teamId == "" && restrictedToChannels == nil {
rootQ = bleve.NewMatchAllQuery()
} else {
boolQ := bleve.NewBooleanQuery()
if term != "" {
termQ := bleve.NewPrefixQuery(strings.ToLower(term))
if options.AllowFullNames {
termQ.SetField("SuggestionsWithFullname")
} else {
termQ.SetField("SuggestionsWithoutFullname")
}
boolQ.AddMust(termQ)
}
if len(restrictedToChannels) > 0 {
// restricted channels are already filtered by team, so we
// can search only those matches
restrictedChannelsQ := []query.Query{}
for _, channelId := range restrictedToChannels {
channelIdQ := bleve.NewTermQuery(channelId)
channelIdQ.SetField("ChannelsIds")
restrictedChannelsQ = append(restrictedChannelsQ, channelIdQ)
}
boolQ.AddMust(bleve.NewDisjunctionQuery(restrictedChannelsQ...))
} else {
// this means that we only need to restrict by team
if teamId != "" {
teamIdQ := bleve.NewTermQuery(teamId)
teamIdQ.SetField("TeamsIds")
boolQ.AddMust(teamIdQ)
}
}
rootQ = boolQ
}
search := bleve.NewSearchRequest(rootQ)
search.Size = options.Limit
results, err := b.UserIndex.Search(search)
if err != nil {
return nil, model.NewAppError("Bleveengine.SearchUsersInTeam", "bleveengine.search_users_in_team.error", nil, "", http.StatusInternalServerError).Wrap(err)
}
usersIds := []string{}
for _, r := range results.Hits {
usersIds = append(usersIds, r.ID)
}
return usersIds, nil
}
func (b *BleveEngine) DeleteUser(user *model.User) *model.AppError {
b.Mutex.RLock()
defer b.Mutex.RUnlock()
if err := b.UserIndex.Delete(user.Id); err != nil {
return model.NewAppError("Bleveengine.DeleteUser", "bleveengine.delete_user.error", nil, "", http.StatusInternalServerError).Wrap(err)
}
return nil
}
func (b *BleveEngine) IndexFile(file *model.FileInfo, channelId string) *model.AppError {
b.Mutex.RLock()
defer b.Mutex.RUnlock()
blvFile := BLVFileFromFileInfo(file, channelId)
if err := b.FileIndex.Index(blvFile.Id, blvFile); err != nil {
return model.NewAppError("Bleveengine.IndexFile", "bleveengine.index_file.error", nil, "", http.StatusInternalServerError).Wrap(err)
}
return nil
}
func (b *BleveEngine) SearchFiles(channels model.ChannelList, searchParams []*model.SearchParams, page, perPage int) ([]string, *model.AppError) {
channelQueries := []query.Query{}
for _, channel := range channels {
channelIdQ := bleve.NewTermQuery(channel.Id)
channelIdQ.SetField("ChannelId")
channelQueries = append(channelQueries, channelIdQ)
}
channelDisjunctionQ := bleve.NewDisjunctionQuery(channelQueries...)
var termQueries []query.Query
var notTermQueries []query.Query
var filters []query.Query
var notFilters []query.Query
for i, params := range searchParams {
var termOperator query.MatchQueryOperator = query.MatchQueryOperatorAnd
if searchParams[0].OrTerms {
termOperator = query.MatchQueryOperatorOr
}
// Date, channels and FromUsers filters come in all
// searchParams iteration, and as they are global to the
// query, we only need to process them once
if i == 0 {
if len(params.InChannels) > 0 {
inChannels := []query.Query{}
for _, channelId := range params.InChannels {
channelQ := bleve.NewTermQuery(channelId)
channelQ.SetField("ChannelId")
inChannels = append(inChannels, channelQ)
}
filters = append(filters, bleve.NewDisjunctionQuery(inChannels...))
}
if len(params.ExcludedChannels) > 0 {
excludedChannels := []query.Query{}
for _, channelId := range params.ExcludedChannels {
channelQ := bleve.NewTermQuery(channelId)
channelQ.SetField("ChannelId")
excludedChannels = append(excludedChannels, channelQ)
}
notFilters = append(notFilters, bleve.NewDisjunctionQuery(excludedChannels...))
}
if len(params.FromUsers) > 0 {
fromUsers := []query.Query{}
for _, userId := range params.FromUsers {
userQ := bleve.NewTermQuery(userId)
userQ.SetField("CreatorId")
fromUsers = append(fromUsers, userQ)
}
filters = append(filters, bleve.NewDisjunctionQuery(fromUsers...))
}
if len(params.ExcludedUsers) > 0 {
excludedUsers := []query.Query{}
for _, userId := range params.ExcludedUsers {
userQ := bleve.NewTermQuery(userId)
userQ.SetField("CreatorId")
excludedUsers = append(excludedUsers, userQ)
}
notFilters = append(notFilters, bleve.NewDisjunctionQuery(excludedUsers...))
}
if len(params.Extensions) > 0 {
extensions := []query.Query{}
for _, extension := range params.Extensions {
extensionQ := bleve.NewTermQuery(extension)
extensionQ.SetField("Extension")
extensions = append(extensions, extensionQ)
}
filters = append(filters, bleve.NewDisjunctionQuery(extensions...))
}
if len(params.ExcludedExtensions) > 0 {
excludedExtensions := []query.Query{}
for _, extension := range params.ExcludedExtensions {
extensionQ := bleve.NewTermQuery(extension)
extensionQ.SetField("Extension")
excludedExtensions = append(excludedExtensions, extensionQ)
}
notFilters = append(notFilters, bleve.NewDisjunctionQuery(excludedExtensions...))
}
if params.OnDate != "" {
before, after := params.GetOnDateMillis()
beforeFloat64 := float64(before)
afterFloat64 := float64(after)
onDateQ := bleve.NewNumericRangeQuery(&beforeFloat64, &afterFloat64)
onDateQ.SetField("CreateAt")
filters = append(filters, onDateQ)
} else {
if params.AfterDate != "" || params.BeforeDate != "" {
var min, max *float64
if params.AfterDate != "" {
minf := float64(params.GetAfterDateMillis())
min = &minf
}
if params.BeforeDate != "" {
maxf := float64(params.GetBeforeDateMillis())
max = &maxf
}
dateQ := bleve.NewNumericRangeQuery(min, max)
dateQ.SetField("CreateAt")
filters = append(filters, dateQ)
}
if params.ExcludedAfterDate != "" {
minf := float64(params.GetExcludedAfterDateMillis())
dateQ := bleve.NewNumericRangeQuery(&minf, nil)
dateQ.SetField("CreateAt")
notFilters = append(notFilters, dateQ)
}
if params.ExcludedBeforeDate != "" {
maxf := float64(params.GetExcludedBeforeDateMillis())
dateQ := bleve.NewNumericRangeQuery(nil, &maxf)
dateQ.SetField("CreateAt")
notFilters = append(notFilters, dateQ)
}
if params.ExcludedDate != "" {
before, after := params.GetExcludedDateMillis()
beforef := float64(before)
afterf := float64(after)
onDateQ := bleve.NewNumericRangeQuery(&beforef, &afterf)
onDateQ.SetField("CreateAt")
notFilters = append(notFilters, onDateQ)
}
}
}
if params.Terms != "" {
terms := []string{}
for _, term := range strings.Split(params.Terms, " ") {
if strings.HasSuffix(term, "*") {
nameQ := bleve.NewWildcardQuery(term)
nameQ.SetField("Name")
contentQ := bleve.NewWildcardQuery(term)
contentQ.SetField("Content")
termQueries = append(termQueries, bleve.NewDisjunctionQuery(nameQ, contentQ))
} else {
terms = append(terms, term)
}
}
if len(terms) > 0 {
nameQ := bleve.NewMatchQuery(strings.Join(terms, " "))
nameQ.SetField("Name")
nameQ.SetOperator(termOperator)
contentQ := bleve.NewMatchQuery(strings.Join(terms, " "))
contentQ.SetField("Content")
contentQ.SetOperator(termOperator)
termQueries = append(termQueries, bleve.NewDisjunctionQuery(nameQ, contentQ))
}
}
if params.ExcludedTerms != "" {
nameQ := bleve.NewMatchQuery(params.ExcludedTerms)
nameQ.SetField("Name")
nameQ.SetOperator(termOperator)
contentQ := bleve.NewMatchQuery(params.ExcludedTerms)
contentQ.SetField("Content")
contentQ.SetOperator(termOperator)
notTermQueries = append(notTermQueries, bleve.NewDisjunctionQuery(nameQ, contentQ))
}
}
allTermsQ := bleve.NewBooleanQuery()
allTermsQ.AddMustNot(notTermQueries...)
if searchParams[0].OrTerms {
allTermsQ.AddShould(termQueries...)
} else {
allTermsQ.AddMust(termQueries...)
}
query := bleve.NewBooleanQuery()
query.AddMust(channelDisjunctionQ)
if len(termQueries) > 0 || len(notTermQueries) > 0 {
query.AddMust(allTermsQ)
}
if len(filters) > 0 {
query.AddMust(bleve.NewConjunctionQuery(filters...))
}
if len(notFilters) > 0 {
query.AddMustNot(notFilters...)
}
search := bleve.NewSearchRequestOptions(query, perPage, page*perPage, false)
search.SortBy([]string{"-CreateAt"})
results, err := b.FileIndex.Search(search)
if err != nil {
return nil, model.NewAppError("Bleveengine.SearchFiles", "bleveengine.search_files.error", nil, "", http.StatusInternalServerError).Wrap(err)
}
fileIds := []string{}
for _, r := range results.Hits {
fileIds = append(fileIds, r.ID)
}
return fileIds, nil
}
func (b *BleveEngine) DeleteFile(fileID string) *model.AppError {
b.Mutex.RLock()
defer b.Mutex.RUnlock()
if err := b.FileIndex.Delete(fileID); err != nil {
return model.NewAppError("Bleveengine.DeleteFile", "bleveengine.delete_file.error", nil, "", http.StatusInternalServerError).Wrap(err)
}
return nil
}
func (b *BleveEngine) deleteFiles(searchRequest *bleve.SearchRequest, batchSize int) (int64, error) {
resultsCount := int64(0)
for {
// As we are deleting the files after fetching them, we need to keep
// From fixed always to 0
searchRequest.From = 0
searchRequest.Size = batchSize
results, err := b.FileIndex.Search(searchRequest)
if err != nil {
return -1, err
}
batch := b.FileIndex.NewBatch()
for _, file := range results.Hits {
batch.Delete(file.ID)
}
if err := b.FileIndex.Batch(batch); err != nil {
return -1, err
}
resultsCount += int64(results.Hits.Len())
if results.Hits.Len() < batchSize {
break
}
}
return resultsCount, nil
}
func (b *BleveEngine) DeleteUserFiles(rctx request.CTX, userID string) *model.AppError {
b.Mutex.RLock()
defer b.Mutex.RUnlock()
query := bleve.NewTermQuery(userID)
query.SetField("CreatorId")
search := bleve.NewSearchRequest(query)
deleted, err := b.deleteFiles(search, DeleteFilesBatchSize)
if err != nil {
return model.NewAppError("Bleveengine.DeleteUserFiles",
"bleveengine.delete_user_files.error", nil,
err.Error(), http.StatusInternalServerError)
}
rctx.Logger().Info("Files for user deleted", mlog.String("user_id", userID), mlog.Int("deleted", deleted))
return nil
}
func (b *BleveEngine) DeletePostFiles(rctx request.CTX, postID string) *model.AppError {
b.Mutex.RLock()
defer b.Mutex.RUnlock()
query := bleve.NewTermQuery(postID)
query.SetField("PostId")
search := bleve.NewSearchRequest(query)
deleted, err := b.deleteFiles(search, DeleteFilesBatchSize)
if err != nil {
return model.NewAppError("Bleveengine.DeletePostFiles",
"bleveengine.delete_post_files.error", nil,
err.Error(), http.StatusInternalServerError)
}
rctx.Logger().Info("Files for post deleted", mlog.String("post_id", postID), mlog.Int("deleted", deleted))
return nil
}
func (b *BleveEngine) DeleteFilesBatch(rctx request.CTX, endTime, limit int64) *model.AppError {
b.Mutex.RLock()
defer b.Mutex.RUnlock()
endTimeFloat := float64(endTime)
query := bleve.NewNumericRangeQuery(nil, &endTimeFloat)
query.SetField("CreateAt")
search := bleve.NewSearchRequestOptions(query, int(limit), 0, false)
search.SortBy([]string{"-CreateAt"})
deleted, err := b.deleteFiles(search, DeleteFilesBatchSize)
if err != nil {
return model.NewAppError("Bleveengine.DeleteFilesBatch",
"bleveengine.delete_files_batch.error", nil,
err.Error(), http.StatusInternalServerError)
}
rctx.Logger().Info("Files in batch deleted", mlog.Int("endTime", endTime), mlog.Int("limit", limit), mlog.Int("deleted", deleted))
return nil
}