Mono repo -> Master (#22553)
Combines the following repositories into one: https://github.com/mattermost/mattermost-server https://github.com/mattermost/mattermost-webapp https://github.com/mattermost/focalboard https://github.com/mattermost/mattermost-plugin-playbooks
Этот коммит содержится в:
327
server/channels/store/searchlayer/channel_layer.go
Обычный файл
327
server/channels/store/searchlayer/channel_layer.go
Обычный файл
@@ -0,0 +1,327 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package searchlayer
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost-server/v6/server/channels/store"
|
||||
"github.com/mattermost/mattermost-server/v6/server/platform/services/searchengine"
|
||||
"github.com/mattermost/mattermost-server/v6/server/platform/shared/mlog"
|
||||
)
|
||||
|
||||
type SearchChannelStore struct {
|
||||
store.ChannelStore
|
||||
rootStore *SearchStore
|
||||
}
|
||||
|
||||
func (c *SearchChannelStore) deleteChannelIndex(channel *model.Channel) {
|
||||
if channel.Type == model.ChannelTypeOpen {
|
||||
for _, engine := range c.rootStore.searchEngine.GetActiveEngines() {
|
||||
if engine.IsIndexingEnabled() {
|
||||
runIndexFn(engine, func(engineCopy searchengine.SearchEngineInterface) {
|
||||
if err := engineCopy.DeleteChannel(channel); err != nil {
|
||||
mlog.Warn("Encountered error deleting channel", mlog.String("channel_id", channel.Id), mlog.String("search_engine", engineCopy.GetName()), mlog.Err(err))
|
||||
return
|
||||
}
|
||||
mlog.Debug("Removed channel from index in search engine", mlog.String("search_engine", engineCopy.GetName()), mlog.String("channel_id", channel.Id))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *SearchChannelStore) indexChannel(channel *model.Channel) {
|
||||
var userIDs, teamMemberIDs []string
|
||||
var err error
|
||||
if channel.Type == model.ChannelTypePrivate {
|
||||
userIDs, err = c.GetAllChannelMembersById(channel.Id)
|
||||
if err != nil {
|
||||
mlog.Warn("Encountered error while indexing channel", mlog.String("channel_id", channel.Id), mlog.Err(err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
teamMemberIDs, err = c.GetTeamMembersForChannel(channel.Id)
|
||||
if err != nil {
|
||||
mlog.Warn("Encountered error while indexing channel", mlog.String("channel_id", channel.Id), mlog.Err(err))
|
||||
return
|
||||
}
|
||||
|
||||
for _, engine := range c.rootStore.searchEngine.GetActiveEngines() {
|
||||
if engine.IsIndexingEnabled() {
|
||||
runIndexFn(engine, func(engineCopy searchengine.SearchEngineInterface) {
|
||||
if err := engineCopy.IndexChannel(channel, userIDs, teamMemberIDs); err != nil {
|
||||
mlog.Warn("Encountered error indexing channel", mlog.String("channel_id", channel.Id), mlog.String("search_engine", engineCopy.GetName()), mlog.Err(err))
|
||||
return
|
||||
}
|
||||
mlog.Debug("Indexed channel in search engine", mlog.String("search_engine", engineCopy.GetName()), mlog.String("channel_id", channel.Id))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *SearchChannelStore) Save(channel *model.Channel, maxChannels int64) (*model.Channel, error) {
|
||||
newChannel, err := c.ChannelStore.Save(channel, maxChannels)
|
||||
if err == nil {
|
||||
c.indexChannel(newChannel)
|
||||
}
|
||||
return newChannel, err
|
||||
}
|
||||
|
||||
func (c *SearchChannelStore) Update(channel *model.Channel) (*model.Channel, error) {
|
||||
updatedChannel, err := c.ChannelStore.Update(channel)
|
||||
if err == nil {
|
||||
c.indexChannel(updatedChannel)
|
||||
}
|
||||
return updatedChannel, err
|
||||
}
|
||||
|
||||
func (c *SearchChannelStore) UpdateMember(cm *model.ChannelMember) (*model.ChannelMember, error) {
|
||||
member, err := c.ChannelStore.UpdateMember(cm)
|
||||
if err == nil {
|
||||
c.rootStore.indexUserFromID(cm.UserId)
|
||||
channel, channelErr := c.ChannelStore.Get(member.ChannelId, true)
|
||||
if channelErr != nil {
|
||||
mlog.Warn("Encountered error indexing user in channel", mlog.String("channel_id", member.ChannelId), mlog.Err(channelErr))
|
||||
} else {
|
||||
c.indexChannel(channel)
|
||||
c.rootStore.indexUserFromID(channel.CreatorId)
|
||||
}
|
||||
}
|
||||
return member, err
|
||||
}
|
||||
|
||||
func (c *SearchChannelStore) SaveMember(cm *model.ChannelMember) (*model.ChannelMember, error) {
|
||||
member, err := c.ChannelStore.SaveMember(cm)
|
||||
if err == nil {
|
||||
c.rootStore.indexUserFromID(cm.UserId)
|
||||
channel, channelErr := c.ChannelStore.Get(member.ChannelId, true)
|
||||
if channelErr != nil {
|
||||
mlog.Warn("Encountered error indexing user in channel", mlog.String("channel_id", member.ChannelId), mlog.Err(channelErr))
|
||||
} else {
|
||||
c.indexChannel(channel)
|
||||
c.rootStore.indexUserFromID(channel.CreatorId)
|
||||
}
|
||||
}
|
||||
return member, err
|
||||
}
|
||||
|
||||
func (c *SearchChannelStore) RemoveMember(channelID, userIdToRemove string) error {
|
||||
err := c.ChannelStore.RemoveMember(channelID, userIdToRemove)
|
||||
if err == nil {
|
||||
c.rootStore.indexUserFromID(userIdToRemove)
|
||||
}
|
||||
|
||||
channel, err := c.ChannelStore.Get(channelID, true)
|
||||
if err == nil {
|
||||
c.indexChannel(channel)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *SearchChannelStore) RemoveMembers(channelID string, userIds []string) error {
|
||||
if err := c.ChannelStore.RemoveMembers(channelID, userIds); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
channel, err := c.ChannelStore.Get(channelID, true)
|
||||
if err == nil {
|
||||
c.indexChannel(channel)
|
||||
}
|
||||
|
||||
for _, uid := range userIds {
|
||||
c.rootStore.indexUserFromID(uid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *SearchChannelStore) CreateDirectChannel(user *model.User, otherUser *model.User, channelOptions ...model.ChannelOption) (*model.Channel, error) {
|
||||
channel, err := c.ChannelStore.CreateDirectChannel(user, otherUser, channelOptions...)
|
||||
if err == nil {
|
||||
c.rootStore.indexUserFromID(user.Id)
|
||||
c.rootStore.indexUserFromID(otherUser.Id)
|
||||
c.indexChannel(channel)
|
||||
}
|
||||
return channel, err
|
||||
}
|
||||
|
||||
func (c *SearchChannelStore) SaveDirectChannel(directchannel *model.Channel, member1 *model.ChannelMember, member2 *model.ChannelMember) (*model.Channel, error) {
|
||||
channel, err := c.ChannelStore.SaveDirectChannel(directchannel, member1, member2)
|
||||
if err == nil {
|
||||
c.rootStore.indexUserFromID(member1.UserId)
|
||||
c.rootStore.indexUserFromID(member2.UserId)
|
||||
c.indexChannel(channel)
|
||||
}
|
||||
return channel, err
|
||||
}
|
||||
|
||||
func (c *SearchChannelStore) Autocomplete(userID, term string, includeDeleted, isGuest bool) (model.ChannelListWithTeamData, error) {
|
||||
var channelList model.ChannelListWithTeamData
|
||||
var err error
|
||||
|
||||
allFailed := true
|
||||
for _, engine := range c.rootStore.searchEngine.GetActiveEngines() {
|
||||
if engine.IsAutocompletionEnabled() {
|
||||
channelList, err = c.searchAutocompleteChannelsAllTeams(engine, userID, term, includeDeleted, isGuest)
|
||||
if err != nil {
|
||||
mlog.Warn("Encountered error on AutocompleteChannels through SearchEngine. Falling back to default autocompletion.", mlog.String("search_engine", engine.GetName()), mlog.Err(err))
|
||||
continue
|
||||
}
|
||||
allFailed = false
|
||||
mlog.Debug("Using the first available search engine", mlog.String("search_engine", engine.GetName()))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if allFailed {
|
||||
mlog.Debug("Using database search because no other search engine is available")
|
||||
channelList, err = c.ChannelStore.Autocomplete(userID, term, includeDeleted, isGuest)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Failed to autocomplete channels in team")
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return channelList, err
|
||||
}
|
||||
|
||||
return channelList, nil
|
||||
}
|
||||
|
||||
func (c *SearchChannelStore) AutocompleteInTeam(teamID, userID, term string, includeDeleted, isGuest bool) (model.ChannelList, error) {
|
||||
var channelList model.ChannelList
|
||||
var err error
|
||||
|
||||
allFailed := true
|
||||
for _, engine := range c.rootStore.searchEngine.GetActiveEngines() {
|
||||
if engine.IsAutocompletionEnabled() {
|
||||
channelList, err = c.searchAutocompleteChannels(engine, teamID, userID, term, includeDeleted, isGuest)
|
||||
if err != nil {
|
||||
mlog.Warn("Encountered error on AutocompleteChannels through SearchEngine. Falling back to default autocompletion.", mlog.String("search_engine", engine.GetName()), mlog.Err(err))
|
||||
continue
|
||||
}
|
||||
allFailed = false
|
||||
mlog.Debug("Using the first available search engine", mlog.String("search_engine", engine.GetName()))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if allFailed {
|
||||
mlog.Debug("Using database search because no other search engine is available")
|
||||
channelList, err = c.ChannelStore.AutocompleteInTeam(teamID, userID, term, includeDeleted, isGuest)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Failed to autocomplete channels in team")
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return channelList, err
|
||||
}
|
||||
|
||||
return channelList, nil
|
||||
}
|
||||
|
||||
func (c *SearchChannelStore) searchAutocompleteChannels(engine searchengine.SearchEngineInterface, teamId, userID, term string, includeDeleted, isGuest bool) (model.ChannelList, error) {
|
||||
channelIds, err := engine.SearchChannels(teamId, userID, term, isGuest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
channelList := model.ChannelList{}
|
||||
var nErr error
|
||||
if len(channelIds) > 0 {
|
||||
channelList, nErr = c.ChannelStore.GetChannelsByIds(channelIds, includeDeleted)
|
||||
if nErr != nil {
|
||||
return nil, errors.Wrap(nErr, "Failed to get channels by ids")
|
||||
}
|
||||
}
|
||||
|
||||
return channelList, nil
|
||||
}
|
||||
|
||||
func (c *SearchChannelStore) searchAutocompleteChannelsAllTeams(engine searchengine.SearchEngineInterface, userID, term string, includeDeleted, isGuest bool) (model.ChannelListWithTeamData, error) {
|
||||
channelIds, err := engine.SearchChannels("", userID, term, isGuest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
channelList := model.ChannelListWithTeamData{}
|
||||
var nErr error
|
||||
if len(channelIds) > 0 {
|
||||
channelList, nErr = c.ChannelStore.GetChannelsWithTeamDataByIds(channelIds, includeDeleted)
|
||||
if nErr != nil {
|
||||
return nil, errors.Wrap(nErr, "Failed to get channels by ids")
|
||||
}
|
||||
}
|
||||
|
||||
return channelList, nil
|
||||
}
|
||||
|
||||
func (c *SearchChannelStore) PermanentDeleteMembersByUser(userId string) error {
|
||||
channels, errGetChannels := c.ChannelStore.GetChannelsByUser(userId, false, 0, -1, "")
|
||||
if errGetChannels != nil {
|
||||
mlog.Warn("Encountered error indexing channel after removing user", mlog.String("user_id", userId), mlog.Err(errGetChannels))
|
||||
}
|
||||
|
||||
err := c.ChannelStore.PermanentDeleteMembersByUser(userId)
|
||||
if err == nil {
|
||||
c.rootStore.indexUserFromID(userId)
|
||||
if errGetChannels == nil {
|
||||
for _, ch := range channels {
|
||||
c.indexChannel(ch)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *SearchChannelStore) RemoveAllDeactivatedMembers(channelId string) error {
|
||||
profiles, errProfiles := c.rootStore.User().GetAllProfilesInChannel(context.Background(), channelId, true)
|
||||
if errProfiles != nil {
|
||||
mlog.Warn("Encountered error indexing users for channel", mlog.String("channel_id", channelId), mlog.Err(errProfiles))
|
||||
}
|
||||
|
||||
err := c.ChannelStore.RemoveAllDeactivatedMembers(channelId)
|
||||
if err == nil && errProfiles == nil {
|
||||
for _, user := range profiles {
|
||||
if user.DeleteAt != 0 {
|
||||
c.rootStore.indexUser(user)
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *SearchChannelStore) PermanentDeleteMembersByChannel(channelId string) error {
|
||||
profiles, errProfiles := c.rootStore.User().GetAllProfilesInChannel(context.Background(), channelId, true)
|
||||
if errProfiles != nil {
|
||||
mlog.Warn("Encountered error indexing users for channel", mlog.String("channel_id", channelId), mlog.Err(errProfiles))
|
||||
}
|
||||
|
||||
err := c.ChannelStore.PermanentDeleteMembersByChannel(channelId)
|
||||
if err == nil && errProfiles == nil {
|
||||
for _, user := range profiles {
|
||||
c.rootStore.indexUser(user)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *SearchChannelStore) PermanentDelete(channelId string) error {
|
||||
channel, channelErr := c.ChannelStore.Get(channelId, true)
|
||||
if channelErr != nil {
|
||||
mlog.Warn("Encountered error deleting channel", mlog.String("channel_id", channelId), mlog.Err(channelErr))
|
||||
}
|
||||
err := c.ChannelStore.PermanentDelete(channelId)
|
||||
if err == nil && channelErr == nil {
|
||||
c.deleteChannelIndex(channel)
|
||||
}
|
||||
return err
|
||||
}
|
||||
195
server/channels/store/searchlayer/file_info_layer.go
Обычный файл
195
server/channels/store/searchlayer/file_info_layer.go
Обычный файл
@@ -0,0 +1,195 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package searchlayer
|
||||
|
||||
import (
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost-server/v6/server/channels/store"
|
||||
"github.com/mattermost/mattermost-server/v6/server/platform/services/searchengine"
|
||||
"github.com/mattermost/mattermost-server/v6/server/platform/shared/mlog"
|
||||
)
|
||||
|
||||
type SearchFileInfoStore struct {
|
||||
store.FileInfoStore
|
||||
rootStore *SearchStore
|
||||
}
|
||||
|
||||
func (s SearchFileInfoStore) indexFile(file *model.FileInfo) {
|
||||
for _, engine := range s.rootStore.searchEngine.GetActiveEngines() {
|
||||
if engine.IsIndexingEnabled() {
|
||||
runIndexFn(engine, func(engineCopy searchengine.SearchEngineInterface) {
|
||||
if file.PostId == "" {
|
||||
return
|
||||
}
|
||||
post, postErr := s.rootStore.Post().GetSingle(file.PostId, false)
|
||||
if postErr != nil {
|
||||
mlog.Error("Couldn't get post for file for SearchEngine indexing.", mlog.String("post_id", file.PostId), mlog.String("search_engine", engineCopy.GetName()), mlog.String("file_info_id", file.Id), mlog.Err(postErr))
|
||||
return
|
||||
}
|
||||
|
||||
if err := engineCopy.IndexFile(file, post.ChannelId); err != nil {
|
||||
mlog.Error("Encountered error indexing file", mlog.String("file_info_id", file.Id), mlog.String("search_engine", engineCopy.GetName()), mlog.Err(err))
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s SearchFileInfoStore) deleteFileIndex(fileID string) {
|
||||
for _, engine := range s.rootStore.searchEngine.GetActiveEngines() {
|
||||
if engine.IsIndexingEnabled() {
|
||||
runIndexFn(engine, func(engineCopy searchengine.SearchEngineInterface) {
|
||||
if err := engineCopy.DeleteFile(fileID); err != nil {
|
||||
mlog.Error("Encountered error deleting file", mlog.String("file_info_id", fileID), mlog.String("search_engine", engineCopy.GetName()), mlog.Err(err))
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s SearchFileInfoStore) deleteFileIndexForUser(userID string) {
|
||||
for _, engine := range s.rootStore.searchEngine.GetActiveEngines() {
|
||||
if engine.IsIndexingEnabled() {
|
||||
runIndexFn(engine, func(engineCopy searchengine.SearchEngineInterface) {
|
||||
if err := engineCopy.DeleteUserFiles(userID); err != nil {
|
||||
mlog.Error("Encountered error deleting files for user", mlog.String("user_id", userID), mlog.String("search_engine", engineCopy.GetName()), mlog.Err(err))
|
||||
return
|
||||
}
|
||||
mlog.Debug("Removed user's files from the index in search engine", mlog.String("search_engine", engineCopy.GetName()), mlog.String("user_id", userID))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s SearchFileInfoStore) deleteFileIndexForPost(postID string) {
|
||||
for _, engine := range s.rootStore.searchEngine.GetActiveEngines() {
|
||||
if engine.IsIndexingEnabled() {
|
||||
runIndexFn(engine, func(engineCopy searchengine.SearchEngineInterface) {
|
||||
if err := engineCopy.DeletePostFiles(postID); err != nil {
|
||||
mlog.Error("Encountered error deleting files for post", mlog.String("post_id", postID), mlog.String("search_engine", engineCopy.GetName()), mlog.Err(err))
|
||||
return
|
||||
}
|
||||
mlog.Debug("Removed post's files from the index in search engine", mlog.String("search_engine", engineCopy.GetName()), mlog.String("post_id", postID))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s SearchFileInfoStore) deleteFileIndexBatch(endTime, limit int64) {
|
||||
for _, engine := range s.rootStore.searchEngine.GetActiveEngines() {
|
||||
if engine.IsIndexingEnabled() {
|
||||
runIndexFn(engine, func(engineCopy searchengine.SearchEngineInterface) {
|
||||
if err := engineCopy.DeleteFilesBatch(endTime, limit); err != nil {
|
||||
mlog.Error("Encountered error deleting a batch of files", mlog.Int64("limit", limit), mlog.Int64("end_time", endTime), mlog.String("search_engine", engineCopy.GetName()), mlog.Err(err))
|
||||
return
|
||||
}
|
||||
mlog.Debug("Removed batch of files from the index in search engine", mlog.String("search_engine", engineCopy.GetName()), mlog.Int64("end_time", endTime), mlog.Int64("limit", limit))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s SearchFileInfoStore) Save(info *model.FileInfo) (*model.FileInfo, error) {
|
||||
nfile, err := s.FileInfoStore.Save(info)
|
||||
if err == nil {
|
||||
s.indexFile(nfile)
|
||||
}
|
||||
return nfile, err
|
||||
}
|
||||
|
||||
func (s SearchFileInfoStore) SetContent(fileID, content string) error {
|
||||
err := s.FileInfoStore.SetContent(fileID, content)
|
||||
if err == nil {
|
||||
nfile, err2 := s.FileInfoStore.GetFromMaster(fileID)
|
||||
if err2 == nil {
|
||||
nfile.Content = content
|
||||
s.indexFile(nfile)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s SearchFileInfoStore) AttachToPost(fileId, postId, creatorId string) error {
|
||||
err := s.FileInfoStore.AttachToPost(fileId, postId, creatorId)
|
||||
if err == nil {
|
||||
nFileInfo, err2 := s.FileInfoStore.GetFromMaster(fileId)
|
||||
if err2 == nil {
|
||||
s.indexFile(nFileInfo)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s SearchFileInfoStore) DeleteForPost(postId string) (string, error) {
|
||||
result, err := s.FileInfoStore.DeleteForPost(postId)
|
||||
if err == nil {
|
||||
s.deleteFileIndexForPost(postId)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s SearchFileInfoStore) PermanentDelete(fileId string) error {
|
||||
err := s.FileInfoStore.PermanentDelete(fileId)
|
||||
if err == nil {
|
||||
s.deleteFileIndex(fileId)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s SearchFileInfoStore) PermanentDeleteBatch(endTime int64, limit int64) (int64, error) {
|
||||
result, err := s.FileInfoStore.PermanentDeleteBatch(endTime, limit)
|
||||
if err == nil {
|
||||
s.deleteFileIndexBatch(endTime, limit)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s SearchFileInfoStore) PermanentDeleteByUser(userId string) (int64, error) {
|
||||
result, err := s.FileInfoStore.PermanentDeleteByUser(userId)
|
||||
if err == nil {
|
||||
s.deleteFileIndexForUser(userId)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s SearchFileInfoStore) Search(paramsList []*model.SearchParams, userId, teamId string, page, perPage int) (*model.FileInfoList, error) {
|
||||
for _, engine := range s.rootStore.searchEngine.GetActiveEngines() {
|
||||
if engine.IsSearchEnabled() {
|
||||
userChannels, nErr := s.rootStore.Channel().GetChannels(teamId, userId, &model.ChannelSearchOpts{
|
||||
IncludeDeleted: paramsList[0].IncludeDeletedChannels,
|
||||
LastDeleteAt: 0,
|
||||
})
|
||||
if nErr != nil {
|
||||
return nil, nErr
|
||||
}
|
||||
fileIds, appErr := engine.SearchFiles(userChannels, paramsList, page, perPage)
|
||||
if appErr != nil {
|
||||
mlog.Error("Encountered error on Search.", mlog.String("search_engine", engine.GetName()), mlog.Err(appErr))
|
||||
continue
|
||||
}
|
||||
|
||||
// Get the files
|
||||
filesList := model.NewFileInfoList()
|
||||
if len(fileIds) > 0 {
|
||||
files, nErr := s.FileInfoStore.GetByIds(fileIds)
|
||||
if nErr != nil {
|
||||
return nil, nErr
|
||||
}
|
||||
for _, f := range files {
|
||||
filesList.AddFileInfo(f)
|
||||
filesList.AddOrder(f.Id)
|
||||
}
|
||||
}
|
||||
return filesList, nil
|
||||
}
|
||||
}
|
||||
|
||||
if *s.rootStore.getConfig().SqlSettings.DisableDatabaseSearch {
|
||||
return model.NewFileInfoList(), nil
|
||||
}
|
||||
|
||||
return s.FileInfoStore.Search(paramsList, userId, teamId, page, perPage)
|
||||
}
|
||||
126
server/channels/store/searchlayer/layer.go
Обычный файл
126
server/channels/store/searchlayer/layer.go
Обычный файл
@@ -0,0 +1,126 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package searchlayer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost-server/v6/server/channels/store"
|
||||
"github.com/mattermost/mattermost-server/v6/server/platform/services/searchengine"
|
||||
"github.com/mattermost/mattermost-server/v6/server/platform/shared/mlog"
|
||||
)
|
||||
|
||||
type SearchStore struct {
|
||||
store.Store
|
||||
searchEngine *searchengine.Broker
|
||||
user *SearchUserStore
|
||||
team *SearchTeamStore
|
||||
channel *SearchChannelStore
|
||||
post *SearchPostStore
|
||||
fileInfo *SearchFileInfoStore
|
||||
configValue atomic.Value
|
||||
}
|
||||
|
||||
func NewSearchLayer(baseStore store.Store, searchEngine *searchengine.Broker, cfg *model.Config) *SearchStore {
|
||||
searchStore := &SearchStore{
|
||||
Store: baseStore,
|
||||
searchEngine: searchEngine,
|
||||
}
|
||||
searchStore.configValue.Store(cfg)
|
||||
searchStore.channel = &SearchChannelStore{ChannelStore: baseStore.Channel(), rootStore: searchStore}
|
||||
searchStore.post = &SearchPostStore{PostStore: baseStore.Post(), rootStore: searchStore}
|
||||
searchStore.team = &SearchTeamStore{TeamStore: baseStore.Team(), rootStore: searchStore}
|
||||
searchStore.user = &SearchUserStore{UserStore: baseStore.User(), rootStore: searchStore}
|
||||
searchStore.fileInfo = &SearchFileInfoStore{FileInfoStore: baseStore.FileInfo(), rootStore: searchStore}
|
||||
|
||||
return searchStore
|
||||
}
|
||||
|
||||
func (s *SearchStore) UpdateConfig(cfg *model.Config) {
|
||||
s.configValue.Store(cfg)
|
||||
}
|
||||
|
||||
func (s *SearchStore) getConfig() *model.Config {
|
||||
return s.configValue.Load().(*model.Config)
|
||||
}
|
||||
|
||||
func (s *SearchStore) Channel() store.ChannelStore {
|
||||
return s.channel
|
||||
}
|
||||
|
||||
func (s *SearchStore) Post() store.PostStore {
|
||||
return s.post
|
||||
}
|
||||
|
||||
func (s *SearchStore) FileInfo() store.FileInfoStore {
|
||||
return s.fileInfo
|
||||
}
|
||||
|
||||
func (s *SearchStore) Team() store.TeamStore {
|
||||
return s.team
|
||||
}
|
||||
|
||||
func (s *SearchStore) User() store.UserStore {
|
||||
return s.user
|
||||
}
|
||||
|
||||
func (s *SearchStore) indexUserFromID(userId string) {
|
||||
user, err := s.User().Get(context.Background(), userId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s.indexUser(user)
|
||||
}
|
||||
|
||||
func (s *SearchStore) indexUser(user *model.User) {
|
||||
for _, engine := range s.searchEngine.GetActiveEngines() {
|
||||
if engine.IsIndexingEnabled() {
|
||||
runIndexFn(engine, func(engineCopy searchengine.SearchEngineInterface) {
|
||||
userTeams, nErr := s.Team().GetTeamsByUserId(user.Id)
|
||||
if nErr != nil {
|
||||
mlog.Error("Encountered error indexing user", mlog.String("user_id", user.Id), mlog.String("search_engine", engineCopy.GetName()), mlog.Err(nErr))
|
||||
return
|
||||
}
|
||||
|
||||
userTeamsIds := []string{}
|
||||
for _, team := range userTeams {
|
||||
userTeamsIds = append(userTeamsIds, team.Id)
|
||||
}
|
||||
|
||||
userChannelMembers, err := s.Channel().GetAllChannelMembersForUser(user.Id, false, true)
|
||||
if err != nil {
|
||||
mlog.Error("Encountered error indexing user", mlog.String("user_id", user.Id), mlog.String("search_engine", engineCopy.GetName()), mlog.Err(err))
|
||||
return
|
||||
}
|
||||
|
||||
userChannelsIds := []string{}
|
||||
for channelId := range userChannelMembers {
|
||||
userChannelsIds = append(userChannelsIds, channelId)
|
||||
}
|
||||
|
||||
if err := engineCopy.IndexUser(user, userTeamsIds, userChannelsIds); err != nil {
|
||||
mlog.Error("Encountered error indexing user", mlog.String("user_id", user.Id), mlog.String("search_engine", engineCopy.GetName()), mlog.Err(err))
|
||||
return
|
||||
}
|
||||
mlog.Debug("Indexed user in search engine", mlog.String("search_engine", engineCopy.GetName()), mlog.String("user_id", user.Id))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Runs an indexing function synchronously or asynchronously depending on the engine
|
||||
func runIndexFn(engine searchengine.SearchEngineInterface, indexFn func(searchengine.SearchEngineInterface)) {
|
||||
if engine.IsIndexingSync() {
|
||||
indexFn(engine)
|
||||
if err := engine.RefreshIndexes(); err != nil {
|
||||
mlog.Error("Encountered error refresh the indexes", mlog.Err(err))
|
||||
}
|
||||
} else {
|
||||
go (func(engineCopy searchengine.SearchEngineInterface) {
|
||||
indexFn(engineCopy)
|
||||
})(engine)
|
||||
}
|
||||
}
|
||||
45
server/channels/store/searchlayer/layer_test.go
Обычный файл
45
server/channels/store/searchlayer/layer_test.go
Обычный файл
@@ -0,0 +1,45 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package searchlayer_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost-server/v6/server/channels/store/searchlayer"
|
||||
"github.com/mattermost/mattermost-server/v6/server/channels/store/sqlstore"
|
||||
"github.com/mattermost/mattermost-server/v6/server/channels/store/storetest"
|
||||
"github.com/mattermost/mattermost-server/v6/server/channels/testlib"
|
||||
"github.com/mattermost/mattermost-server/v6/server/platform/services/searchengine"
|
||||
)
|
||||
|
||||
// Test to verify race condition on UpdateConfig. The test must run with -race flag in order to verify
|
||||
// that there is no race. Ref: (#MM-30868)
|
||||
func TestUpdateConfigRace(t *testing.T) {
|
||||
driverName := os.Getenv("MM_SQLSETTINGS_DRIVERNAME")
|
||||
if driverName == "" {
|
||||
driverName = model.DatabaseDriverPostgres
|
||||
}
|
||||
settings := storetest.MakeSqlSettings(driverName, false)
|
||||
store := sqlstore.New(*settings, nil)
|
||||
|
||||
cfg := &model.Config{}
|
||||
cfg.SetDefaults()
|
||||
cfg.ClusterSettings.MaxIdleConns = model.NewInt(1)
|
||||
searchEngine := searchengine.NewBroker(cfg)
|
||||
layer := searchlayer.NewSearchLayer(&testlib.TestStore{Store: store}, searchEngine, cfg)
|
||||
var wg sync.WaitGroup
|
||||
|
||||
wg.Add(5)
|
||||
for i := 0; i < 5; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
layer.UpdateConfig(cfg.Clone())
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
195
server/channels/store/searchlayer/post_layer.go
Обычный файл
195
server/channels/store/searchlayer/post_layer.go
Обычный файл
@@ -0,0 +1,195 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package searchlayer
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost-server/v6/server/channels/store"
|
||||
"github.com/mattermost/mattermost-server/v6/server/platform/services/searchengine"
|
||||
"github.com/mattermost/mattermost-server/v6/server/platform/shared/mlog"
|
||||
)
|
||||
|
||||
type SearchPostStore struct {
|
||||
store.PostStore
|
||||
rootStore *SearchStore
|
||||
}
|
||||
|
||||
func (s SearchPostStore) indexPost(post *model.Post) {
|
||||
for _, engine := range s.rootStore.searchEngine.GetActiveEngines() {
|
||||
if engine.IsIndexingEnabled() {
|
||||
runIndexFn(engine, func(engineCopy searchengine.SearchEngineInterface) {
|
||||
channel, chanErr := s.rootStore.Channel().Get(post.ChannelId, true)
|
||||
if chanErr != nil {
|
||||
mlog.Error("Couldn't get channel for post for SearchEngine indexing.", mlog.String("channel_id", post.ChannelId), mlog.String("search_engine", engineCopy.GetName()), mlog.String("post_id", post.Id), mlog.Err(chanErr))
|
||||
return
|
||||
}
|
||||
if err := engineCopy.IndexPost(post, channel.TeamId); err != nil {
|
||||
mlog.Warn("Encountered error indexing post", mlog.String("post_id", post.Id), mlog.String("search_engine", engineCopy.GetName()), mlog.Err(err))
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s SearchPostStore) deletePostIndex(post *model.Post) {
|
||||
for _, engine := range s.rootStore.searchEngine.GetActiveEngines() {
|
||||
if engine.IsIndexingEnabled() {
|
||||
runIndexFn(engine, func(engineCopy searchengine.SearchEngineInterface) {
|
||||
if err := engineCopy.DeletePost(post); err != nil {
|
||||
mlog.Warn("Encountered error deleting post", mlog.String("post_id", post.Id), mlog.String("search_engine", engineCopy.GetName()), mlog.Err(err))
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s SearchPostStore) deleteChannelPostsIndex(channelID string) {
|
||||
for _, engine := range s.rootStore.searchEngine.GetActiveEngines() {
|
||||
if engine.IsIndexingEnabled() {
|
||||
runIndexFn(engine, func(engineCopy searchengine.SearchEngineInterface) {
|
||||
if err := engineCopy.DeleteChannelPosts(channelID); err != nil {
|
||||
mlog.Warn("Encountered error deleting channel posts", mlog.String("channel_id", channelID), mlog.String("search_engine", engineCopy.GetName()), mlog.Err(err))
|
||||
return
|
||||
}
|
||||
mlog.Debug("Removed all channel posts from the index in search engine", mlog.String("channel_id", channelID), mlog.String("search_engine", engineCopy.GetName()))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s SearchPostStore) deleteUserPostsIndex(userID string) {
|
||||
for _, engine := range s.rootStore.searchEngine.GetActiveEngines() {
|
||||
if engine.IsIndexingEnabled() {
|
||||
runIndexFn(engine, func(engineCopy searchengine.SearchEngineInterface) {
|
||||
if err := engineCopy.DeleteUserPosts(userID); err != nil {
|
||||
mlog.Warn("Encountered error deleting user posts", mlog.String("user_id", userID), mlog.String("search_engine", engineCopy.GetName()), mlog.Err(err))
|
||||
return
|
||||
}
|
||||
mlog.Debug("Removed all user posts from the index in search engine", mlog.String("user_id", userID), mlog.String("search_engine", engineCopy.GetName()))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s SearchPostStore) Update(newPost, oldPost *model.Post) (*model.Post, error) {
|
||||
post, err := s.PostStore.Update(newPost, oldPost)
|
||||
|
||||
if err == nil {
|
||||
s.indexPost(post)
|
||||
}
|
||||
return post, err
|
||||
}
|
||||
|
||||
func (s *SearchPostStore) Overwrite(post *model.Post) (*model.Post, error) {
|
||||
post, err := s.PostStore.Overwrite(post)
|
||||
if err == nil {
|
||||
s.indexPost(post)
|
||||
}
|
||||
return post, err
|
||||
}
|
||||
|
||||
func (s SearchPostStore) Save(post *model.Post) (*model.Post, error) {
|
||||
npost, err := s.PostStore.Save(post)
|
||||
|
||||
if err == nil {
|
||||
s.indexPost(npost)
|
||||
}
|
||||
return npost, err
|
||||
}
|
||||
|
||||
func (s SearchPostStore) Delete(postId string, date int64, deletedByID string) error {
|
||||
err := s.PostStore.Delete(postId, date, deletedByID)
|
||||
|
||||
if err == nil {
|
||||
opts := model.GetPostsOptions{
|
||||
SkipFetchThreads: true,
|
||||
}
|
||||
postList, err2 := s.PostStore.Get(context.Background(), postId, opts, "", map[string]bool{})
|
||||
if postList != nil && len(postList.Order) > 0 {
|
||||
if err2 != nil {
|
||||
s.deletePostIndex(postList.Posts[postList.Order[0]])
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s SearchPostStore) PermanentDeleteByUser(userID string) error {
|
||||
err := s.PostStore.PermanentDeleteByUser(userID)
|
||||
if err == nil {
|
||||
s.deleteUserPostsIndex(userID)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s SearchPostStore) PermanentDeleteByChannel(channelID string) error {
|
||||
err := s.PostStore.PermanentDeleteByChannel(channelID)
|
||||
if err == nil {
|
||||
s.deleteChannelPostsIndex(channelID)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s SearchPostStore) searchPostsForUserByEngine(engine searchengine.SearchEngineInterface, paramsList []*model.SearchParams, userId, teamId string, page, perPage int) (*model.PostSearchResults, error) {
|
||||
if err := model.IsSearchParamsListValid(paramsList); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We only allow the user to search in channels they are a member of.
|
||||
userChannels, err2 := s.rootStore.Channel().GetChannels(teamId, userId,
|
||||
&model.ChannelSearchOpts{
|
||||
IncludeDeleted: paramsList[0].IncludeDeletedChannels,
|
||||
LastDeleteAt: 0,
|
||||
})
|
||||
if err2 != nil {
|
||||
return nil, errors.Wrap(err2, "error getting channel for user")
|
||||
}
|
||||
|
||||
postIds, matches, err := engine.SearchPosts(userChannels, paramsList, page, perPage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get the posts
|
||||
postList := model.NewPostList()
|
||||
if len(postIds) > 0 {
|
||||
posts, err := s.PostStore.GetPostsByIds(postIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, p := range posts {
|
||||
if p.DeleteAt == 0 {
|
||||
postList.AddPost(p)
|
||||
postList.AddOrder(p.Id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return model.MakePostSearchResults(postList, matches), nil
|
||||
}
|
||||
|
||||
func (s SearchPostStore) SearchPostsForUser(paramsList []*model.SearchParams, userId, teamId string, page, perPage int) (*model.PostSearchResults, error) {
|
||||
for _, engine := range s.rootStore.searchEngine.GetActiveEngines() {
|
||||
if engine.IsSearchEnabled() {
|
||||
results, err := s.searchPostsForUserByEngine(engine, paramsList, userId, teamId, page, perPage)
|
||||
if err != nil {
|
||||
mlog.Warn("Encountered error on SearchPostsInTeamForUser.", mlog.String("search_engine", engine.GetName()), mlog.Err(err))
|
||||
continue
|
||||
}
|
||||
return results, err
|
||||
}
|
||||
}
|
||||
|
||||
if *s.rootStore.getConfig().SqlSettings.DisableDatabaseSearch {
|
||||
return &model.PostSearchResults{PostList: model.NewPostList(), Matches: model.PostSearchMatches{}}, nil
|
||||
}
|
||||
|
||||
return s.PostStore.SearchPostsForUser(paramsList, userId, teamId, page, perPage)
|
||||
}
|
||||
7
server/channels/store/searchlayer/stop_word.go
Обычный файл
7
server/channels/store/searchlayer/stop_word.go
Обычный файл
@@ -0,0 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package searchlayer
|
||||
|
||||
var MySQLStopWords = []string{"a", "about", "an", "are", "as", "at", "be", "by", "com", "de", "en", "for", "from", "how", "i", "in", "is", "it", "la", "of",
|
||||
"on", "or", "that", "the", "this", "to", "was", "what", "when", "where", "who", "will", "with", "und", "the", "www"}
|
||||
46
server/channels/store/searchlayer/team_layer.go
Обычный файл
46
server/channels/store/searchlayer/team_layer.go
Обычный файл
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package searchlayer
|
||||
|
||||
import (
|
||||
model "github.com/mattermost/mattermost-server/v6/model"
|
||||
store "github.com/mattermost/mattermost-server/v6/server/channels/store"
|
||||
)
|
||||
|
||||
type SearchTeamStore struct {
|
||||
store.TeamStore
|
||||
rootStore *SearchStore
|
||||
}
|
||||
|
||||
func (s SearchTeamStore) SaveMember(teamMember *model.TeamMember, maxUsersPerTeam int) (*model.TeamMember, error) {
|
||||
member, err := s.TeamStore.SaveMember(teamMember, maxUsersPerTeam)
|
||||
if err == nil {
|
||||
s.rootStore.indexUserFromID(member.UserId)
|
||||
}
|
||||
return member, err
|
||||
}
|
||||
|
||||
func (s SearchTeamStore) UpdateMember(teamMember *model.TeamMember) (*model.TeamMember, error) {
|
||||
member, err := s.TeamStore.UpdateMember(teamMember)
|
||||
if err == nil {
|
||||
s.rootStore.indexUserFromID(member.UserId)
|
||||
}
|
||||
return member, err
|
||||
}
|
||||
|
||||
func (s SearchTeamStore) RemoveMember(teamId string, userId string) error {
|
||||
err := s.TeamStore.RemoveMember(teamId, userId)
|
||||
if err == nil {
|
||||
s.rootStore.indexUserFromID(userId)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s SearchTeamStore) RemoveAllMembersByUser(userId string) error {
|
||||
err := s.TeamStore.RemoveAllMembersByUser(userId)
|
||||
if err == nil {
|
||||
s.rootStore.indexUserFromID(userId)
|
||||
}
|
||||
return err
|
||||
}
|
||||
236
server/channels/store/searchlayer/user_layer.go
Обычный файл
236
server/channels/store/searchlayer/user_layer.go
Обычный файл
@@ -0,0 +1,236 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package searchlayer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost-server/v6/server/channels/store"
|
||||
"github.com/mattermost/mattermost-server/v6/server/platform/services/searchengine"
|
||||
"github.com/mattermost/mattermost-server/v6/server/platform/shared/mlog"
|
||||
)
|
||||
|
||||
type SearchUserStore struct {
|
||||
store.UserStore
|
||||
rootStore *SearchStore
|
||||
}
|
||||
|
||||
func (s *SearchUserStore) deleteUserIndex(user *model.User) {
|
||||
for _, engine := range s.rootStore.searchEngine.GetActiveEngines() {
|
||||
if engine.IsIndexingEnabled() {
|
||||
runIndexFn(engine, func(engineCopy searchengine.SearchEngineInterface) {
|
||||
if err := engineCopy.DeleteUser(user); err != nil {
|
||||
mlog.Error("Encountered error deleting user", mlog.String("user_id", user.Id), mlog.String("search_engine", engineCopy.GetName()), mlog.Err(err))
|
||||
return
|
||||
}
|
||||
mlog.Debug("Removed user from the index in search engine", mlog.String("search_engine", engineCopy.GetName()), mlog.String("user_id", user.Id))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SearchUserStore) Search(teamId, term string, options *model.UserSearchOptions) ([]*model.User, error) {
|
||||
for _, engine := range s.rootStore.searchEngine.GetActiveEngines() {
|
||||
if engine.IsSearchEnabled() {
|
||||
listOfAllowedChannels, nErr := s.getListOfAllowedChannels(teamId, "", options.ViewRestrictions)
|
||||
if nErr != nil {
|
||||
mlog.Warn("Encountered error on Search.", mlog.String("search_engine", engine.GetName()), mlog.Err(nErr))
|
||||
continue
|
||||
}
|
||||
|
||||
if listOfAllowedChannels != nil && len(listOfAllowedChannels) == 0 {
|
||||
return []*model.User{}, nil
|
||||
}
|
||||
|
||||
sanitizedTerm := sanitizeSearchTerm(term)
|
||||
|
||||
usersIds, err := engine.SearchUsersInTeam(teamId, listOfAllowedChannels, sanitizedTerm, options)
|
||||
if err != nil {
|
||||
mlog.Warn("Encountered error on Search", mlog.String("search_engine", engine.GetName()), mlog.Err(err))
|
||||
continue
|
||||
}
|
||||
|
||||
users, nErr := s.UserStore.GetProfileByIds(context.Background(), usersIds, nil, false)
|
||||
if nErr != nil {
|
||||
mlog.Warn("Encountered error on Search", mlog.String("search_engine", engine.GetName()), mlog.Err(nErr))
|
||||
continue
|
||||
}
|
||||
|
||||
mlog.Debug("Using the first available search engine", mlog.String("search_engine", engine.GetName()))
|
||||
return users, nil
|
||||
}
|
||||
}
|
||||
|
||||
mlog.Debug("Using database search because no other search engine is available")
|
||||
|
||||
return s.UserStore.Search(teamId, term, options)
|
||||
}
|
||||
|
||||
func (s *SearchUserStore) Update(user *model.User, trustedUpdateData bool) (*model.UserUpdate, error) {
|
||||
userUpdate, err := s.UserStore.Update(user, trustedUpdateData)
|
||||
|
||||
if err == nil {
|
||||
s.rootStore.indexUser(userUpdate.New)
|
||||
}
|
||||
return userUpdate, err
|
||||
}
|
||||
|
||||
func (s *SearchUserStore) Save(user *model.User) (*model.User, error) {
|
||||
nuser, err := s.UserStore.Save(user)
|
||||
|
||||
if err == nil {
|
||||
s.rootStore.indexUser(nuser)
|
||||
}
|
||||
return nuser, err
|
||||
}
|
||||
|
||||
func (s *SearchUserStore) PermanentDelete(userId string) error {
|
||||
user, userErr := s.UserStore.Get(context.Background(), userId)
|
||||
if userErr != nil {
|
||||
mlog.Warn("Encountered error deleting user", mlog.String("user_id", userId), mlog.Err(userErr))
|
||||
}
|
||||
err := s.UserStore.PermanentDelete(userId)
|
||||
if err == nil && userErr == nil {
|
||||
s.deleteUserIndex(user)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *SearchUserStore) autocompleteUsersInChannelByEngine(engine searchengine.SearchEngineInterface, teamId, channelId, term string, options *model.UserSearchOptions) (*model.UserAutocompleteInChannel, error) {
|
||||
var err *model.AppError
|
||||
uchanIds := []string{}
|
||||
nuchanIds := []string{}
|
||||
sanitizedTerm := sanitizeSearchTerm(term)
|
||||
if channelId != "" && options.ListOfAllowedChannels != nil && !strings.Contains(strings.Join(options.ListOfAllowedChannels, "."), channelId) {
|
||||
nuchanIds, err = engine.SearchUsersInTeam(teamId, options.ListOfAllowedChannels, sanitizedTerm, options)
|
||||
} else {
|
||||
uchanIds, nuchanIds, err = engine.SearchUsersInChannel(teamId, channelId, options.ListOfAllowedChannels, sanitizedTerm, options)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
uchan := make(chan store.StoreResult, 1)
|
||||
go func() {
|
||||
users, nErr := s.UserStore.GetProfileByIds(context.Background(), uchanIds, nil, false)
|
||||
uchan <- store.StoreResult{Data: users, NErr: nErr}
|
||||
close(uchan)
|
||||
}()
|
||||
|
||||
nuchan := make(chan store.StoreResult, 1)
|
||||
go func() {
|
||||
users, nErr := s.UserStore.GetProfileByIds(context.Background(), nuchanIds, nil, false)
|
||||
nuchan <- store.StoreResult{Data: users, NErr: nErr}
|
||||
close(nuchan)
|
||||
}()
|
||||
|
||||
autocomplete := &model.UserAutocompleteInChannel{}
|
||||
|
||||
result := <-uchan
|
||||
if result.NErr != nil {
|
||||
return nil, errors.Wrap(result.NErr, "failed to get user profiles by ids")
|
||||
}
|
||||
inUsers := result.Data.([]*model.User)
|
||||
autocomplete.InChannel = inUsers
|
||||
|
||||
result = <-nuchan
|
||||
if result.NErr != nil {
|
||||
return nil, errors.Wrap(result.NErr, "failed to get user profiles by ids")
|
||||
}
|
||||
outUsers := result.Data.([]*model.User)
|
||||
autocomplete.OutOfChannel = outUsers
|
||||
|
||||
return autocomplete, nil
|
||||
}
|
||||
|
||||
// getListOfAllowedChannels return the list of allowed channels to search user based on the
|
||||
//
|
||||
// next scenarios:
|
||||
// - If there isn't view restrictions (team or channel) and no team id to filter them, then all
|
||||
// channels are allowed (nil return)
|
||||
// - If we receive a team Id and either we don't have view restrictions or the provided team id is included in the
|
||||
// list of restricted teams, then we return all the team channels
|
||||
// - If we don't receive team id or the provided team id is not in the list of allowed teams to search of and we
|
||||
// don't have channel restrictions then we return an empty result because we cannot get channels
|
||||
// - If we receive channels restrictions we get:
|
||||
// - If we don't have team id, we get those restricted channels (guest accounts and quick search)
|
||||
// - If we have a team id then we only return those restricted channels that belongs to that team
|
||||
func (s *SearchUserStore) getListOfAllowedChannels(teamId, channelId string, viewRestrictions *model.ViewUsersRestrictions) ([]string, error) {
|
||||
var listOfAllowedChannels []string
|
||||
if viewRestrictions == nil && teamId == "" {
|
||||
// nil return without error means all channels are allowed
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if teamId != "" && (viewRestrictions == nil || strings.Contains(strings.Join(viewRestrictions.Teams, "."), teamId)) {
|
||||
channels, err := s.rootStore.Channel().GetTeamChannels(teamId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get team channels")
|
||||
}
|
||||
for _, channel := range channels {
|
||||
listOfAllowedChannels = append(listOfAllowedChannels, channel.Id)
|
||||
}
|
||||
|
||||
if channelId != "" {
|
||||
ch, err := s.rootStore.Channel().Get(channelId, true)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get channel with id: %s", channelId)
|
||||
}
|
||||
// Check if DM/GM channel, and add to the list.
|
||||
// This is because GetTeamChannels does not return DM/GM channels.
|
||||
// And since the channelId is passed from the API layer, it is already
|
||||
// auth checked to confirm that the user has permission.
|
||||
if ch.IsGroupOrDirect() {
|
||||
listOfAllowedChannels = append(listOfAllowedChannels, channelId)
|
||||
}
|
||||
}
|
||||
return listOfAllowedChannels, nil
|
||||
}
|
||||
|
||||
if len(viewRestrictions.Channels) > 0 {
|
||||
channels, err := s.rootStore.Channel().GetChannelsByIds(viewRestrictions.Channels, false)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get channels by ids")
|
||||
}
|
||||
for _, c := range channels {
|
||||
if teamId == "" || (teamId != "" && c.TeamId == teamId) {
|
||||
listOfAllowedChannels = append(listOfAllowedChannels, c.Id)
|
||||
}
|
||||
}
|
||||
return listOfAllowedChannels, nil
|
||||
}
|
||||
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (s *SearchUserStore) AutocompleteUsersInChannel(teamId, channelId, term string, options *model.UserSearchOptions) (*model.UserAutocompleteInChannel, error) {
|
||||
for _, engine := range s.rootStore.searchEngine.GetActiveEngines() {
|
||||
if engine.IsAutocompletionEnabled() {
|
||||
listOfAllowedChannels, nErr := s.getListOfAllowedChannels(teamId, channelId, options.ViewRestrictions)
|
||||
if nErr != nil {
|
||||
mlog.Warn("Encountered error on AutocompleteUsersInChannel.", mlog.String("search_engine", engine.GetName()), mlog.Err(nErr))
|
||||
continue
|
||||
}
|
||||
if listOfAllowedChannels != nil && len(listOfAllowedChannels) == 0 {
|
||||
return &model.UserAutocompleteInChannel{}, nil
|
||||
}
|
||||
options.ListOfAllowedChannels = listOfAllowedChannels
|
||||
|
||||
autocomplete, nErr := s.autocompleteUsersInChannelByEngine(engine, teamId, channelId, term, options)
|
||||
if nErr != nil {
|
||||
mlog.Warn("Encountered error on AutocompleteUsersInChannel.", mlog.String("search_engine", engine.GetName()), mlog.Err(nErr))
|
||||
continue
|
||||
}
|
||||
mlog.Debug("Using the first available search engine", mlog.String("search_engine", engine.GetName()))
|
||||
return autocomplete, nil
|
||||
}
|
||||
}
|
||||
|
||||
mlog.Debug("Using database search because no other search engine is available")
|
||||
return s.UserStore.AutocompleteUsersInChannel(teamId, channelId, term, options)
|
||||
}
|
||||
12
server/channels/store/searchlayer/utils.go
Обычный файл
12
server/channels/store/searchlayer/utils.go
Обычный файл
@@ -0,0 +1,12 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package searchlayer
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func sanitizeSearchTerm(term string) string {
|
||||
return strings.TrimLeft(term, "@")
|
||||
}
|
||||
Ссылка в новой задаче
Block a user