mirror of
https://github.com/mattermost/mattermost.git
synced 2026-05-28 04:35:04 -04:00
Adds elasticsearch to the user and channel autocompletion functions (#10354)
* Adds elasticsearch to the user and channel autocompletion functions * Implement channel store GetChannelsByIds test * Style changes and govet fixes * Add gofmt fixes * Extract default channel search limit to a const * Add StringSliceDiff function to the utils package * Honor USER_SEARCH_MAX_LIMIT on the user autocomplete api handler * Change the elasticsearch development image
This commit is contained in:
parent
5dae08761c
commit
44887a0272
17 changed files with 461 additions and 13 deletions
2
Makefile
2
Makefile
|
|
@ -171,7 +171,7 @@ ifeq ($(BUILD_ENTERPRISE_READY),true)
|
|||
|
||||
@if [ $(shell docker ps -a --no-trunc --quiet --filter name=^/mattermost-elasticsearch$$ | wc -l) -eq 0 ]; then \
|
||||
echo starting mattermost-elasticsearch; \
|
||||
docker run --name mattermost-elasticsearch -p 9200:9200 -e "http.host=0.0.0.0" -e "transport.host=127.0.0.1" -e "ES_JAVA_OPTS=-Xms250m -Xmx250m" -d grundleborg/elasticsearch:latest > /dev/null; \
|
||||
docker run --name mattermost-elasticsearch -p 9200:9200 -e "http.host=0.0.0.0" -e "transport.host=127.0.0.1" -e "ES_JAVA_OPTS=-Xms250m -Xmx250m" -d mattermost/mattermost-elasticsearch-docker:6.5.1 > /dev/null; \
|
||||
elif [ $(shell docker ps --no-trunc --quiet --filter name=^/mattermost-elasticsearch$$ | wc -l) -eq 0 ]; then \
|
||||
echo restarting mattermost-elasticsearch; \
|
||||
docker start mattermost-elasticsearch> /dev/null; \
|
||||
|
|
|
|||
|
|
@ -598,6 +598,8 @@ func autocompleteUsers(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||
limit, _ := strconv.Atoi(limitStr)
|
||||
if limitStr == "" {
|
||||
limit = model.USER_SEARCH_DEFAULT_LIMIT
|
||||
} else if limit > model.USER_SEARCH_MAX_LIMIT {
|
||||
limit = model.USER_SEARCH_MAX_LIMIT
|
||||
}
|
||||
|
||||
options := &model.UserSearchOptions{
|
||||
|
|
|
|||
142
app/channel.go
142
app/channel.go
|
|
@ -91,6 +91,15 @@ func (a *App) JoinDefaultChannels(teamId string, user *model.User, shouldBeAdmin
|
|||
}
|
||||
}
|
||||
|
||||
esInterface := a.Elasticsearch
|
||||
if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing {
|
||||
a.Srv.Go(func() {
|
||||
if err = a.indexUser(user); err != nil {
|
||||
mlog.Error("Encountered error indexing user", mlog.String("user_id", user.Id), mlog.Err(err))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -160,6 +169,15 @@ func (a *App) CreateChannelWithUser(channel *model.Channel, userId string) (*mod
|
|||
message.Add("team_id", channel.TeamId)
|
||||
a.Publish(message)
|
||||
|
||||
esInterface := a.Elasticsearch
|
||||
if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing {
|
||||
a.Srv.Go(func() {
|
||||
if err := a.indexUser(user); err != nil {
|
||||
mlog.Error("Encountered error indexing user", mlog.String("user_id", user.Id), mlog.Err(err))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return rchannel, nil
|
||||
}
|
||||
|
||||
|
|
@ -223,6 +241,24 @@ func (a *App) CreateChannel(channel *model.Channel, addMember bool) (*model.Chan
|
|||
})
|
||||
}
|
||||
|
||||
esInterface := a.Elasticsearch
|
||||
if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing {
|
||||
if sc.Type == "O" {
|
||||
a.Srv.Go(func() {
|
||||
if err := esInterface.IndexChannel(sc); err != nil {
|
||||
mlog.Error("Encountered error indexing channel", mlog.String("channel_id", sc.Id), mlog.Err(err))
|
||||
}
|
||||
})
|
||||
}
|
||||
if addMember {
|
||||
a.Srv.Go(func() {
|
||||
if err := a.indexUserFromId(channel.CreatorId); err != nil {
|
||||
mlog.Error("Encountered error indexing user", mlog.String("user_id", channel.CreatorId), mlog.Err(err))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return sc, nil
|
||||
}
|
||||
|
||||
|
|
@ -253,6 +289,17 @@ func (a *App) GetOrCreateDirectChannel(userId, otherUserId string) (*model.Chann
|
|||
})
|
||||
}
|
||||
|
||||
esInterface := a.Elasticsearch
|
||||
if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing {
|
||||
a.Srv.Go(func() {
|
||||
for _, id := range []string{userId, otherUserId} {
|
||||
if err := a.indexUserFromId(id); err != nil {
|
||||
mlog.Error("Encountered error indexing user", mlog.String("user_id", id), mlog.Err(err))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_DIRECT_ADDED, "", channel.Id, "", nil)
|
||||
message.Add("teammate_id", otherUserId)
|
||||
a.Publish(message)
|
||||
|
|
@ -344,6 +391,17 @@ func (a *App) CreateGroupChannel(userIds []string, creatorId string) (*model.Cha
|
|||
message.Add("teammate_ids", model.ArrayToJson(userIds))
|
||||
a.Publish(message)
|
||||
|
||||
esInterface := a.Elasticsearch
|
||||
if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing {
|
||||
a.Srv.Go(func() {
|
||||
for _, id := range userIds {
|
||||
if err := a.indexUserFromId(id); err != nil {
|
||||
mlog.Error("Encountered error indexing user", mlog.String("user_id", id), mlog.Err(err))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return channel, nil
|
||||
}
|
||||
|
||||
|
|
@ -431,6 +489,15 @@ func (a *App) UpdateChannel(channel *model.Channel) (*model.Channel, *model.AppE
|
|||
messageWs.Add("channel", channel.ToJson())
|
||||
a.Publish(messageWs)
|
||||
|
||||
esInterface := a.Elasticsearch
|
||||
if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing && channel.Type == "O" {
|
||||
a.Srv.Go(func() {
|
||||
if err := esInterface.IndexChannel(channel); err != nil {
|
||||
mlog.Error("Encountered error indexing channel", mlog.String("channel_id", channel.Id), mlog.Err(err))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return channel, nil
|
||||
}
|
||||
|
||||
|
|
@ -874,6 +941,15 @@ func (a *App) AddChannelMember(userId string, channel *model.Channel, userReques
|
|||
})
|
||||
}
|
||||
|
||||
esInterface := a.Elasticsearch
|
||||
if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing {
|
||||
a.Srv.Go(func() {
|
||||
if err := a.indexUser(user); err != nil {
|
||||
mlog.Error("Encountered error indexing user", mlog.String("user_id", user.Id), mlog.Err(err))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if userRequestorId == "" || userId == userRequestorId {
|
||||
a.postJoinChannelMessage(user, channel)
|
||||
} else {
|
||||
|
|
@ -1289,6 +1365,15 @@ func (a *App) JoinChannel(channel *model.Channel, userId string) *model.AppError
|
|||
})
|
||||
}
|
||||
|
||||
esInterface := a.Elasticsearch
|
||||
if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing {
|
||||
a.Srv.Go(func() {
|
||||
if err := a.indexUser(user); err != nil {
|
||||
mlog.Error("Encountered error indexing user", mlog.String("user_id", user.Id), mlog.Err(err))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if err := a.postJoinChannelMessage(user, channel); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -1495,6 +1580,15 @@ func (a *App) removeUserFromChannel(userIdToRemove string, removerUserId string,
|
|||
})
|
||||
}
|
||||
|
||||
esInterface := a.Elasticsearch
|
||||
if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing {
|
||||
a.Srv.Go(func() {
|
||||
if err := a.indexUserFromId(userIdToRemove); err != nil {
|
||||
mlog.Error("Encountered error indexing user", mlog.String("user_id", userIdToRemove), mlog.Err(err))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_USER_REMOVED, "", channel.Id, "", nil)
|
||||
message.Add("user_id", userIdToRemove)
|
||||
message.Add("remover_id", removerUserId)
|
||||
|
|
@ -1585,6 +1679,31 @@ func (a *App) UpdateChannelLastViewedAt(channelIds []string, userId string) *mod
|
|||
func (a *App) AutocompleteChannels(teamId string, term string) (*model.ChannelList, *model.AppError) {
|
||||
includeDeleted := *a.Config().TeamSettings.ExperimentalViewArchivedChannels
|
||||
|
||||
esInterface := a.Elasticsearch
|
||||
license := a.License()
|
||||
if esInterface != nil && *a.Config().ElasticsearchSettings.EnableAutocomplete && license != nil && *license.Features.Elasticsearch {
|
||||
channelIds, err := a.Elasticsearch.SearchChannels(teamId, term)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
channelList := model.ChannelList{}
|
||||
if len(channelIds) > 0 {
|
||||
cresult := <-a.Srv.Store.Channel().GetChannelsByIds(channelIds)
|
||||
if cresult.Err != nil {
|
||||
return nil, cresult.Err
|
||||
}
|
||||
for _, c := range cresult.Data.([]*model.Channel) {
|
||||
if c.DeleteAt > 0 && !includeDeleted {
|
||||
continue
|
||||
}
|
||||
channelList = append(channelList, c)
|
||||
}
|
||||
}
|
||||
|
||||
return &channelList, nil
|
||||
}
|
||||
|
||||
result := <-a.Srv.Store.Channel().AutocompleteInTeam(teamId, term, includeDeleted)
|
||||
if result.Err != nil {
|
||||
return nil, result.Err
|
||||
|
|
@ -1710,6 +1829,11 @@ func (a *App) ViewChannel(view *model.ChannelView, userId string, clearPushNotif
|
|||
}
|
||||
|
||||
func (a *App) PermanentDeleteChannel(channel *model.Channel) *model.AppError {
|
||||
channelUsers := <-a.Srv.Store.User().GetAllProfilesInChannel(channel.Id, false)
|
||||
if channelUsers.Err != nil {
|
||||
return channelUsers.Err
|
||||
}
|
||||
|
||||
if result := <-a.Srv.Store.Post().PermanentDeleteByChannel(channel.Id); result.Err != nil {
|
||||
return result.Err
|
||||
}
|
||||
|
|
@ -1730,6 +1854,24 @@ func (a *App) PermanentDeleteChannel(channel *model.Channel) *model.AppError {
|
|||
return result.Err
|
||||
}
|
||||
|
||||
esInterface := a.Elasticsearch
|
||||
if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing {
|
||||
a.Srv.Go(func() {
|
||||
for _, user := range channelUsers.Data.(map[string]*model.User) {
|
||||
if err := a.indexUser(user); err != nil {
|
||||
mlog.Error("Encountered error indexing user", mlog.String("user_id", user.Id), mlog.Err(err))
|
||||
}
|
||||
}
|
||||
})
|
||||
if channel.Type == "O" {
|
||||
a.Srv.Go(func() {
|
||||
if err := esInterface.DeleteChannel(channel); err != nil {
|
||||
mlog.Error("Encountered error deleting channel", mlog.String("channel_id", channel.Id), mlog.Err(err))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -548,9 +548,14 @@ func (a *App) trackConfig() {
|
|||
"isdefault_password": isDefault(*cfg.ElasticsearchSettings.Password, model.ELASTICSEARCH_SETTINGS_DEFAULT_PASSWORD),
|
||||
"enable_indexing": *cfg.ElasticsearchSettings.EnableIndexing,
|
||||
"enable_searching": *cfg.ElasticsearchSettings.EnableSearching,
|
||||
"enable_autocomplete": *cfg.ElasticsearchSettings.EnableAutocomplete,
|
||||
"sniff": *cfg.ElasticsearchSettings.Sniff,
|
||||
"post_index_replicas": *cfg.ElasticsearchSettings.PostIndexReplicas,
|
||||
"post_index_shards": *cfg.ElasticsearchSettings.PostIndexShards,
|
||||
"channel_index_replicas": *cfg.ElasticsearchSettings.ChannelIndexReplicas,
|
||||
"channel_index_shards": *cfg.ElasticsearchSettings.ChannelIndexShards,
|
||||
"user_index_replicas": *cfg.ElasticsearchSettings.UserIndexReplicas,
|
||||
"user_index_shards": *cfg.ElasticsearchSettings.UserIndexShards,
|
||||
"isdefault_index_prefix": isDefault(*cfg.ElasticsearchSettings.IndexPrefix, model.ELASTICSEARCH_SETTINGS_DEFAULT_INDEX_PREFIX),
|
||||
"live_indexing_batch_size": *cfg.ElasticsearchSettings.LiveIndexingBatchSize,
|
||||
"bulk_indexing_time_window_seconds": *cfg.ElasticsearchSettings.BulkIndexingTimeWindowSeconds,
|
||||
|
|
|
|||
|
|
@ -830,6 +830,15 @@ func (a *App) LeaveTeam(team *model.Team, user *model.User, requestorId string)
|
|||
})
|
||||
}
|
||||
|
||||
esInterface := a.Elasticsearch
|
||||
if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing {
|
||||
a.Srv.Go(func() {
|
||||
if err := a.indexUser(user); err != nil {
|
||||
mlog.Error("Encountered error indexing user", mlog.String("user_id", user.Id), mlog.Err(err))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if uua := <-a.Srv.Store.User().UpdateUpdateAt(user.Id); uua.Err != nil {
|
||||
return uua.Err
|
||||
}
|
||||
|
|
|
|||
119
app/user.go
119
app/user.go
|
|
@ -31,6 +31,7 @@ import (
|
|||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/plugin"
|
||||
"github.com/mattermost/mattermost-server/services/mfa"
|
||||
"github.com/mattermost/mattermost-server/store"
|
||||
"github.com/mattermost/mattermost-server/utils"
|
||||
"github.com/mattermost/mattermost-server/utils/fileutils"
|
||||
)
|
||||
|
|
@ -186,6 +187,40 @@ func (a *App) IsFirstUserAccount() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// indexUser fetches the required information to index a user from the database and
|
||||
// calls the elasticsearch interface method
|
||||
func (a *App) indexUser(user *model.User) *model.AppError {
|
||||
userTeams := <-a.Srv.Store.Team().GetTeamsByUserId(user.Id)
|
||||
if userTeams.Err != nil {
|
||||
return userTeams.Err
|
||||
}
|
||||
|
||||
userTeamsIds := []string{}
|
||||
for _, team := range userTeams.Data.([]*model.Team) {
|
||||
userTeamsIds = append(userTeamsIds, team.Id)
|
||||
}
|
||||
|
||||
userChannelMembers := <-a.Srv.Store.Channel().GetAllChannelMembersForUser(user.Id, false, true)
|
||||
if userChannelMembers.Err != nil {
|
||||
return userChannelMembers.Err
|
||||
}
|
||||
|
||||
userChannelsIds := []string{}
|
||||
for channelId := range userChannelMembers.Data.(map[string]string) {
|
||||
userChannelsIds = append(userChannelsIds, channelId)
|
||||
}
|
||||
|
||||
return a.Elasticsearch.IndexUser(user, userTeamsIds, userChannelsIds)
|
||||
}
|
||||
|
||||
func (a *App) indexUserFromId(userId string) *model.AppError {
|
||||
user, err := a.GetUser(userId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return a.indexUser(user)
|
||||
}
|
||||
|
||||
// CreateUser creates a user and sets several fields of the returned User struct to
|
||||
// their zero values.
|
||||
func (a *App) CreateUser(user *model.User) (*model.User, *model.AppError) {
|
||||
|
|
@ -230,6 +265,15 @@ func (a *App) CreateUser(user *model.User) (*model.User, *model.AppError) {
|
|||
})
|
||||
}
|
||||
|
||||
esInterface := a.Elasticsearch
|
||||
if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing {
|
||||
a.Srv.Go(func() {
|
||||
if err := a.indexUser(user); err != nil {
|
||||
mlog.Error("Encountered error indexing user", mlog.String("user_id", user.Id), mlog.Err(err))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return ruser, nil
|
||||
}
|
||||
|
||||
|
|
@ -1076,6 +1120,15 @@ func (a *App) UpdateUser(user *model.User, sendNotifications bool) (*model.User,
|
|||
|
||||
a.InvalidateCacheForUser(user.Id)
|
||||
|
||||
esInterface := a.Elasticsearch
|
||||
if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing {
|
||||
a.Srv.Go(func() {
|
||||
if err := a.indexUser(user); err != nil {
|
||||
mlog.Error("Encountered error indexing user", mlog.String("user_id", user.Id), mlog.Err(err))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return rusers[0], nil
|
||||
}
|
||||
|
||||
|
|
@ -1413,6 +1466,15 @@ func (a *App) PermanentDeleteUser(user *model.User) *model.AppError {
|
|||
|
||||
mlog.Warn(fmt.Sprintf("Permanently deleted account %v id=%v", user.Email, user.Id), mlog.String("user_id", user.Id))
|
||||
|
||||
esInterface := a.Elasticsearch
|
||||
if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing {
|
||||
a.Srv.Go(func() {
|
||||
if err := a.Elasticsearch.DeleteUser(user); err != nil {
|
||||
mlog.Error("Encountered error deleting user", mlog.String("user_id", user.Id), mlog.Err(err))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -1596,7 +1658,21 @@ func (a *App) SearchUsersNotInChannel(teamId string, channelId string, term stri
|
|||
}
|
||||
|
||||
func (a *App) SearchUsersInTeam(teamId string, term string, options *model.UserSearchOptions) ([]*model.User, *model.AppError) {
|
||||
result := <-a.Srv.Store.User().Search(teamId, term, options)
|
||||
var result store.StoreResult
|
||||
|
||||
esInterface := a.Elasticsearch
|
||||
license := a.License()
|
||||
if esInterface != nil && *a.Config().ElasticsearchSettings.EnableAutocomplete && license != nil && *license.Features.Elasticsearch {
|
||||
usersIds, err := a.Elasticsearch.SearchUsersInTeam(teamId, term, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result = <-a.Srv.Store.User().GetProfileByIds(usersIds, false)
|
||||
} else {
|
||||
result = <-a.Srv.Store.User().Search(teamId, term, options)
|
||||
}
|
||||
|
||||
if result.Err != nil {
|
||||
return nil, result.Err
|
||||
}
|
||||
|
|
@ -1638,8 +1714,21 @@ func (a *App) SearchUsersWithoutTeam(term string, options *model.UserSearchOptio
|
|||
}
|
||||
|
||||
func (a *App) AutocompleteUsersInChannel(teamId string, channelId string, term string, options *model.UserSearchOptions) (*model.UserAutocompleteInChannel, *model.AppError) {
|
||||
uchan := a.Srv.Store.User().SearchInChannel(channelId, term, options)
|
||||
nuchan := a.Srv.Store.User().SearchNotInChannel(teamId, channelId, term, options)
|
||||
var uchan, nuchan store.StoreChannel
|
||||
|
||||
esInterface := a.Elasticsearch
|
||||
license := a.License()
|
||||
if esInterface != nil && *a.Config().ElasticsearchSettings.EnableAutocomplete && license != nil && *license.Features.Elasticsearch {
|
||||
uchanIds, nuchanIds, err := a.Elasticsearch.SearchUsersInChannel(teamId, channelId, term, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uchan = a.Srv.Store.User().GetProfileByIds(uchanIds, false)
|
||||
nuchan = a.Srv.Store.User().GetProfileByIds(nuchanIds, false)
|
||||
} else {
|
||||
uchan = a.Srv.Store.User().SearchInChannel(channelId, term, options)
|
||||
nuchan = a.Srv.Store.User().SearchNotInChannel(teamId, channelId, term, options)
|
||||
}
|
||||
|
||||
autocomplete := &model.UserAutocompleteInChannel{}
|
||||
|
||||
|
|
@ -1672,8 +1761,21 @@ func (a *App) AutocompleteUsersInChannel(teamId string, channelId string, term s
|
|||
|
||||
func (a *App) AutocompleteUsersInTeam(teamId string, term string, options *model.UserSearchOptions) (*model.UserAutocompleteInTeam, *model.AppError) {
|
||||
autocomplete := &model.UserAutocompleteInTeam{}
|
||||
var result store.StoreResult
|
||||
|
||||
esInterface := a.Elasticsearch
|
||||
license := a.License()
|
||||
if esInterface != nil && *a.Config().ElasticsearchSettings.EnableAutocomplete && license != nil && *license.Features.Elasticsearch {
|
||||
usersIds, err := a.Elasticsearch.SearchUsersInTeam(teamId, term, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result = <-a.Srv.Store.User().GetProfileByIds(usersIds, false)
|
||||
} else {
|
||||
result = <-a.Srv.Store.User().Search(teamId, term, options)
|
||||
}
|
||||
|
||||
result := <-a.Srv.Store.User().Search(teamId, term, options)
|
||||
if result.Err != nil {
|
||||
return nil, result.Err
|
||||
}
|
||||
|
|
@ -1730,6 +1832,15 @@ func (a *App) UpdateOAuthUserAttrs(userData io.Reader, user *model.User, provide
|
|||
|
||||
user = result.Data.([2]*model.User)[0]
|
||||
a.InvalidateCacheForUser(user.Id)
|
||||
|
||||
esInterface := a.Elasticsearch
|
||||
if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing {
|
||||
a.Srv.Go(func() {
|
||||
if err := a.indexUser(user); err != nil {
|
||||
mlog.Error("Encountered error indexing user", mlog.String("user_id", user.Id), mlog.Err(err))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -372,9 +372,14 @@
|
|||
"Password": "changeme",
|
||||
"EnableIndexing": false,
|
||||
"EnableSearching": false,
|
||||
"EnableAutocomplete": false,
|
||||
"Sniff": true,
|
||||
"PostIndexReplicas": 1,
|
||||
"PostIndexShards": 1,
|
||||
"ChannelIndexReplicas": 1,
|
||||
"ChannelIndexShards": 1,
|
||||
"UserIndexReplicas": 1,
|
||||
"UserIndexShards": 1,
|
||||
"AggregatePostsAfterDays": 365,
|
||||
"PostsAggregatorJobStartTime": "03:00",
|
||||
"IndexPrefix": "",
|
||||
|
|
|
|||
|
|
@ -15,6 +15,13 @@ type ElasticsearchInterface interface {
|
|||
IndexPost(post *model.Post, teamId string) *model.AppError
|
||||
SearchPosts(channels *model.ChannelList, searchParams []*model.SearchParams, page, perPage int) ([]string, model.PostSearchMatches, *model.AppError)
|
||||
DeletePost(post *model.Post) *model.AppError
|
||||
IndexChannel(channel *model.Channel) *model.AppError
|
||||
SearchChannels(teamId, term string) ([]string, *model.AppError)
|
||||
DeleteChannel(channel *model.Channel) *model.AppError
|
||||
IndexUser(user *model.User, teamsIds, channelsIds []string) *model.AppError
|
||||
SearchUsersInChannel(teamId, channelId, term string, options *model.UserSearchOptions) ([]string, []string, *model.AppError)
|
||||
SearchUsersInTeam(teamId, term string, options *model.UserSearchOptions) ([]string, *model.AppError)
|
||||
DeleteUser(user *model.User) *model.AppError
|
||||
TestConfig(cfg *model.Config) *model.AppError
|
||||
PurgeIndexes() *model.AppError
|
||||
DataRetentionDeleteIndexes(cutoff time.Time) *model.AppError
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import (
|
|||
"io"
|
||||
)
|
||||
|
||||
const CHANNEL_SEARCH_DEFAULT_LIMIT = 50
|
||||
|
||||
type ChannelSearch struct {
|
||||
Term string `json:"term"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -155,6 +155,10 @@ const (
|
|||
ELASTICSEARCH_SETTINGS_DEFAULT_PASSWORD = "changeme"
|
||||
ELASTICSEARCH_SETTINGS_DEFAULT_POST_INDEX_REPLICAS = 1
|
||||
ELASTICSEARCH_SETTINGS_DEFAULT_POST_INDEX_SHARDS = 1
|
||||
ELASTICSEARCH_SETTINGS_DEFAULT_CHANNEL_INDEX_REPLICAS = 1
|
||||
ELASTICSEARCH_SETTINGS_DEFAULT_CHANNEL_INDEX_SHARDS = 1
|
||||
ELASTICSEARCH_SETTINGS_DEFAULT_USER_INDEX_REPLICAS = 1
|
||||
ELASTICSEARCH_SETTINGS_DEFAULT_USER_INDEX_SHARDS = 1
|
||||
ELASTICSEARCH_SETTINGS_DEFAULT_AGGREGATE_POSTS_AFTER_DAYS = 365
|
||||
ELASTICSEARCH_SETTINGS_DEFAULT_POSTS_AGGREGATOR_JOB_START_TIME = "03:00"
|
||||
ELASTICSEARCH_SETTINGS_DEFAULT_INDEX_PREFIX = ""
|
||||
|
|
@ -1922,9 +1926,14 @@ type ElasticsearchSettings struct {
|
|||
Password *string
|
||||
EnableIndexing *bool
|
||||
EnableSearching *bool
|
||||
EnableAutocomplete *bool
|
||||
Sniff *bool
|
||||
PostIndexReplicas *int
|
||||
PostIndexShards *int
|
||||
ChannelIndexReplicas *int
|
||||
ChannelIndexShards *int
|
||||
UserIndexReplicas *int
|
||||
UserIndexShards *int
|
||||
AggregatePostsAfterDays *int
|
||||
PostsAggregatorJobStartTime *string
|
||||
IndexPrefix *string
|
||||
|
|
@ -1954,6 +1963,10 @@ func (s *ElasticsearchSettings) SetDefaults() {
|
|||
s.EnableSearching = NewBool(false)
|
||||
}
|
||||
|
||||
if s.EnableAutocomplete == nil {
|
||||
s.EnableAutocomplete = NewBool(false)
|
||||
}
|
||||
|
||||
if s.Sniff == nil {
|
||||
s.Sniff = NewBool(true)
|
||||
}
|
||||
|
|
@ -1966,6 +1979,22 @@ func (s *ElasticsearchSettings) SetDefaults() {
|
|||
s.PostIndexShards = NewInt(ELASTICSEARCH_SETTINGS_DEFAULT_POST_INDEX_SHARDS)
|
||||
}
|
||||
|
||||
if s.ChannelIndexReplicas == nil {
|
||||
s.ChannelIndexReplicas = NewInt(ELASTICSEARCH_SETTINGS_DEFAULT_CHANNEL_INDEX_REPLICAS)
|
||||
}
|
||||
|
||||
if s.ChannelIndexShards == nil {
|
||||
s.ChannelIndexShards = NewInt(ELASTICSEARCH_SETTINGS_DEFAULT_CHANNEL_INDEX_SHARDS)
|
||||
}
|
||||
|
||||
if s.UserIndexReplicas == nil {
|
||||
s.UserIndexReplicas = NewInt(ELASTICSEARCH_SETTINGS_DEFAULT_USER_INDEX_REPLICAS)
|
||||
}
|
||||
|
||||
if s.UserIndexShards == nil {
|
||||
s.UserIndexShards = NewInt(ELASTICSEARCH_SETTINGS_DEFAULT_USER_INDEX_SHARDS)
|
||||
}
|
||||
|
||||
if s.AggregatePostsAfterDays == nil {
|
||||
s.AggregatePostsAfterDays = NewInt(ELASTICSEARCH_SETTINGS_DEFAULT_AGGREGATE_POSTS_AFTER_DAYS)
|
||||
}
|
||||
|
|
@ -2689,6 +2718,10 @@ func (ess *ElasticsearchSettings) isValid() *AppError {
|
|||
return NewAppError("Config.IsValid", "model.config.is_valid.elastic_search.enable_searching.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if *ess.EnableAutocomplete && !*ess.EnableIndexing {
|
||||
return NewAppError("Config.IsValid", "model.config.is_valid.elastic_search.enable_autocomplete.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if *ess.AggregatePostsAfterDays < 1 {
|
||||
return NewAppError("Config.IsValid", "model.config.is_valid.elastic_search.aggregate_posts_after_days.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -785,10 +785,10 @@ func (s SqlChannelStore) SetDeleteAt(channelId string, deleteAt, updateAt int64)
|
|||
// Additionally propagate the write to the PublicChannels table.
|
||||
if _, err := transaction.Exec(`
|
||||
UPDATE
|
||||
PublicChannels
|
||||
SET
|
||||
PublicChannels
|
||||
SET
|
||||
DeleteAt = :DeleteAt
|
||||
WHERE
|
||||
WHERE
|
||||
Id = :ChannelId
|
||||
`, map[string]interface{}{
|
||||
"DeleteAt": deleteAt,
|
||||
|
|
@ -835,7 +835,7 @@ func (s SqlChannelStore) PermanentDeleteByTeam(teamId string) store.StoreChannel
|
|||
// Additionally propagate the deletions to the PublicChannels table.
|
||||
if _, err := transaction.Exec(`
|
||||
DELETE FROM
|
||||
PublicChannels
|
||||
PublicChannels
|
||||
WHERE
|
||||
TeamId = :TeamId
|
||||
`, map[string]interface{}{
|
||||
|
|
@ -881,7 +881,7 @@ func (s SqlChannelStore) PermanentDelete(channelId string) store.StoreChannel {
|
|||
// Additionally propagate the deletion to the PublicChannels table.
|
||||
if _, err := transaction.Exec(`
|
||||
DELETE FROM
|
||||
PublicChannels
|
||||
PublicChannels
|
||||
WHERE
|
||||
Id = :ChannelId
|
||||
`, map[string]interface{}{
|
||||
|
|
@ -1689,7 +1689,7 @@ func (s SqlChannelStore) RemoveAllDeactivatedMembers(channelId string) store.Sto
|
|||
DELETE
|
||||
FROM
|
||||
ChannelMembers
|
||||
WHERE
|
||||
WHERE
|
||||
UserId IN (
|
||||
SELECT
|
||||
Id
|
||||
|
|
@ -1838,6 +1838,24 @@ func (s SqlChannelStore) GetAll(teamId string) store.StoreChannel {
|
|||
})
|
||||
}
|
||||
|
||||
func (s SqlChannelStore) GetChannelsByIds(channelIds []string) store.StoreChannel {
|
||||
return store.Do(func(result *store.StoreResult) {
|
||||
keys, params := MapStringsToQueryParams(channelIds, "Channel")
|
||||
|
||||
query := `SELECT * FROM Channels WHERE Id IN ` + keys + ` ORDER BY Name`
|
||||
|
||||
var channels []*model.Channel
|
||||
_, err := s.GetReplica().Select(&channels, query, params)
|
||||
|
||||
if err != nil {
|
||||
mlog.Error(fmt.Sprint(err))
|
||||
result.Err = model.NewAppError("SqlChannelStore.GetChannelsByIds", "store.sql_channel.get_channels_by_ids.app_error", nil, "", http.StatusInternalServerError)
|
||||
} else {
|
||||
result.Data = channels
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (s SqlChannelStore) GetForPost(postId string) store.StoreChannel {
|
||||
return store.Do(func(result *store.StoreResult) {
|
||||
channel := &model.Channel{}
|
||||
|
|
@ -1942,8 +1960,7 @@ func (s SqlChannelStore) AutocompleteInTeam(teamId string, term string, includeD
|
|||
c.TeamId = :TeamId
|
||||
` + deleteFilter + `
|
||||
%v
|
||||
LIMIT 50
|
||||
`
|
||||
LIMIT ` + strconv.Itoa(model.CHANNEL_SEARCH_DEFAULT_LIMIT)
|
||||
|
||||
var channels model.ChannelList
|
||||
|
||||
|
|
|
|||
|
|
@ -148,6 +148,7 @@ type ChannelStore interface {
|
|||
GetChannelCounts(teamId string, userId string) StoreChannel
|
||||
GetTeamChannels(teamId string) StoreChannel
|
||||
GetAll(teamId string) StoreChannel
|
||||
GetChannelsByIds(channelIds []string) StoreChannel
|
||||
GetForPost(postId string) StoreChannel
|
||||
SaveMember(member *model.ChannelMember) StoreChannel
|
||||
UpdateMember(member *model.ChannelMember) StoreChannel
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ package storetest
|
|||
|
||||
import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
|
@ -41,6 +42,7 @@ func TestChannelStore(t *testing.T, ss store.Store, s SqlSupplier) {
|
|||
t.Run("Update", func(t *testing.T) { testChannelStoreUpdate(t, ss) })
|
||||
t.Run("GetChannelUnread", func(t *testing.T) { testGetChannelUnread(t, ss) })
|
||||
t.Run("Get", func(t *testing.T) { testChannelStoreGet(t, ss, s) })
|
||||
t.Run("GetChannelsByIds", func(t *testing.T) { testChannelStoreGetChannelsByIds(t, ss) })
|
||||
t.Run("GetForPost", func(t *testing.T) { testChannelStoreGetForPost(t, ss) })
|
||||
t.Run("Restore", func(t *testing.T) { testChannelStoreRestore(t, ss) })
|
||||
t.Run("Delete", func(t *testing.T) { testChannelStoreDelete(t, ss) })
|
||||
|
|
@ -432,6 +434,73 @@ func testChannelStoreGet(t *testing.T, ss store.Store, s SqlSupplier) {
|
|||
s.GetMaster().Exec("TRUNCATE Channels")
|
||||
}
|
||||
|
||||
func testChannelStoreGetChannelsByIds(t *testing.T, ss store.Store) {
|
||||
o1 := model.Channel{}
|
||||
o1.TeamId = model.NewId()
|
||||
o1.DisplayName = "Name"
|
||||
o1.Name = "aa" + model.NewId() + "b"
|
||||
o1.Type = model.CHANNEL_OPEN
|
||||
store.Must(ss.Channel().Save(&o1, -1))
|
||||
|
||||
u1 := &model.User{}
|
||||
u1.Email = MakeEmail()
|
||||
u1.Nickname = model.NewId()
|
||||
store.Must(ss.User().Save(u1))
|
||||
store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: model.NewId(), UserId: u1.Id}, -1))
|
||||
|
||||
u2 := model.User{}
|
||||
u2.Email = MakeEmail()
|
||||
u2.Nickname = model.NewId()
|
||||
store.Must(ss.User().Save(&u2))
|
||||
store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: model.NewId(), UserId: u2.Id}, -1))
|
||||
|
||||
o2 := model.Channel{}
|
||||
o2.TeamId = model.NewId()
|
||||
o2.DisplayName = "Direct Name"
|
||||
o2.Name = "bb" + model.NewId() + "b"
|
||||
o2.Type = model.CHANNEL_DIRECT
|
||||
|
||||
m1 := model.ChannelMember{}
|
||||
m1.ChannelId = o2.Id
|
||||
m1.UserId = u1.Id
|
||||
m1.NotifyProps = model.GetDefaultChannelNotifyProps()
|
||||
|
||||
m2 := model.ChannelMember{}
|
||||
m2.ChannelId = o2.Id
|
||||
m2.UserId = u2.Id
|
||||
m2.NotifyProps = model.GetDefaultChannelNotifyProps()
|
||||
|
||||
store.Must(ss.Channel().SaveDirectChannel(&o2, &m1, &m2))
|
||||
|
||||
if r1 := <-ss.Channel().GetChannelsByIds([]string{o1.Id, o2.Id}); r1.Err != nil {
|
||||
t.Fatal(r1.Err)
|
||||
} else {
|
||||
cl := r1.Data.([]*model.Channel)
|
||||
if len(cl) != 2 {
|
||||
t.Fatal("invalid returned channels, expected 2 and got " + strconv.Itoa(len(cl)))
|
||||
}
|
||||
if cl[0].ToJson() != o1.ToJson() {
|
||||
t.Fatal("invalid returned channel")
|
||||
}
|
||||
if cl[1].ToJson() != o2.ToJson() {
|
||||
t.Fatal("invalid returned channel")
|
||||
}
|
||||
}
|
||||
|
||||
nonexistentId := "abcd1234"
|
||||
if r2 := <-ss.Channel().GetChannelsByIds([]string{o1.Id, nonexistentId}); r2.Err != nil {
|
||||
t.Fatal(r2.Err)
|
||||
} else {
|
||||
cl := r2.Data.([]*model.Channel)
|
||||
if len(cl) != 1 {
|
||||
t.Fatal("invalid returned channels, expected 1 and got " + strconv.Itoa(len(cl)))
|
||||
}
|
||||
if cl[0].ToJson() != o1.ToJson() {
|
||||
t.Fatal("invalid returned channel")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testChannelStoreGetForPost(t *testing.T, ss store.Store) {
|
||||
o1 := store.Must(ss.Channel().Save(&model.Channel{
|
||||
TeamId: model.NewId(),
|
||||
|
|
|
|||
|
|
@ -418,6 +418,22 @@ func (_m *ChannelStore) GetDeletedByName(team_id string, name string) store.Stor
|
|||
return r0
|
||||
}
|
||||
|
||||
// GetChannelsByIds provides a mock funcion with given fields: channelIds
|
||||
func (_m *ChannelStore) GetChannelsByIds(channelIds []string) store.StoreChannel {
|
||||
ret := _m.Called(channelIds)
|
||||
|
||||
var r0 store.StoreChannel
|
||||
if rf, ok := ret.Get(0).(func([]string) store.StoreChannel); ok {
|
||||
r0 = rf(channelIds)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(store.StoreChannel)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// GetForPost provides a mock function with given fields: postId
|
||||
func (_m *ChannelStore) GetForPost(postId string) store.StoreChannel {
|
||||
ret := _m.Called(postId)
|
||||
|
|
|
|||
|
|
@ -344,9 +344,14 @@
|
|||
"Password": "changeme",
|
||||
"EnableIndexing": false,
|
||||
"EnableSearching": false,
|
||||
"EnableAutocomplete": false,
|
||||
"Sniff": true,
|
||||
"PostIndexReplicas": 1,
|
||||
"PostIndexShards": 1,
|
||||
"ChannelIndexReplicas": 1,
|
||||
"ChannelIndexShards": 1,
|
||||
"UserIndexReplicas": 1,
|
||||
"UserIndexShards": 1,
|
||||
"AggregatePostsAfterDays": 365,
|
||||
"PostsAggregatorJobStartTime": "03:00",
|
||||
"IndexPrefix": "",
|
||||
|
|
|
|||
|
|
@ -52,6 +52,22 @@ func RemoveDuplicatesFromStringArray(arr []string) []string {
|
|||
return result
|
||||
}
|
||||
|
||||
func StringSliceDiff(a, b []string) []string {
|
||||
m := make(map[string]bool)
|
||||
result := []string{}
|
||||
|
||||
for _, item := range b {
|
||||
m[item] = true
|
||||
}
|
||||
|
||||
for _, item := range a {
|
||||
if !m[item] {
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func GetIpAddress(r *http.Request) string {
|
||||
address := ""
|
||||
|
||||
|
|
|
|||
|
|
@ -48,6 +48,14 @@ func TestRemoveDuplicatesFromStringArray(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestStringSliceDiff(t *testing.T) {
|
||||
a := []string{"one", "two", "three", "four", "five", "six"}
|
||||
b := []string{"two", "seven", "four", "six"}
|
||||
expected := []string{"one", "three", "five"}
|
||||
|
||||
assert.Equal(t, StringSliceDiff(a, b), expected)
|
||||
}
|
||||
|
||||
func TestGetIpAddress(t *testing.T) {
|
||||
// Test with a single IP in the X-Forwarded-For
|
||||
httpRequest1 := http.Request{
|
||||
|
|
|
|||
Loading…
Reference in a new issue