summaryrefslogtreecommitdiffstats
path: root/store/sqlstore
diff options
context:
space:
mode:
authorGeorge Goldberg <george@gberg.me>2018-01-05 15:59:10 +0000
committerSaturnino Abril <saturnino.abril@gmail.com>2018-01-05 23:59:10 +0800
commit143e664cd98aec9ee81ba953fd93704b7be33460 (patch)
treee3efdbe85ed73d9e812b706960dd5cd4c1a3ac65 /store/sqlstore
parenta5bbf3f64366110c97a3d9d2014623ce0c224be4 (diff)
downloadchat-143e664cd98aec9ee81ba953fd93704b7be33460.tar.gz
chat-143e664cd98aec9ee81ba953fd93704b7be33460.tar.bz2
chat-143e664cd98aec9ee81ba953fd93704b7be33460.zip
PLT-8312: Use combined LIKE/Full Text search for channels. (#8029)
* PLT-8312: Use combined LIKE/Full Text search for channels. * Code tidyup * Get it working consistently and update unit tests. * Fix code style.
Diffstat (limited to 'store/sqlstore')
-rw-r--r--store/sqlstore/channel_store.go64
-rw-r--r--store/sqlstore/user_store.go23
2 files changed, 76 insertions, 11 deletions
diff --git a/store/sqlstore/channel_store.go b/store/sqlstore/channel_store.go
index 9869b3720..af78b06e0 100644
--- a/store/sqlstore/channel_store.go
+++ b/store/sqlstore/channel_store.go
@@ -1308,23 +1308,73 @@ 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{}
+ // Copy the terms as we will need to prepare them differently for each search type.
+ likeTerm := term
+ fulltextTerm := term
+
+ searchColumns := "Name, DisplayName"
+
// These chars must be removed from the like query.
- for _, c := range ignoreUserSearchChar {
- term = strings.Replace(term, c, "", -1)
+ for _, c := range ignoreLikeSearchChar {
+ likeTerm = strings.Replace(likeTerm, c, "", -1)
}
// These chars must be escaped in the like query.
- for _, c := range escapeUserSearchChar {
- term = strings.Replace(term, c, "*"+c, -1)
+ for _, c := range escapeLikeSearchChar {
+ likeTerm = strings.Replace(likeTerm, c, "*"+c, -1)
}
- if term == "" {
+ // These chars must be treated as spaces in the fulltext query.
+ for _, c := range spaceFulltextSearchChar {
+ fulltextTerm = strings.Replace(fulltextTerm, c, " ", -1)
+ }
+
+ if likeTerm == "" {
+ // If the likeTerm is empty after preparing, then don't bother searching.
searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", "", 1)
} else {
- isPostgreSQL := s.DriverName() == model.DATABASE_DRIVER_POSTGRES
- searchQuery = generateSearchQuery(searchQuery, []string{term}, []string{"Name", "DisplayName"}, parameters, isPostgreSQL)
+ // Prepare the LIKE portion of the query.
+ var searchFields []string
+ for _, field := range strings.Split(searchColumns, ", ") {
+ if s.DriverName() == model.DATABASE_DRIVER_POSTGRES {
+ searchFields = append(searchFields, fmt.Sprintf("lower(%s) LIKE lower(%s) escape '*'", field, ":LikeTerm"))
+ } else {
+ searchFields = append(searchFields, fmt.Sprintf("%s LIKE %s escape '*'", field, ":LikeTerm"))
+ }
+ }
+ likeSearchClause := fmt.Sprintf("(%s)", strings.Join(searchFields, " OR "))
+ parameters["LikeTerm"] = fmt.Sprintf("%s%%", likeTerm)
+
+ // Prepare the FULLTEXT portion of the query.
+ if s.DriverName() == model.DATABASE_DRIVER_POSTGRES {
+ splitTerm := strings.Fields(fulltextTerm)
+ for i, t := range strings.Fields(fulltextTerm) {
+ if i == len(splitTerm)-1 {
+ splitTerm[i] = t + ":*"
+ } else {
+ splitTerm[i] = t + ":* &"
+ }
+ }
+
+ fulltextTerm = strings.Join(splitTerm, " ")
+
+ fulltextSearchClause := fmt.Sprintf("((%s) @@ to_tsquery(:FulltextTerm))", convertMySQLFullTextColumnsToPostgres(searchColumns))
+ searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", "AND ("+likeSearchClause+" OR "+fulltextSearchClause+")", 1)
+ } else if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
+ splitTerm := strings.Fields(fulltextTerm)
+ for i, t := range strings.Fields(fulltextTerm) {
+ splitTerm[i] = "+" + t + "*"
+ }
+
+ fulltextTerm = strings.Join(splitTerm, " ")
+
+ fulltextSearchClause := fmt.Sprintf("MATCH(%s) AGAINST (:FulltextTerm IN BOOLEAN MODE)", searchColumns)
+ searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", fmt.Sprintf("AND (%s OR %s)", likeSearchClause, fulltextSearchClause), 1)
+ }
}
+ parameters["FulltextTerm"] = fulltextTerm
+
var channels model.ChannelList
if _, err := s.GetReplica().Select(&channels, searchQuery, parameters); err != nil {
diff --git a/store/sqlstore/user_store.go b/store/sqlstore/user_store.go
index 5ecc1fdda..549377d61 100644
--- a/store/sqlstore/user_store.go
+++ b/store/sqlstore/user_store.go
@@ -1022,15 +1022,30 @@ func (us SqlUserStore) SearchInChannel(channelId string, term string, options ma
})
}
-var escapeUserSearchChar = []string{
+var escapeLikeSearchChar = []string{
"%",
"_",
}
-var ignoreUserSearchChar = []string{
+var ignoreLikeSearchChar = []string{
"*",
}
+var spaceFulltextSearchChar = []string{
+ "<",
+ ">",
+ "+",
+ "-",
+ "(",
+ ")",
+ "~",
+ ":",
+ "*",
+ "\"",
+ "!",
+ "@",
+}
+
func generateSearchQuery(searchQuery string, terms []string, fields []string, parameters map[string]interface{}, isPostgreSQL bool) string {
searchTerms := []string{}
for i, term := range terms {
@@ -1054,12 +1069,12 @@ func (us SqlUserStore) performSearch(searchQuery string, term string, options ma
result := store.StoreResult{}
// These chars must be removed from the like query.
- for _, c := range ignoreUserSearchChar {
+ for _, c := range ignoreLikeSearchChar {
term = strings.Replace(term, c, "", -1)
}
// These chars must be escaped in the like query.
- for _, c := range escapeUserSearchChar {
+ for _, c := range escapeLikeSearchChar {
term = strings.Replace(term, c, "*"+c, -1)
}