mirror of
https://github.com/mattermost/mattermost.git
synced 2026-05-28 04:35:04 -04:00
MM-63298: [AI assisted] Elasticsearch add a global search prefix (#30417)
This PR adds functionality to search by a global search prefix. This allows Mattermost to be used across multiple data centers with multiple Elasticsearch instances synchronized using cross-cluster replication. While here, we also add tests cases to cover for some missing search interface methods. For now, no system console setting is exposed. Because IndexPrefix is also not exposed. It can be added later if a need arises. https://mattermost.atlassian.net/browse/MM-63298 ```release-note A new config setting ElasticsearchSettings.GlobalSearchPrefix is added which can be used to search across multiple indices having a common prefix. This is useful in a scenario with multiple Elasticsearch instances, where multiple instances are writing to different indices with different prefixes using the ElasticsearchSettings.IndexPrefix setting. ``` Co-authored-by: Mattermost Build <build@mattermost.com>
This commit is contained in:
parent
7250095f86
commit
09488558a0
7 changed files with 488 additions and 23 deletions
|
|
@ -31,12 +31,6 @@ type SearchTestHelper struct {
|
|||
}
|
||||
|
||||
func (th *SearchTestHelper) SetupBasicFixtures() error {
|
||||
// Remove users from previous tests
|
||||
err := th.cleanAllUsers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create teams
|
||||
team, err := th.createTeam("searchtest-team", "Searchtest team", model.TeamOpen)
|
||||
if err != nil {
|
||||
|
|
@ -140,7 +134,7 @@ func (th *SearchTestHelper) CleanFixtures() error {
|
|||
return err
|
||||
}
|
||||
|
||||
err = th.cleanAllUsers()
|
||||
err = th.cleanAllUsers([]*model.User{th.User, th.User2, th.UserAnotherTeam})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -202,12 +196,7 @@ func (th *SearchTestHelper) deleteBotUser(botID string) error {
|
|||
return th.Store.User().PermanentDelete(th.Context, botID)
|
||||
}
|
||||
|
||||
func (th *SearchTestHelper) cleanAllUsers() error {
|
||||
users, err := th.Store.User().GetAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
func (th *SearchTestHelper) cleanAllUsers(users []*model.User) error {
|
||||
for _, u := range users {
|
||||
err := th.deleteUser(u)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -265,6 +265,17 @@ func ESUserFromUserForIndexing(userForIndexing *model.UserForIndexing) *ESUser {
|
|||
return ESUserFromUserAndTeams(user, userForIndexing.TeamsIds, userForIndexing.ChannelsIds)
|
||||
}
|
||||
|
||||
// SearchIndexName returns the index pattern to search for a given index name.
|
||||
func SearchIndexName(settings model.ElasticsearchSettings, name string) string {
|
||||
if *settings.GlobalSearchPrefix == "" {
|
||||
return *settings.IndexPrefix + name
|
||||
}
|
||||
|
||||
// GlobalSearchPrefix is a prefix of IndexPrefix itself. This is verified in the config.
|
||||
// Therefore, we use * to search across all indices with the common search prefix.
|
||||
return *settings.GlobalSearchPrefix + "*" + name
|
||||
}
|
||||
|
||||
func BuildPostIndexName(aggregateAfterDays int, unaggregatedBase string, aggregatedBase string, now time.Time, createAt int64) string {
|
||||
postTime := time.Unix(createAt/1000, 0)
|
||||
aggregateCutoffTime := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local).AddDate(0, 0, -aggregateAfterDays+1)
|
||||
|
|
|
|||
|
|
@ -381,6 +381,190 @@ func (c *CommonTestSuite) TestIndexUser() {
|
|||
c.True(found)
|
||||
}
|
||||
|
||||
func (c *CommonTestSuite) TestSearchUsersInChannel() {
|
||||
// Create test channels
|
||||
channel1 := createChannel(c.TH.BasicTeam.Id, "channel1", "Test Channel 1", model.ChannelTypeOpen)
|
||||
c.Nil(c.ESImpl.IndexChannel(c.TH.Context, channel1, []string{}, []string{}))
|
||||
|
||||
channel2 := createChannel(c.TH.BasicTeam.Id, "channel2", "Test Channel 2", model.ChannelTypeOpen)
|
||||
c.Nil(c.ESImpl.IndexChannel(c.TH.Context, channel2, []string{}, []string{}))
|
||||
|
||||
// Create and index users with different channel memberships
|
||||
user1 := createUser("test.user1", "testuser1", "Test", "User1")
|
||||
c.Nil(c.ESImpl.IndexUser(c.TH.Context, user1, []string{c.TH.BasicTeam.Id}, []string{channel1.Id}))
|
||||
|
||||
user2 := createUser("test.user2", "testuser2", "Test", "User2")
|
||||
c.Nil(c.ESImpl.IndexUser(c.TH.Context, user2, []string{c.TH.BasicTeam.Id}, []string{channel1.Id, channel2.Id}))
|
||||
|
||||
user3 := createUser("test.user3", "testuser3", "Another", "User3")
|
||||
c.Nil(c.ESImpl.IndexUser(c.TH.Context, user3, []string{c.TH.BasicTeam.Id}, []string{channel2.Id}))
|
||||
|
||||
// Wait for indexing to complete
|
||||
c.NoError(c.RefreshIndexFn())
|
||||
|
||||
// Search options
|
||||
options := &model.UserSearchOptions{
|
||||
AllowFullNames: true,
|
||||
Limit: 100,
|
||||
}
|
||||
|
||||
c.Run("All users in channel1", func() {
|
||||
// Test 1: Search for all users in channel1
|
||||
inChannel, notInChannel, err := c.ESImpl.SearchUsersInChannel(c.TH.BasicTeam.Id, channel1.Id, nil, "", options)
|
||||
c.Nil(err)
|
||||
c.Len(inChannel, 2)
|
||||
c.Contains(inChannel, user1.Id)
|
||||
c.Contains(inChannel, user2.Id)
|
||||
c.Len(notInChannel, 1)
|
||||
c.Contains(notInChannel, user3.Id)
|
||||
})
|
||||
|
||||
c.Run("Search for specific user in channel1", func() {
|
||||
// Test 2: Search with term that should match user1 in channel1
|
||||
inChannel, notInChannel, err := c.ESImpl.SearchUsersInChannel(c.TH.BasicTeam.Id, channel1.Id, nil, "testuser1", options)
|
||||
c.Nil(err)
|
||||
c.Len(inChannel, 1)
|
||||
c.Contains(inChannel, user1.Id)
|
||||
c.Empty(notInChannel)
|
||||
})
|
||||
|
||||
c.Run("Search with restricted channels", func() {
|
||||
// Test 3: Search with restrictedToChannels, user3 should be in notInChannel
|
||||
restrictedToChannels := []string{channel2.Id}
|
||||
inChannel, notInChannel, err := c.ESImpl.SearchUsersInChannel(c.TH.BasicTeam.Id, channel1.Id, restrictedToChannels, "", options)
|
||||
c.Nil(err)
|
||||
c.Len(inChannel, 2)
|
||||
c.Contains(inChannel, user1.Id)
|
||||
c.Contains(inChannel, user2.Id)
|
||||
c.Len(notInChannel, 1)
|
||||
c.Contains(notInChannel, user3.Id) // user3 is in channel2 but not channel1
|
||||
})
|
||||
|
||||
c.Run("Search with term in restricted channels", func() {
|
||||
// Test 4: Search with a term in restrictedToChannels
|
||||
restrictedToChannels := []string{channel2.Id}
|
||||
inChannel, notInChannel, err := c.ESImpl.SearchUsersInChannel(c.TH.BasicTeam.Id, channel1.Id, restrictedToChannels, "another", options)
|
||||
c.Nil(err)
|
||||
c.Empty(inChannel) // No users in channel1 match "another"
|
||||
c.Len(notInChannel, 1)
|
||||
c.Contains(notInChannel, user3.Id) // user3's name contains "Another" and is in channel2
|
||||
})
|
||||
|
||||
c.Run("Search with empty restricted channels", func() {
|
||||
// Test 5: Search with restrictedToChannels but empty (should return no results)
|
||||
emptyRestricted := []string{}
|
||||
inChannel, notInChannel, err := c.ESImpl.SearchUsersInChannel(c.TH.BasicTeam.Id, channel1.Id, emptyRestricted, "", options)
|
||||
c.Nil(err)
|
||||
c.Empty(inChannel)
|
||||
c.Empty(notInChannel)
|
||||
})
|
||||
|
||||
// Clean up
|
||||
c.Nil(c.ESImpl.DeleteUser(user1))
|
||||
c.Nil(c.ESImpl.DeleteUser(user2))
|
||||
c.Nil(c.ESImpl.DeleteUser(user3))
|
||||
c.Nil(c.ESImpl.DeleteChannel(channel1))
|
||||
c.Nil(c.ESImpl.DeleteChannel(channel2))
|
||||
}
|
||||
|
||||
func (c *CommonTestSuite) TestSearchUsersInTeam() {
|
||||
// Create additional teams
|
||||
team1 := c.TH.CreateTeam()
|
||||
team2 := c.TH.CreateTeam()
|
||||
|
||||
// Create and index users with different team memberships
|
||||
user1 := createUser("test.user1", "testuser1", "Test", "User1")
|
||||
c.Nil(c.ESImpl.IndexUser(c.TH.Context, user1, []string{team1.Id}, []string{}))
|
||||
|
||||
user2 := createUser("test.user2", "testuser2", "Test", "User2")
|
||||
c.Nil(c.ESImpl.IndexUser(c.TH.Context, user2, []string{team1.Id, team2.Id}, []string{}))
|
||||
|
||||
user3 := createUser("test.user3", "testuser3", "Another", "User3")
|
||||
c.Nil(c.ESImpl.IndexUser(c.TH.Context, user3, []string{team2.Id}, []string{}))
|
||||
|
||||
// Wait for indexing to complete
|
||||
c.NoError(c.RefreshIndexFn())
|
||||
|
||||
// Search options
|
||||
options := &model.UserSearchOptions{
|
||||
AllowFullNames: true,
|
||||
Limit: 100,
|
||||
}
|
||||
|
||||
c.Run("Search for all users in team1", func() {
|
||||
// Test 1: Search for all users in team1
|
||||
userIds, err := c.ESImpl.SearchUsersInTeam(team1.Id, nil, "testuser", options)
|
||||
c.Nil(err)
|
||||
c.Len(userIds, 2)
|
||||
c.Contains(userIds, user1.Id)
|
||||
c.Contains(userIds, user2.Id)
|
||||
c.NotContains(userIds, user3.Id)
|
||||
})
|
||||
|
||||
c.Run("Search for specific user in team1", func() {
|
||||
// Test 2: Search with term that should match user1 in team1
|
||||
userIds, err := c.ESImpl.SearchUsersInTeam(team1.Id, nil, "testuser1", options)
|
||||
c.Nil(err)
|
||||
c.Len(userIds, 1)
|
||||
c.Contains(userIds, user1.Id)
|
||||
c.NotContains(userIds, user2.Id)
|
||||
c.NotContains(userIds, user3.Id)
|
||||
})
|
||||
|
||||
c.Run("Search in team2", func() {
|
||||
// Test 3: Search in team2
|
||||
userIds, err := c.ESImpl.SearchUsersInTeam(team2.Id, nil, "testuser", options)
|
||||
c.Nil(err)
|
||||
c.Len(userIds, 2)
|
||||
c.Contains(userIds, user2.Id)
|
||||
c.Contains(userIds, user3.Id)
|
||||
c.NotContains(userIds, user1.Id)
|
||||
})
|
||||
|
||||
c.Run("Search with term in team2", func() {
|
||||
// Test 4: Search with term in team2
|
||||
userIds, err := c.ESImpl.SearchUsersInTeam(team2.Id, nil, "another", options)
|
||||
c.Nil(err)
|
||||
c.Len(userIds, 1)
|
||||
c.Contains(userIds, user3.Id)
|
||||
c.NotContains(userIds, user1.Id)
|
||||
c.NotContains(userIds, user2.Id)
|
||||
})
|
||||
|
||||
c.Run("Search with restrictedToChannels", func() {
|
||||
// Test 5: Search with restrictedToChannels
|
||||
// Create channel in team1
|
||||
channel1 := createChannel(team1.Id, "channel1", "Test Channel 1", model.ChannelTypeOpen)
|
||||
c.Nil(c.ESImpl.IndexChannel(c.TH.Context, channel1, []string{}, []string{}))
|
||||
|
||||
// Update user1 to be in channel1
|
||||
c.Nil(c.ESImpl.IndexUser(c.TH.Context, user1, []string{team1.Id}, []string{channel1.Id}))
|
||||
c.NoError(c.RefreshIndexFn())
|
||||
|
||||
// Search for users in team1 restricted to channel1
|
||||
userIds, err := c.ESImpl.SearchUsersInTeam(team1.Id, []string{channel1.Id}, "", options)
|
||||
c.Nil(err)
|
||||
c.Len(userIds, 1)
|
||||
c.Contains(userIds, user1.Id)
|
||||
c.NotContains(userIds, user2.Id)
|
||||
c.NotContains(userIds, user3.Id)
|
||||
c.Nil(c.ESImpl.DeleteChannel(channel1))
|
||||
})
|
||||
|
||||
c.Run("Search with empty restrictedToChannels", func() {
|
||||
// Test 6: Search with empty restrictedToChannels (should return no results)
|
||||
emptyRestricted := []string{}
|
||||
userIds, err := c.ESImpl.SearchUsersInTeam(team1.Id, emptyRestricted, "", options)
|
||||
c.Nil(err)
|
||||
c.Empty(userIds)
|
||||
})
|
||||
|
||||
// Clean up
|
||||
c.Nil(c.ESImpl.DeleteUser(user1))
|
||||
c.Nil(c.ESImpl.DeleteUser(user2))
|
||||
c.Nil(c.ESImpl.DeleteUser(user3))
|
||||
}
|
||||
|
||||
func (c *CommonTestSuite) TestDeleteUser() {
|
||||
// Create and index a user
|
||||
user := createUser("test.user", "testuser", "Test", "User")
|
||||
|
|
@ -553,6 +737,264 @@ func (c *CommonTestSuite) TestDeletePostFiles() {
|
|||
c.False(found)
|
||||
}
|
||||
|
||||
func (c *CommonTestSuite) TestSearchFiles() {
|
||||
// First, create and index a channel
|
||||
channel := createChannel(c.TH.BasicTeam.Id, "channel", "Test Channel", model.ChannelTypeOpen)
|
||||
c.Nil(c.ESImpl.IndexChannel(c.TH.Context, channel, []string{}, []string{}))
|
||||
|
||||
// Then, create and index a user
|
||||
user := createUser("test.user", "testuser", "Test", "User")
|
||||
c.Nil(c.ESImpl.IndexUser(c.TH.Context, user, []string{c.TH.BasicTeam.Id}, []string{channel.Id}))
|
||||
|
||||
// Create multiple test files with different content
|
||||
file1 := createFile(user.Id, channel.Id, "", "apple document content", "apple_report", "txt")
|
||||
c.Nil(c.ESImpl.IndexFile(file1, channel.Id))
|
||||
|
||||
file2 := createFile(user.Id, channel.Id, "", "orange presentation content", "orange_slides", "pdf")
|
||||
c.Nil(c.ESImpl.IndexFile(file2, channel.Id))
|
||||
|
||||
file3 := createFile(user.Id, channel.Id, "", "banana data content", "banana_sheet", "xls")
|
||||
c.Nil(c.ESImpl.IndexFile(file3, channel.Id))
|
||||
|
||||
// Wait for indexing to complete
|
||||
c.NoError(c.RefreshIndexFn())
|
||||
|
||||
// Create channel list for search
|
||||
channels := model.ChannelList{channel}
|
||||
|
||||
c.Run("Search by term (file name and content)", func() {
|
||||
// Test 1: Search by term (file name and content)
|
||||
searchParams := []*model.SearchParams{
|
||||
{
|
||||
Terms: "apple",
|
||||
IsHashtag: false,
|
||||
OrTerms: false,
|
||||
},
|
||||
}
|
||||
|
||||
fileIds, err := c.ESImpl.SearchFiles(channels, searchParams, 0, 10)
|
||||
c.Nil(err)
|
||||
c.Contains(fileIds, file1.Id)
|
||||
c.NotContains(fileIds, file2.Id)
|
||||
c.NotContains(fileIds, file3.Id)
|
||||
})
|
||||
|
||||
c.Run("Search by extension", func() {
|
||||
// Test 2: Search by extension
|
||||
searchParams := []*model.SearchParams{
|
||||
{
|
||||
Terms: "",
|
||||
IsHashtag: false,
|
||||
OrTerms: false,
|
||||
Extensions: []string{"pdf"},
|
||||
},
|
||||
}
|
||||
|
||||
fileIds, err := c.ESImpl.SearchFiles(channels, searchParams, 0, 10)
|
||||
c.Nil(err)
|
||||
c.NotContains(fileIds, file1.Id)
|
||||
c.Contains(fileIds, file2.Id)
|
||||
c.NotContains(fileIds, file3.Id)
|
||||
})
|
||||
|
||||
c.Run("Search with OR terms", func() {
|
||||
// Test 3: Search with OR terms
|
||||
searchParams := []*model.SearchParams{
|
||||
{
|
||||
Terms: "apple banana",
|
||||
IsHashtag: false,
|
||||
OrTerms: true,
|
||||
},
|
||||
}
|
||||
|
||||
fileIds, err := c.ESImpl.SearchFiles(channels, searchParams, 0, 10)
|
||||
c.Nil(err)
|
||||
c.Contains(fileIds, file1.Id)
|
||||
c.NotContains(fileIds, file2.Id)
|
||||
c.Contains(fileIds, file3.Id)
|
||||
})
|
||||
|
||||
c.Run("Search with excluded terms", func() {
|
||||
// Test 4: Search with excluded terms
|
||||
searchParams := []*model.SearchParams{
|
||||
{
|
||||
Terms: "content",
|
||||
ExcludedTerms: "orange",
|
||||
IsHashtag: false,
|
||||
OrTerms: false,
|
||||
},
|
||||
}
|
||||
|
||||
fileIds, err := c.ESImpl.SearchFiles(channels, searchParams, 0, 10)
|
||||
c.Nil(err)
|
||||
c.Contains(fileIds, file1.Id)
|
||||
c.NotContains(fileIds, file2.Id)
|
||||
c.Contains(fileIds, file3.Id)
|
||||
})
|
||||
|
||||
// Clean up indexed files
|
||||
c.Nil(c.ESImpl.DeleteFile(file1.Id))
|
||||
c.Nil(c.ESImpl.DeleteFile(file2.Id))
|
||||
c.Nil(c.ESImpl.DeleteFile(file3.Id))
|
||||
}
|
||||
|
||||
func (c *CommonTestSuite) TestSearchMultiDC() {
|
||||
// Store original settings to restore later
|
||||
originalIndexPrefix := *c.TH.App.Config().ElasticsearchSettings.IndexPrefix
|
||||
originalGlobalSearchPrefix := *c.TH.App.Config().ElasticsearchSettings.GlobalSearchPrefix
|
||||
|
||||
defer c.TH.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.ElasticsearchSettings.IndexPrefix = originalIndexPrefix
|
||||
*cfg.ElasticsearchSettings.GlobalSearchPrefix = originalGlobalSearchPrefix
|
||||
})
|
||||
|
||||
// First using DC1 prefix
|
||||
c.TH.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.ElasticsearchSettings.IndexPrefix = "test_dc1_"
|
||||
*cfg.ElasticsearchSettings.GlobalSearchPrefix = ""
|
||||
})
|
||||
|
||||
// Create a post with content specific to DC1
|
||||
postDC1 := createPost(c.TH.BasicUser.Id, c.TH.BasicChannel.Id, "unique apple datacenter1")
|
||||
c.Nil(c.ESImpl.IndexPost(postDC1, c.TH.BasicTeam.Id))
|
||||
|
||||
// Now switch to DC2 prefix
|
||||
c.TH.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.ElasticsearchSettings.IndexPrefix = "test_dc2_"
|
||||
*cfg.ElasticsearchSettings.GlobalSearchPrefix = ""
|
||||
})
|
||||
|
||||
// Create a post with content specific to DC2
|
||||
postDC2 := createPost(c.TH.BasicUser.Id, c.TH.BasicChannel.Id, "unique banana datacenter2")
|
||||
c.Nil(c.ESImpl.IndexPost(postDC2, c.TH.BasicTeam.Id))
|
||||
|
||||
// Ensure posts are indexed
|
||||
c.NoError(c.RefreshIndexFn())
|
||||
|
||||
channels := model.ChannelList{c.TH.BasicChannel}
|
||||
|
||||
// First verify each prefix only finds its own posts
|
||||
c.Run("DC1 prefix only finds DC1 post", func() {
|
||||
c.TH.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.ElasticsearchSettings.IndexPrefix = "test_dc1_"
|
||||
*cfg.ElasticsearchSettings.GlobalSearchPrefix = ""
|
||||
})
|
||||
|
||||
// Search for common term
|
||||
searchParams := []*model.SearchParams{
|
||||
{
|
||||
Terms: "unique",
|
||||
IsHashtag: false,
|
||||
OrTerms: false,
|
||||
},
|
||||
}
|
||||
|
||||
postIds, _, err := c.ESImpl.SearchPosts(channels, searchParams, 0, 20)
|
||||
c.Nil(err)
|
||||
c.Contains(postIds, postDC1.Id)
|
||||
c.NotContains(postIds, postDC2.Id)
|
||||
})
|
||||
|
||||
c.Run("DC2 prefix only finds DC2 post", func() {
|
||||
c.TH.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.ElasticsearchSettings.IndexPrefix = "test_dc2_"
|
||||
*cfg.ElasticsearchSettings.GlobalSearchPrefix = ""
|
||||
})
|
||||
|
||||
// Search for common term
|
||||
searchParams := []*model.SearchParams{
|
||||
{
|
||||
Terms: "unique",
|
||||
IsHashtag: false,
|
||||
OrTerms: false,
|
||||
},
|
||||
}
|
||||
|
||||
postIds, _, err := c.ESImpl.SearchPosts(channels, searchParams, 0, 20)
|
||||
c.Nil(err)
|
||||
c.Contains(postIds, postDC2.Id)
|
||||
c.NotContains(postIds, postDC1.Id)
|
||||
})
|
||||
|
||||
c.Run("Global prefix finds posts from both DCs", func() {
|
||||
// Set global search prefix to search across both indices
|
||||
c.TH.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.ElasticsearchSettings.IndexPrefix = "test_dc1_" // Specific prefix doesn't matter for this test
|
||||
*cfg.ElasticsearchSettings.GlobalSearchPrefix = "test_"
|
||||
})
|
||||
|
||||
// Search for common term - should find both posts
|
||||
searchParams := []*model.SearchParams{
|
||||
{
|
||||
Terms: "unique",
|
||||
IsHashtag: false,
|
||||
OrTerms: false,
|
||||
},
|
||||
}
|
||||
|
||||
postIds, _, err := c.ESImpl.SearchPosts(channels, searchParams, 0, 20)
|
||||
c.Nil(err)
|
||||
c.Len(postIds, 2)
|
||||
c.Contains(postIds, postDC1.Id)
|
||||
c.Contains(postIds, postDC2.Id)
|
||||
|
||||
// Search for DC1-specific content
|
||||
searchParams = []*model.SearchParams{
|
||||
{
|
||||
Terms: "apple",
|
||||
IsHashtag: false,
|
||||
OrTerms: false,
|
||||
},
|
||||
}
|
||||
|
||||
postIds, _, err = c.ESImpl.SearchPosts(channels, searchParams, 0, 20)
|
||||
c.Nil(err)
|
||||
c.Contains(postIds, postDC1.Id)
|
||||
c.NotContains(postIds, postDC2.Id)
|
||||
|
||||
// Search for DC2-specific content
|
||||
searchParams = []*model.SearchParams{
|
||||
{
|
||||
Terms: "banana",
|
||||
IsHashtag: false,
|
||||
OrTerms: false,
|
||||
},
|
||||
}
|
||||
|
||||
postIds, _, err = c.ESImpl.SearchPosts(channels, searchParams, 0, 20)
|
||||
c.Nil(err)
|
||||
c.Contains(postIds, postDC2.Id)
|
||||
c.NotContains(postIds, postDC1.Id)
|
||||
|
||||
// Search for datacenter-specific content
|
||||
searchParams = []*model.SearchParams{
|
||||
{
|
||||
Terms: "datacenter1",
|
||||
IsHashtag: false,
|
||||
OrTerms: false,
|
||||
},
|
||||
}
|
||||
|
||||
postIds, _, err = c.ESImpl.SearchPosts(channels, searchParams, 0, 20)
|
||||
c.Nil(err)
|
||||
c.Contains(postIds, postDC1.Id)
|
||||
c.NotContains(postIds, postDC2.Id)
|
||||
|
||||
searchParams = []*model.SearchParams{
|
||||
{
|
||||
Terms: "datacenter2",
|
||||
IsHashtag: false,
|
||||
OrTerms: false,
|
||||
},
|
||||
}
|
||||
|
||||
postIds, _, err = c.ESImpl.SearchPosts(channels, searchParams, 0, 20)
|
||||
c.Nil(err)
|
||||
c.Contains(postIds, postDC2.Id)
|
||||
c.NotContains(postIds, postDC1.Id)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *CommonTestSuite) TestElasticsearchDataRetentionDeleteIndexes() {
|
||||
c.Nil(c.CreateIndexFn("posts_2017_09_15"))
|
||||
c.Nil(c.CreateIndexFn("posts_2017_09_16"))
|
||||
|
|
|
|||
|
|
@ -549,7 +549,7 @@ func (es *ElasticsearchInterfaceImpl) SearchPosts(channels model.ChannelList, se
|
|||
}
|
||||
|
||||
search := es.client.Search().
|
||||
Index(*es.Platform.Config().ElasticsearchSettings.IndexPrefix + common.IndexBasePosts + "*").
|
||||
Index(common.SearchIndexName(es.Platform.Config().ElasticsearchSettings, common.IndexBasePosts+"*")).
|
||||
Request(&search.Request{
|
||||
Query: query,
|
||||
Highlight: highlight,
|
||||
|
|
@ -822,7 +822,7 @@ func (es *ElasticsearchInterfaceImpl) SearchChannels(teamId, userID string, term
|
|||
}
|
||||
|
||||
search := es.client.Search().
|
||||
Index(*es.Platform.Config().ElasticsearchSettings.IndexPrefix + common.IndexBaseChannels).
|
||||
Index(common.SearchIndexName(es.Platform.Config().ElasticsearchSettings, common.IndexBaseChannels)).
|
||||
Request(&search.Request{
|
||||
Query: &types.Query{Bool: query},
|
||||
}).
|
||||
|
|
@ -996,7 +996,7 @@ func (es *ElasticsearchInterfaceImpl) autocompleteUsers(contextCategory string,
|
|||
}
|
||||
|
||||
search := es.client.Search().
|
||||
Index(*es.Platform.Config().ElasticsearchSettings.IndexPrefix + common.IndexBaseUsers).
|
||||
Index(common.SearchIndexName(es.Platform.Config().ElasticsearchSettings, common.IndexBaseUsers)).
|
||||
Request(&search.Request{
|
||||
Query: &types.Query{Bool: query},
|
||||
}).
|
||||
|
|
@ -1116,7 +1116,7 @@ func (es *ElasticsearchInterfaceImpl) autocompleteUsersNotInChannel(teamId, chan
|
|||
}
|
||||
|
||||
search := es.client.Search().
|
||||
Index(*es.Platform.Config().ElasticsearchSettings.IndexPrefix + common.IndexBaseUsers).
|
||||
Index(common.SearchIndexName(es.Platform.Config().ElasticsearchSettings, common.IndexBaseUsers)).
|
||||
Request(&search.Request{
|
||||
Query: &types.Query{Bool: query},
|
||||
}).
|
||||
|
|
@ -1664,7 +1664,7 @@ func (es *ElasticsearchInterfaceImpl) SearchFiles(channels model.ChannelList, se
|
|||
}
|
||||
|
||||
search := es.client.Search().
|
||||
Index(*es.Platform.Config().ElasticsearchSettings.IndexPrefix + common.IndexBaseFiles).
|
||||
Index(common.SearchIndexName(es.Platform.Config().ElasticsearchSettings, common.IndexBaseFiles)).
|
||||
Request(&search.Request{
|
||||
Query: query,
|
||||
}).
|
||||
|
|
|
|||
|
|
@ -618,7 +618,7 @@ func (os *OpensearchInterfaceImpl) SearchPosts(channels model.ChannelList, searc
|
|||
|
||||
var searchResult searchResp
|
||||
_, err = os.client.Client.Do(ctx, &opensearchapi.SearchReq{
|
||||
Indices: []string{*os.Platform.Config().ElasticsearchSettings.IndexPrefix + common.IndexBasePosts + "*"},
|
||||
Indices: []string{common.SearchIndexName(os.Platform.Config().ElasticsearchSettings, common.IndexBasePosts+"*")},
|
||||
Body: bytes.NewReader(searchBuf),
|
||||
Params: opensearchapi.SearchParams{
|
||||
From: model.NewPointer(page * perPage),
|
||||
|
|
@ -910,7 +910,7 @@ func (os *OpensearchInterfaceImpl) SearchChannels(teamId, userID string, term st
|
|||
return []string{}, model.NewAppError("Opensearch.SearchChannels", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
searchResult, err := os.client.Search(ctx, &opensearchapi.SearchReq{
|
||||
Indices: []string{*os.Platform.Config().ElasticsearchSettings.IndexPrefix + common.IndexBaseChannels},
|
||||
Indices: []string{common.SearchIndexName(os.Platform.Config().ElasticsearchSettings, common.IndexBaseChannels)},
|
||||
Body: bytes.NewReader(buf),
|
||||
Params: opensearchapi.SearchParams{
|
||||
Size: model.NewPointer(model.ChannelSearchDefaultLimit),
|
||||
|
|
@ -1097,7 +1097,7 @@ func (os *OpensearchInterfaceImpl) autocompleteUsers(contextCategory string, cat
|
|||
}
|
||||
|
||||
searchResults, err := os.client.Search(ctx, &opensearchapi.SearchReq{
|
||||
Indices: []string{*os.Platform.Config().ElasticsearchSettings.IndexPrefix + common.IndexBaseUsers},
|
||||
Indices: []string{common.SearchIndexName(os.Platform.Config().ElasticsearchSettings, common.IndexBaseUsers)},
|
||||
Body: bytes.NewReader(buf),
|
||||
Params: opensearchapi.SearchParams{
|
||||
Size: model.NewPointer(options.Limit),
|
||||
|
|
@ -1223,7 +1223,7 @@ func (os *OpensearchInterfaceImpl) autocompleteUsersNotInChannel(teamId, channel
|
|||
}
|
||||
|
||||
searchResults, err := os.client.Search(ctx, &opensearchapi.SearchReq{
|
||||
Indices: []string{*os.Platform.Config().ElasticsearchSettings.IndexPrefix + common.IndexBaseUsers},
|
||||
Indices: []string{common.SearchIndexName(os.Platform.Config().ElasticsearchSettings, common.IndexBaseUsers)},
|
||||
Body: bytes.NewReader(buf),
|
||||
Params: opensearchapi.SearchParams{
|
||||
Size: model.NewPointer(options.Limit),
|
||||
|
|
@ -1798,7 +1798,7 @@ func (os *OpensearchInterfaceImpl) SearchFiles(channels model.ChannelList, searc
|
|||
}
|
||||
|
||||
searchResult, err := os.client.Search(ctx, &opensearchapi.SearchReq{
|
||||
Indices: []string{*os.Platform.Config().ElasticsearchSettings.IndexPrefix + common.IndexBaseFiles},
|
||||
Indices: []string{common.SearchIndexName(os.Platform.Config().ElasticsearchSettings, common.IndexBaseFiles)},
|
||||
Body: bytes.NewReader(searchBuf),
|
||||
Params: opensearchapi.SearchParams{
|
||||
From: model.NewPointer(page * perPage),
|
||||
|
|
|
|||
|
|
@ -8952,6 +8952,10 @@
|
|||
"id": "model.config.is_valid.elastic_search.connection_url.app_error",
|
||||
"translation": "Search ConnectionUrl setting must be provided when indexing is enabled."
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.elastic_search.empty_index_prefix.app_error",
|
||||
"translation": "IndexPrefix cannot be empty if GlobalSearchPrefix is set."
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.elastic_search.enable_autocomplete.app_error",
|
||||
"translation": "{{.EnableIndexing}} setting must be set to true when {{.Autocomplete}} is set to true"
|
||||
|
|
@ -8964,6 +8968,10 @@
|
|||
"id": "model.config.is_valid.elastic_search.ignored_indexes_dash_prefix.app_error",
|
||||
"translation": "Ignored indexes for purge should not start with dash."
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.elastic_search.incorrect_search_prefix.app_error",
|
||||
"translation": "GlobalSearchPrefix {{.GlobalSearchPrefix}} should be a prefix of IndexPrefix {{.IndexPrefix}}."
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.elastic_search.invalid_backend.app_error",
|
||||
"translation": "Invalid search backend. Must be either elasticsearch or opensearch."
|
||||
|
|
|
|||
|
|
@ -2916,6 +2916,7 @@ type ElasticsearchSettings struct {
|
|||
AggregatePostsAfterDays *int `access:"environment_elasticsearch,write_restrictable,cloud_restrictable"` // telemetry: none
|
||||
PostsAggregatorJobStartTime *string `access:"environment_elasticsearch,write_restrictable,cloud_restrictable"` // telemetry: none
|
||||
IndexPrefix *string `access:"environment_elasticsearch,write_restrictable,cloud_restrictable"`
|
||||
GlobalSearchPrefix *string `access:"environment_elasticsearch,write_restrictable,cloud_restrictable"`
|
||||
LiveIndexingBatchSize *int `access:"environment_elasticsearch,write_restrictable,cloud_restrictable"`
|
||||
BulkIndexingTimeWindowSeconds *int `json:",omitempty"` // telemetry: none
|
||||
BatchSize *int `access:"environment_elasticsearch,write_restrictable,cloud_restrictable"`
|
||||
|
|
@ -3009,6 +3010,10 @@ func (s *ElasticsearchSettings) SetDefaults() {
|
|||
s.IndexPrefix = NewPointer(ElasticsearchSettingsDefaultIndexPrefix)
|
||||
}
|
||||
|
||||
if s.GlobalSearchPrefix == nil {
|
||||
s.GlobalSearchPrefix = NewPointer("")
|
||||
}
|
||||
|
||||
if s.LiveIndexingBatchSize == nil {
|
||||
s.LiveIndexingBatchSize = NewPointer(ElasticsearchSettingsDefaultLiveIndexingBatchSize)
|
||||
}
|
||||
|
|
@ -4427,6 +4432,16 @@ func (s *ElasticsearchSettings) isValid() *AppError {
|
|||
return NewAppError("Config.IsValid", "model.config.is_valid.elastic_search.invalid_backend.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if *s.GlobalSearchPrefix != "" && *s.IndexPrefix == "" {
|
||||
return NewAppError("Config.IsValid", "model.config.is_valid.elastic_search.empty_index_prefix.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if *s.GlobalSearchPrefix != "" && *s.IndexPrefix != "" {
|
||||
if !strings.HasPrefix(*s.IndexPrefix, *s.GlobalSearchPrefix) {
|
||||
return NewAppError("Config.IsValid", "model.config.is_valid.elastic_search.incorrect_search_prefix.app_error", map[string]any{"IndexPrefix": *s.IndexPrefix, "GlobalSearchPrefix": *s.GlobalSearchPrefix}, "", http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue