diff --git a/server/channels/store/searchtest/post_layer.go b/server/channels/store/searchtest/post_layer.go index 76a63e4f75..c0e95c69f8 100644 --- a/server/channels/store/searchtest/post_layer.go +++ b/server/channels/store/searchtest/post_layer.go @@ -192,6 +192,11 @@ var searchPostStoreTests = []searchTest{ Fn: testSearchHashtagWithUnderscores, Tags: []string{EngineAll}, }, + { + Name: "Should be able to search by hashtags with dashes and numbers", + Fn: testSearchHashtagWithDashAndNumbers, + Tags: []string{EngineAll}, + }, { Name: "Should not return system messages", Fn: testSearchShouldExcludeSystemMessages, @@ -1416,6 +1421,29 @@ func testSearchHashtagWithUnderscores(t *testing.T, th *SearchTestHelper) { th.checkPostInSearchResults(t, p1.Id, results.Posts) } +func testSearchHashtagWithDashAndNumbers(t *testing.T, th *SearchTestHelper) { + p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "searching hashtag #hashtag-1-finals23", "#hashtag-1-finals23", model.PostTypeDefault, 0, false) + require.NoError(t, err) + + p2, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "searching hashtag #hashtag-1-finals23", "#hashtag-1-finals23", model.PostTypeDefault, 0, false) + require.NoError(t, err) + + _, err = th.createPost(th.User.Id, th.ChannelBasic.Id, "searching hashtag #hashtag-finals20", "#hashtag-finals20", model.PostTypeDefault, 0, false) + require.NoError(t, err) + defer th.deleteUserPosts(th.User.Id) + + params := &model.SearchParams{ + Terms: "#hashtag-1-finals23", + IsHashtag: true, + } + results, err := th.Store.Post().SearchPostsForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20) + require.NoError(t, err) + + require.Len(t, results.Posts, 2) + th.checkPostInSearchResults(t, p1.Id, results.Posts) + th.checkPostInSearchResults(t, p2.Id, results.Posts) +} + func testSearchShouldExcludeSystemMessages(t *testing.T, th *SearchTestHelper) { _, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "test system message one", "", model.PostTypeJoinChannel, 0, false) require.NoError(t, err) diff --git a/server/channels/store/sqlstore/post_store.go b/server/channels/store/sqlstore/post_store.go index 00efb995e9..be6ced3e1e 100644 --- a/server/channels/store/sqlstore/post_store.go +++ b/server/channels/store/sqlstore/post_store.go @@ -1982,7 +1982,9 @@ func (s *SqlPostStore) search(teamId string, userId string, params *model.Search } for _, c := range s.specialSearchChars() { - terms = strings.Replace(terms, c, " ", -1) + if !params.IsHashtag { + terms = strings.Replace(terms, c, " ", -1) + } excludedTerms = strings.Replace(excludedTerms, c, " ", -1) } @@ -2007,7 +2009,12 @@ func (s *SqlPostStore) search(teamId string, userId string, params *model.Search } else if strings.HasPrefix(terms, `"`) && strings.HasSuffix(terms, `"`) { termsClause = "(" + strings.Join(strings.Fields(terms), " <-> ") + ")" + excludeClause } else { - termsClause = "(" + strings.Join(strings.Fields(terms), " & ") + ")" + excludeClause + tsVectorSearchQuery := strings.Join(strings.Fields(terms), " & ") + if params.IsHashtag { + tsVectorSearchQuery = terms + } + + termsClause = "(" + tsVectorSearchQuery + ")" + excludeClause } searchClause := fmt.Sprintf("to_tsvector('%[1]s', %[2]s) @@ to_tsquery('%[1]s', ?)", s.pgDefaultTextSearchConfig, searchType) @@ -2041,6 +2048,11 @@ func (s *SqlPostStore) search(teamId string, userId string, params *model.Search } searchClause := fmt.Sprintf("MATCH (%s) AGAINST (? IN BOOLEAN MODE)", searchType) + + if params.IsHashtag { + termsClause = "\"" + termsClause + "\"" + } + baseQuery = baseQuery.Where(searchClause, termsClause) }