summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarrison Healey <harrisonmhealey@gmail.com>2018-06-19 05:46:29 -0400
committerCarlos Tadeu Panato Junior <ctadeu@gmail.com>2018-06-19 11:46:29 +0200
commit226d4b2ac8888646271b9e9e83a513cd6e62d620 (patch)
treeb79a3eff5768f4b9f589b78a7e18324a53cf8e5b
parent6d8140337ef0f68f5177988f3c87bba5e4946399 (diff)
downloadchat-226d4b2ac8888646271b9e9e83a513cd6e62d620.tar.gz
chat-226d4b2ac8888646271b9e9e83a513cd6e62d620.tar.bz2
chat-226d4b2ac8888646271b9e9e83a513cd6e62d620.zip
MM-6992 Added highlighting to elasticsearch results (#8861)
* MM-6992 Added highlighting to elasticsearch results * Added a unique type for post search matches * Fixed Elasticsearch matches not being sent through API
-rw-r--r--api4/post.go6
-rw-r--r--app/post.go10
-rw-r--r--einterfaces/elasticsearch.go2
-rw-r--r--i18n/en.json4
-rw-r--r--model/client4.go11
-rw-r--r--model/post_search_results.go40
6 files changed, 65 insertions, 8 deletions
diff --git a/api4/post.go b/api4/post.go
index 68c1967ff..27ecd1584 100644
--- a/api4/post.go
+++ b/api4/post.go
@@ -341,7 +341,7 @@ func searchPosts(c *Context, w http.ResponseWriter, r *http.Request) {
startTime := time.Now()
- posts, err := c.App.SearchPostsInTeam(terms, c.Session.UserId, c.Params.TeamId, isOrSearch)
+ results, err := c.App.SearchPostsInTeam(terms, c.Session.UserId, c.Params.TeamId, isOrSearch)
elapsedTime := float64(time.Since(startTime)) / float64(time.Second)
metrics := c.App.Metrics
@@ -355,8 +355,10 @@ func searchPosts(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ results = model.MakePostSearchResults(c.App.PostListWithProxyAddedToImageURLs(results.PostList), results.Matches)
+
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
- w.Write([]byte(c.App.PostListWithProxyAddedToImageURLs(posts).ToJson()))
+ w.Write([]byte(results.ToJson()))
}
func updatePost(c *Context, w http.ResponseWriter, r *http.Request) {
diff --git a/app/post.go b/app/post.go
index 4f4c6f65c..e24018995 100644
--- a/app/post.go
+++ b/app/post.go
@@ -621,7 +621,7 @@ func (a *App) DeletePostFiles(post *model.Post) {
}
}
-func (a *App) SearchPostsInTeam(terms string, userId string, teamId string, isOrSearch bool) (*model.PostList, *model.AppError) {
+func (a *App) SearchPostsInTeam(terms string, userId string, teamId string, isOrSearch bool) (*model.PostSearchResults, *model.AppError) {
paramsList := model.ParseSearchParams(terms)
esInterface := a.Elasticsearch
@@ -656,7 +656,7 @@ func (a *App) SearchPostsInTeam(terms string, userId string, teamId string, isOr
// If the processed search params are empty, return empty search results.
if len(finalParamsList) == 0 {
- return model.NewPostList(), nil
+ return model.MakePostSearchResults(model.NewPostList(), nil), nil
}
// We only allow the user to search in channels they are a member of.
@@ -666,7 +666,7 @@ func (a *App) SearchPostsInTeam(terms string, userId string, teamId string, isOr
return nil, err
}
- postIds, err := a.Elasticsearch.SearchPosts(userChannels, finalParamsList)
+ postIds, matches, err := a.Elasticsearch.SearchPosts(userChannels, finalParamsList)
if err != nil {
return nil, err
}
@@ -684,7 +684,7 @@ func (a *App) SearchPostsInTeam(terms string, userId string, teamId string, isOr
}
}
- return postList, nil
+ return model.MakePostSearchResults(postList, matches), nil
} else {
if !*a.Config().ServiceSettings.EnablePostSearch {
return nil, model.NewAppError("SearchPostsInTeam", "store.sql_post.search.disabled", nil, fmt.Sprintf("teamId=%v userId=%v", teamId, userId), http.StatusNotImplemented)
@@ -712,7 +712,7 @@ func (a *App) SearchPostsInTeam(terms string, userId string, teamId string, isOr
posts.SortByCreateAt()
- return posts, nil
+ return model.MakePostSearchResults(posts, nil), nil
}
}
diff --git a/einterfaces/elasticsearch.go b/einterfaces/elasticsearch.go
index 5582fd4e8..1b7444b2b 100644
--- a/einterfaces/elasticsearch.go
+++ b/einterfaces/elasticsearch.go
@@ -12,7 +12,7 @@ import (
type ElasticsearchInterface interface {
Start() *model.AppError
IndexPost(post *model.Post, teamId string) *model.AppError
- SearchPosts(channels *model.ChannelList, searchParams []*model.SearchParams) ([]string, *model.AppError)
+ SearchPosts(channels *model.ChannelList, searchParams []*model.SearchParams) ([]string, model.PostSearchMatches, *model.AppError)
DeletePost(post *model.Post) *model.AppError
TestConfig(cfg *model.Config) *model.AppError
PurgeIndexes() *model.AppError
diff --git a/i18n/en.json b/i18n/en.json
index 6828740fc..4b1849ccc 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -4279,6 +4279,10 @@
"translation": "Search failed to complete"
},
{
+ "id": "ent.elasticsearch.search_posts.parse_matches_failed",
+ "translation": "Failed to parse search result matches"
+ },
+ {
"id": "ent.elasticsearch.search_posts.unmarshall_post_failed",
"translation": "Failed to decode search results"
},
diff --git a/model/client4.go b/model/client4.go
index f5a856835..3a93a9541 100644
--- a/model/client4.go
+++ b/model/client4.go
@@ -2127,6 +2127,17 @@ func (c *Client4) SearchPosts(teamId string, terms string, isOrSearch bool) (*Po
}
}
+// SearchPosts returns any posts with matching terms string, including .
+func (c *Client4) SearchPostsWithMatches(teamId string, terms string, isOrSearch bool) (*PostSearchResults, *Response) {
+ requestBody := map[string]interface{}{"terms": terms, "is_or_search": isOrSearch}
+ if r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/posts/search", StringInterfaceToJson(requestBody)); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return PostSearchResultsFromJson(r.Body), BuildResponse(r)
+ }
+}
+
// DoPostAction performs a post action.
func (c *Client4) DoPostAction(postId, actionId string) (bool, *Response) {
if r, err := c.DoApiPost(c.GetPostRoute(postId)+"/actions/"+actionId, ""); err != nil {
diff --git a/model/post_search_results.go b/model/post_search_results.go
new file mode 100644
index 000000000..2317f1831
--- /dev/null
+++ b/model/post_search_results.go
@@ -0,0 +1,40 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+import (
+ "encoding/json"
+ "io"
+)
+
+type PostSearchMatches map[string][]string
+
+type PostSearchResults struct {
+ *PostList
+ Matches PostSearchMatches `json:"matches"`
+}
+
+func MakePostSearchResults(posts *PostList, matches PostSearchMatches) *PostSearchResults {
+ return &PostSearchResults{
+ posts,
+ matches,
+ }
+}
+
+func (o *PostSearchResults) ToJson() string {
+ copy := *o
+ copy.PostList.StripActionIntegrations()
+ b, err := json.Marshal(&copy)
+ if err != nil {
+ return ""
+ } else {
+ return string(b)
+ }
+}
+
+func PostSearchResultsFromJson(data io.Reader) *PostSearchResults {
+ var o *PostSearchResults
+ json.NewDecoder(data).Decode(&o)
+ return o
+}