[PLT-7764] Fix in:channel search when channel name/displayname includes - (#7603)

* fix in:channel search when channel name/displayname includes `-`

* fix failing test in PostgreSQL

* add lowercase indexes of channel's name and displayname to postgresql, and add unit tests
This commit is contained in:
Saturnino Abril
2017-10-11 17:20:27 +08:00
committed by GitHub
parent 66b07a72ce
commit 521e27f4ac
3 changed files with 159 additions and 47 deletions

View File

@@ -85,6 +85,11 @@ func (s SqlChannelStore) CreateIndexesIfNotExists() {
s.CreateIndexIfNotExists("idx_channels_create_at", "Channels", "CreateAt")
s.CreateIndexIfNotExists("idx_channels_delete_at", "Channels", "DeleteAt")
if s.DriverName() == model.DATABASE_DRIVER_POSTGRES {
s.CreateIndexIfNotExists("idx_channels_name_lower", "Channels", "lower(Name)")
s.CreateIndexIfNotExists("idx_channels_displayname_lower", "Channels", "lower(DisplayName)")
}
s.CreateIndexIfNotExists("idx_channelmembers_channel_id", "ChannelMembers", "ChannelId")
s.CreateIndexIfNotExists("idx_channelmembers_user_id", "ChannelMembers", "UserId")
@@ -1246,43 +1251,25 @@ func (s SqlChannelStore) SearchMore(userId string, teamId string, term string) s
func (s SqlChannelStore) performSearch(searchQuery string, term string, parameters map[string]interface{}) store.StoreResult {
result := store.StoreResult{}
// these chars have special meaning and can be treated as spaces
// These chars must be removed from the like query.
for _, c := range ignoreUserSearchChar {
term = strings.Replace(term, c, " ", -1)
term = strings.Replace(term, c, "", -1)
}
// These chars must be escaped in the like query.
for _, c := range escapeUserSearchChar {
term = strings.Replace(term, c, "*"+c, -1)
}
if term == "" {
searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", "", 1)
} else if s.DriverName() == model.DATABASE_DRIVER_POSTGRES {
splitTerm := strings.Fields(term)
for i, t := range strings.Fields(term) {
if i == len(splitTerm)-1 {
splitTerm[i] = t + ":*"
} else {
splitTerm[i] = t + ":* &"
}
}
term = strings.Join(splitTerm, " ")
searchClause := fmt.Sprintf("AND (%s) @@ to_tsquery('simple', :Term)", "Name || ' ' || DisplayName")
searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", searchClause, 1)
} else if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
splitTerm := strings.Fields(term)
for i, t := range strings.Fields(term) {
splitTerm[i] = "+" + t + "*"
}
term = strings.Join(splitTerm, " ")
searchClause := fmt.Sprintf("AND MATCH(%s) AGAINST (:Term IN BOOLEAN MODE)", "Name, DisplayName")
searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", searchClause, 1)
} else {
isPostgreSQL := s.DriverName() == model.DATABASE_DRIVER_POSTGRES
searchQuery = generateSearchQuery(searchQuery, term, "Name, DisplayName", parameters, isPostgreSQL)
}
var channels model.ChannelList
parameters["Term"] = term
if _, err := s.GetReplica().Select(&channels, searchQuery, parameters); err != nil {
result.Err = model.NewAppError("SqlChannelStore.Search", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError)
} else {

View File

@@ -1028,6 +1028,28 @@ var ignoreUserSearchChar = []string{
"*",
}
func generateSearchQuery(searchQuery, term, searchField string, parameters map[string]interface{}, isPostgreSQL bool) string {
splitTerms := strings.Fields(term)
splitFields := strings.Split(searchField, ", ")
terms := []string{}
for i, term := range splitTerms {
fields := []string{}
for _, field := range splitFields {
if isPostgreSQL {
fields = append(fields, fmt.Sprintf("lower(%s) LIKE lower(%s) escape '*' ", field, fmt.Sprintf(":Term%d", i)))
} else {
fields = append(fields, fmt.Sprintf("%s LIKE %s escape '*' ", field, fmt.Sprintf(":Term%d", i)))
}
}
terms = append(terms, fmt.Sprintf("(%s)", strings.Join(fields, " OR ")))
parameters[fmt.Sprintf("Term%d", i)] = fmt.Sprintf("%s%%", term)
}
searchClause := strings.Join(terms, " AND ")
return strings.Replace(searchQuery, "SEARCH_CLAUSE", fmt.Sprintf(" AND %s ", searchClause), 1)
}
func (us SqlUserStore) performSearch(searchQuery string, term string, options map[string]bool, parameters map[string]interface{}) store.StoreResult {
result := store.StoreResult{}
@@ -1059,25 +1081,8 @@ func (us SqlUserStore) performSearch(searchQuery string, term string, options ma
if term == "" {
searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", "", 1)
} else {
splitTerms := strings.Fields(term)
splitFields := strings.Split(searchType, ", ")
terms := []string{}
for i, term := range splitTerms {
fields := []string{}
for _, field := range splitFields {
if us.DriverName() == model.DATABASE_DRIVER_POSTGRES {
fields = append(fields, fmt.Sprintf("lower(%s) LIKE lower(%s) escape '*' ", field, fmt.Sprintf(":Term%d", i)))
} else {
fields = append(fields, fmt.Sprintf("%s LIKE %s escape '*' ", field, fmt.Sprintf(":Term%d", i)))
}
}
terms = append(terms, fmt.Sprintf("(%s)", strings.Join(fields, " OR ")))
parameters[fmt.Sprintf("Term%d", i)] = fmt.Sprintf("%s%%", term)
}
term := strings.Join(terms, " AND ")
searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", fmt.Sprintf(" AND %s ", term), 1)
isPostgreSQL := us.DriverName() == model.DATABASE_DRIVER_POSTGRES
searchQuery = generateSearchQuery(searchQuery, term, searchType, parameters, isPostgreSQL)
}
var users []*model.User