diff options
-rw-r--r-- | api/command_msg_test.go | 2 | ||||
-rw-r--r-- | api/post.go | 11 | ||||
-rw-r--r-- | api/post_benchmark_test.go | 6 | ||||
-rw-r--r-- | api/post_test.go | 54 | ||||
-rw-r--r-- | model/client.go | 7 | ||||
-rw-r--r-- | model/search_params.go | 1 | ||||
-rw-r--r-- | store/sql_post_store.go | 18 | ||||
-rw-r--r-- | store/sql_post_store_test.go | 5 | ||||
-rw-r--r-- | webapp/client/client.jsx | 10 | ||||
-rw-r--r-- | webapp/components/search_bar.jsx | 1 | ||||
-rw-r--r-- | webapp/tests/client_post.test.jsx | 1 | ||||
-rw-r--r-- | webapp/utils/async_client.jsx | 5 |
12 files changed, 78 insertions, 43 deletions
diff --git a/api/command_msg_test.go b/api/command_msg_test.go index db8c3216c..f11fce091 100644 --- a/api/command_msg_test.go +++ b/api/command_msg_test.go @@ -30,7 +30,7 @@ func TestMsgCommands(t *testing.T) { if !strings.HasSuffix(rs2.GotoLocation, "/"+team.Name+"/channels/"+user1.Id+"__"+user3.Id) && !strings.HasSuffix(rs2.GotoLocation, "/"+team.Name+"/channels/"+user3.Id+"__"+user1.Id) { t.Fatal("failed to create second direct channel") } - if result := Client.Must(Client.SearchPosts("foobar")).Data.(*model.PostList); len(result.Order) == 0 { + if result := Client.Must(Client.SearchPosts("foobar", false)).Data.(*model.PostList); len(result.Order) == 0 { t.Fatalf("post did not get sent to direct message") } diff --git a/api/post.go b/api/post.go index a33b6ebf0..ac499e615 100644 --- a/api/post.go +++ b/api/post.go @@ -25,7 +25,7 @@ import ( func InitPost() { l4g.Debug(utils.T("api.post.init.debug")) - BaseRoutes.NeedTeam.Handle("/posts/search", ApiUserRequired(searchPosts)).Methods("GET") + BaseRoutes.NeedTeam.Handle("/posts/search", ApiUserRequired(searchPosts)).Methods("POST") BaseRoutes.NeedTeam.Handle("/posts/{post_id}", ApiUserRequired(getPostById)).Methods("GET") BaseRoutes.NeedTeam.Handle("/pltmp/{post_id}", ApiUserRequired(getPermalinkTmp)).Methods("GET") @@ -1289,17 +1289,24 @@ func getPostsBeforeOrAfter(c *Context, w http.ResponseWriter, r *http.Request, b } func searchPosts(c *Context, w http.ResponseWriter, r *http.Request) { - terms := r.FormValue("terms") + props := model.StringInterfaceFromJson(r.Body) + terms := props["terms"].(string) if len(terms) == 0 { c.SetInvalidParam("search", "terms") return } + isOrSearch := false + if val, ok := props["is_or_search"]; ok && val != nil { + isOrSearch = val.(bool) + } + paramsList := model.ParseSearchParams(terms) channels := []store.StoreChannel{} for _, params := range paramsList { + params.OrTerms = isOrSearch // don't allow users to search for everything if params.Terms != "*" { channels = append(channels, Srv.Store.Post().Search(c.TeamId, c.Session.UserId, params)) diff --git a/api/post_benchmark_test.go b/api/post_benchmark_test.go index 4e5f6668f..5424bc1dd 100644 --- a/api/post_benchmark_test.go +++ b/api/post_benchmark_test.go @@ -95,9 +95,9 @@ func BenchmarkSearchPosts(b *testing.B) { // Benchmark Start b.ResetTimer() for i := 0; i < b.N; i++ { - Client.Must(Client.SearchPosts("nothere")) - Client.Must(Client.SearchPosts("n")) - Client.Must(Client.SearchPosts("#tag")) + Client.Must(Client.SearchPosts("nothere", false)) + Client.Must(Client.SearchPosts("n", false)) + Client.Must(Client.SearchPosts("#tag", false)) } } diff --git a/api/post_test.go b/api/post_test.go index 529cc6e4d..bb11a5439 100644 --- a/api/post_test.go +++ b/api/post_test.go @@ -357,27 +357,33 @@ func TestSearchPosts(t *testing.T) { post4 := &model.Post{ChannelId: channel1.Id, Message: "hashtag for post4"} post4 = Client.Must(Client.CreatePost(post4)).Data.(*model.Post) - r1 := Client.Must(Client.SearchPosts("search")).Data.(*model.PostList) + r1 := Client.Must(Client.SearchPosts("search", false)).Data.(*model.PostList) if len(r1.Order) != 3 { t.Fatal("wrong search") } - r2 := Client.Must(Client.SearchPosts("post2")).Data.(*model.PostList) + r2 := Client.Must(Client.SearchPosts("post2", false)).Data.(*model.PostList) if len(r2.Order) != 1 && r2.Order[0] == post2.Id { t.Fatal("wrong search") } - r3 := Client.Must(Client.SearchPosts("#hashtag")).Data.(*model.PostList) + r3 := Client.Must(Client.SearchPosts("#hashtag", false)).Data.(*model.PostList) if len(r3.Order) != 1 && r3.Order[0] == post3.Id { t.Fatal("wrong search") } - if r4 := Client.Must(Client.SearchPosts("*")).Data.(*model.PostList); len(r4.Order) != 0 { + if r4 := Client.Must(Client.SearchPosts("*", false)).Data.(*model.PostList); len(r4.Order) != 0 { t.Fatal("searching for just * shouldn't return any results") } + + r5 := Client.Must(Client.SearchPosts("post1 post2", true)).Data.(*model.PostList) + + if len(r5.Order) != 2 { + t.Fatal("wrong search results") + } } func TestSearchHashtagPosts(t *testing.T) { @@ -394,7 +400,7 @@ func TestSearchHashtagPosts(t *testing.T) { post3 := &model.Post{ChannelId: channel1.Id, Message: "no hashtag"} post3 = Client.Must(Client.CreatePost(post3)).Data.(*model.Post) - r1 := Client.Must(Client.SearchPosts("#sgtitlereview")).Data.(*model.PostList) + r1 := Client.Must(Client.SearchPosts("#sgtitlereview", false)).Data.(*model.PostList) if len(r1.Order) != 2 { t.Fatal("wrong search") @@ -425,47 +431,47 @@ func TestSearchPostsInChannel(t *testing.T) { post4 := &model.Post{ChannelId: channel3.Id, Message: "other message with no return"} post4 = Client.Must(Client.CreatePost(post4)).Data.(*model.Post) - if result := Client.Must(Client.SearchPosts("channel:")).Data.(*model.PostList); len(result.Order) != 0 { + if result := Client.Must(Client.SearchPosts("channel:", false)).Data.(*model.PostList); len(result.Order) != 0 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } - if result := Client.Must(Client.SearchPosts("in:")).Data.(*model.PostList); len(result.Order) != 0 { + if result := Client.Must(Client.SearchPosts("in:", false)).Data.(*model.PostList); len(result.Order) != 0 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } - if result := Client.Must(Client.SearchPosts("channel:" + channel1.Name)).Data.(*model.PostList); len(result.Order) != 2 { + if result := Client.Must(Client.SearchPosts("channel:"+channel1.Name, false)).Data.(*model.PostList); len(result.Order) != 2 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } - if result := Client.Must(Client.SearchPosts("in: " + channel2.Name)).Data.(*model.PostList); len(result.Order) != 2 { + if result := Client.Must(Client.SearchPosts("in: "+channel2.Name, false)).Data.(*model.PostList); len(result.Order) != 2 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } - if result := Client.Must(Client.SearchPosts("channel: " + channel2.Name)).Data.(*model.PostList); len(result.Order) != 2 { + if result := Client.Must(Client.SearchPosts("channel: "+channel2.Name, false)).Data.(*model.PostList); len(result.Order) != 2 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } - if result := Client.Must(Client.SearchPosts("ChAnNeL: " + channel2.Name)).Data.(*model.PostList); len(result.Order) != 2 { + if result := Client.Must(Client.SearchPosts("ChAnNeL: "+channel2.Name, false)).Data.(*model.PostList); len(result.Order) != 2 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } - if result := Client.Must(Client.SearchPosts("sgtitlereview")).Data.(*model.PostList); len(result.Order) != 2 { + if result := Client.Must(Client.SearchPosts("sgtitlereview", false)).Data.(*model.PostList); len(result.Order) != 2 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } - if result := Client.Must(Client.SearchPosts("sgtitlereview channel:" + channel1.Name)).Data.(*model.PostList); len(result.Order) != 1 { + if result := Client.Must(Client.SearchPosts("sgtitlereview channel:"+channel1.Name, false)).Data.(*model.PostList); len(result.Order) != 1 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } - if result := Client.Must(Client.SearchPosts("sgtitlereview in: " + channel2.Name)).Data.(*model.PostList); len(result.Order) != 1 { + if result := Client.Must(Client.SearchPosts("sgtitlereview in: "+channel2.Name, false)).Data.(*model.PostList); len(result.Order) != 1 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } - if result := Client.Must(Client.SearchPosts("sgtitlereview channel: " + channel2.Name)).Data.(*model.PostList); len(result.Order) != 1 { + if result := Client.Must(Client.SearchPosts("sgtitlereview channel: "+channel2.Name, false)).Data.(*model.PostList); len(result.Order) != 1 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } - if result := Client.Must(Client.SearchPosts("channel: " + channel2.Name + " channel: " + channel3.Name)).Data.(*model.PostList); len(result.Order) != 3 { + if result := Client.Must(Client.SearchPosts("channel: "+channel2.Name+" channel: "+channel3.Name, false)).Data.(*model.PostList); len(result.Order) != 3 { t.Fatalf("wrong number of posts returned :) %v :) %v", result.Posts, result.Order) } } @@ -493,22 +499,22 @@ func TestSearchPostsFromUser(t *testing.T) { post2 := &model.Post{ChannelId: channel2.Id, Message: "sgtitlereview\n with return"} post2 = Client.Must(Client.CreatePost(post2)).Data.(*model.Post) - if result := Client.Must(Client.SearchPosts("from: " + user1.Username)).Data.(*model.PostList); len(result.Order) != 2 { + if result := Client.Must(Client.SearchPosts("from: "+user1.Username, false)).Data.(*model.PostList); len(result.Order) != 2 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } - if result := Client.Must(Client.SearchPosts("from: " + user2.Username)).Data.(*model.PostList); len(result.Order) != 1 { + if result := Client.Must(Client.SearchPosts("from: "+user2.Username, false)).Data.(*model.PostList); len(result.Order) != 1 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } - if result := Client.Must(Client.SearchPosts("from: " + user2.Username + " sgtitlereview")).Data.(*model.PostList); len(result.Order) != 1 { + if result := Client.Must(Client.SearchPosts("from: "+user2.Username+" sgtitlereview", false)).Data.(*model.PostList); len(result.Order) != 1 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } post3 := &model.Post{ChannelId: channel1.Id, Message: "hullo"} post3 = Client.Must(Client.CreatePost(post3)).Data.(*model.Post) - if result := Client.Must(Client.SearchPosts("from: " + user2.Username + " in:" + channel1.Name)).Data.(*model.PostList); len(result.Order) != 1 { + if result := Client.Must(Client.SearchPosts("from: "+user2.Username+" in:"+channel1.Name, false)).Data.(*model.PostList); len(result.Order) != 1 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } @@ -517,22 +523,22 @@ func TestSearchPostsFromUser(t *testing.T) { // wait for the join/leave messages to be created for user3 since they're done asynchronously time.Sleep(100 * time.Millisecond) - if result := Client.Must(Client.SearchPosts("from: " + user2.Username)).Data.(*model.PostList); len(result.Order) != 2 { + if result := Client.Must(Client.SearchPosts("from: "+user2.Username, false)).Data.(*model.PostList); len(result.Order) != 2 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } - if result := Client.Must(Client.SearchPosts("from: " + user2.Username + " from: " + user3.Username)).Data.(*model.PostList); len(result.Order) != 2 { + if result := Client.Must(Client.SearchPosts("from: "+user2.Username+" from: "+user3.Username, false)).Data.(*model.PostList); len(result.Order) != 2 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } - if result := Client.Must(Client.SearchPosts("from: " + user2.Username + " from: " + user3.Username + " in:" + channel2.Name)).Data.(*model.PostList); len(result.Order) != 1 { + if result := Client.Must(Client.SearchPosts("from: "+user2.Username+" from: "+user3.Username+" in:"+channel2.Name, false)).Data.(*model.PostList); len(result.Order) != 1 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } post4 := &model.Post{ChannelId: channel2.Id, Message: "coconut"} post4 = Client.Must(Client.CreatePost(post4)).Data.(*model.Post) - if result := Client.Must(Client.SearchPosts("from: " + user2.Username + " from: " + user3.Username + " in:" + channel2.Name + " coconut")).Data.(*model.PostList); len(result.Order) != 1 { + if result := Client.Must(Client.SearchPosts("from: "+user2.Username+" from: "+user3.Username+" in:"+channel2.Name+" coconut", false)).Data.(*model.PostList); len(result.Order) != 1 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } } diff --git a/model/client.go b/model/client.go index f045401eb..1575df9e0 100644 --- a/model/client.go +++ b/model/client.go @@ -918,8 +918,11 @@ func (c *Client) DeletePost(channelId string, postId string) (*Result, *AppError } } -func (c *Client) SearchPosts(terms string) (*Result, *AppError) { - if r, err := c.DoApiGet(c.GetTeamRoute()+"/posts/search?terms="+url.QueryEscape(terms), "", ""); err != nil { +func (c *Client) SearchPosts(terms string, isOrSearch bool) (*Result, *AppError) { + data := map[string]interface{}{} + data["terms"] = terms + data["is_or_search"] = isOrSearch + if r, err := c.DoApiPost(c.GetTeamRoute()+"/posts/search", StringInterfaceToJson(data)); err != nil { return nil, err } else { return &Result{r.Header.Get(HEADER_REQUEST_ID), diff --git a/model/search_params.go b/model/search_params.go index d31782691..250c8e1f3 100644 --- a/model/search_params.go +++ b/model/search_params.go @@ -12,6 +12,7 @@ type SearchParams struct { IsHashtag bool InChannels []string FromUsers []string + OrTerms bool } var searchFlags = [...]string{"from", "channel", "in"} diff --git a/store/sql_post_store.go b/store/sql_post_store.go index 54b526191..d7d009ce4 100644 --- a/store/sql_post_store.go +++ b/store/sql_post_store.go @@ -725,7 +725,11 @@ func (s SqlPostStore) Search(teamId string, userId string, params *model.SearchP terms = wildcard.ReplaceAllLiteralString(terms, ":* ") } - terms = strings.Join(strings.Fields(terms), " & ") + if params.OrTerms { + terms = strings.Join(strings.Fields(terms), " | ") + } else { + terms = strings.Join(strings.Fields(terms), " & ") + } searchClause := fmt.Sprintf("AND %s @@ to_tsquery(:Terms)", searchType) searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", searchClause, 1) @@ -733,12 +737,14 @@ func (s SqlPostStore) Search(teamId string, userId string, params *model.SearchP searchClause := fmt.Sprintf("AND MATCH (%s) AGAINST (:Terms IN BOOLEAN MODE)", searchType) searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", searchClause, 1) - splitTerms := strings.Fields(terms) - for i, t := range strings.Fields(terms) { - splitTerms[i] = "+" + t - } + if !params.OrTerms { + splitTerms := strings.Fields(terms) + for i, t := range strings.Fields(terms) { + splitTerms[i] = "+" + t + } - terms = strings.Join(splitTerms, " ") + terms = strings.Join(splitTerms, " ") + } } queryParams["Terms"] = terms diff --git a/store/sql_post_store_test.go b/store/sql_post_store_test.go index 62db6efb5..3c317b926 100644 --- a/store/sql_post_store_test.go +++ b/store/sql_post_store_test.go @@ -767,6 +767,11 @@ func TestPostStoreSearch(t *testing.T) { if len(r12.Order) != 1 { t.Fatal("returned wrong search result") } + + r13 := (<-store.Post().Search(teamId, userId, &model.SearchParams{Terms: "Jersey corey", IsHashtag: false, OrTerms: true})).Data.(*model.PostList) + if len(r13.Order) != 2 { + t.Fatal("returned wrong search result") + } } func TestUserCountsWithPostsByDay(t *testing.T) { diff --git a/webapp/client/client.jsx b/webapp/client/client.jsx index 9bcbeed4e..5d0dd07c9 100644 --- a/webapp/client/client.jsx +++ b/webapp/client/client.jsx @@ -1267,13 +1267,17 @@ export default class Client { this.track('api', 'api_posts_delete'); } - search = (terms, success, error) => { + search = (terms, isOrSearch, success, error) => { + const data = {}; + data.terms = terms; + data.is_or_search = isOrSearch; + request. - get(`${this.getTeamNeededRoute()}/posts/search`). + post(`${this.getTeamNeededRoute()}/posts/search`). set(this.defaultHeaders). type('application/json'). accept('application/json'). - query({terms}). + send(data). end(this.handleResponse.bind(this, 'search', success, error)); this.track('api', 'api_posts_search'); diff --git a/webapp/components/search_bar.jsx b/webapp/components/search_bar.jsx index 1156ac0f1..6ebb9cfdc 100644 --- a/webapp/components/search_bar.jsx +++ b/webapp/components/search_bar.jsx @@ -114,6 +114,7 @@ class SearchBar extends React.Component { client.search( terms, + isMentionSearch, (data) => { this.setState({isSearching: false}); if (utils.isMobile()) { diff --git a/webapp/tests/client_post.test.jsx b/webapp/tests/client_post.test.jsx index db48e4000..c8e6fad0f 100644 --- a/webapp/tests/client_post.test.jsx +++ b/webapp/tests/client_post.test.jsx @@ -102,6 +102,7 @@ describe('Client.Posts', function() { TestHelper.initBasic(() => { TestHelper.basicClient().search( 'unit test', + false, function(data) { assert.equal(data.order[0], TestHelper.basicPost().id); done(); diff --git a/webapp/utils/async_client.jsx b/webapp/utils/async_client.jsx index 1dcead326..a562964b1 100644 --- a/webapp/utils/async_client.jsx +++ b/webapp/utils/async_client.jsx @@ -467,7 +467,7 @@ export function getAllTeamListings() { ); } -export function search(terms) { +export function search(terms, isOrSearch) { if (isCallInProgress('search_' + String(terms))) { return; } @@ -475,6 +475,7 @@ export function search(terms) { callTracker['search_' + String(terms)] = utils.getTimestamp(); Client.search( terms, + isOrSearch, (data) => { callTracker['search_' + String(terms)] = 0; @@ -1370,4 +1371,4 @@ export function getPublicLink(filename, success, error) { } } ); -}
\ No newline at end of file +} |