From 226d4b2ac8888646271b9e9e83a513cd6e62d620 Mon Sep 17 00:00:00 2001 From: Harrison Healey Date: Tue, 19 Jun 2018 05:46:29 -0400 Subject: 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 --- api4/post.go | 6 ++++-- app/post.go | 10 +++++----- einterfaces/elasticsearch.go | 2 +- i18n/en.json | 4 ++++ model/client4.go | 11 +++++++++++ model/post_search_results.go | 40 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 65 insertions(+), 8 deletions(-) create mode 100644 model/post_search_results.go 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 @@ -4278,6 +4278,10 @@ "id": "ent.elasticsearch.search_posts.search_failed", "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(©) + if err != nil { + return "" + } else { + return string(b) + } +} + +func PostSearchResultsFromJson(data io.Reader) *PostSearchResults { + var o *PostSearchResults + json.NewDecoder(data).Decode(&o) + return o +} -- cgit v1.2.3-1-g7c22