github.com/mattermost/mattermost-server/v5@v5.39.3/store/searchtest/post_layer.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package searchtest
     5  
     6  import (
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/require"
    11  
    12  	"github.com/mattermost/mattermost-server/v5/model"
    13  	"github.com/mattermost/mattermost-server/v5/store"
    14  )
    15  
    16  var searchPostStoreTests = []searchTest{
    17  	{
    18  		Name: "Should be able to search posts including results from DMs",
    19  		Fn:   testSearchPostsIncludingDMs,
    20  		Tags: []string{EngineAll},
    21  	},
    22  	{
    23  		Name: "Should be able to search posts using pagination",
    24  		Fn:   testSearchPostsWithPagination,
    25  		Tags: []string{EngineElasticSearch, EngineBleve},
    26  	},
    27  	{
    28  		Name: "Should return pinned and unpinned posts",
    29  		Fn:   testSearchReturnPinnedAndUnpinned,
    30  		Tags: []string{EngineAll},
    31  	},
    32  	{
    33  		Name: "Should be able to search for exact phrases in quotes",
    34  		Fn:   testSearchExactPhraseInQuotes,
    35  		Tags: []string{EnginePostgres, EngineMySql, EngineElasticSearch},
    36  	},
    37  	{
    38  		Name: "Should be able to search for email addresses with or without quotes",
    39  		Fn:   testSearchEmailAddresses,
    40  		Tags: []string{EngineElasticSearch},
    41  	},
    42  	{
    43  		Name: "Should be able to search when markdown underscores are applied",
    44  		Fn:   testSearchMarkdownUnderscores,
    45  		Tags: []string{EnginePostgres, EngineElasticSearch},
    46  	},
    47  	{
    48  		Name: "Should be able to search for non-latin words",
    49  		Fn:   testSearchNonLatinWords,
    50  		Tags: []string{EngineElasticSearch},
    51  	},
    52  	{
    53  		Name: "Should be able to search for alternative spellings of words",
    54  		Fn:   testSearchAlternativeSpellings,
    55  		Tags: []string{EngineElasticSearch},
    56  	},
    57  	{
    58  		Name: "Should be able to search for alternative spellings of words with and without accents",
    59  		Fn:   testSearchAlternativeSpellingsAccents,
    60  		Tags: []string{EngineElasticSearch},
    61  	},
    62  	{
    63  		Name: "Should be able to search or exclude messages written by a specific user",
    64  		Fn:   testSearchOrExcludePostsBySpecificUser,
    65  		Tags: []string{EngineAll},
    66  	},
    67  	{
    68  		Name: "Should be able to search or exclude messages written in a specific channel",
    69  		Fn:   testSearchOrExcludePostsInChannel,
    70  		Tags: []string{EngineAll},
    71  	},
    72  	{
    73  		Name: "Should be able to search or exclude messages written in a DM or GM",
    74  		Fn:   testSearchOrExcludePostsInDMGM,
    75  		Tags: []string{EngineAll},
    76  	},
    77  	{
    78  		Name: "Should be able to filter messages written after a specific date",
    79  		Fn:   testFilterMessagesAfterSpecificDate,
    80  		Tags: []string{EngineAll},
    81  	},
    82  	{
    83  		Name: "Should be able to filter messages written before a specific date",
    84  		Fn:   testFilterMessagesBeforeSpecificDate,
    85  		Tags: []string{EngineAll},
    86  	},
    87  	{
    88  		Name: "Should be able to filter messages written on a specific date",
    89  		Fn:   testFilterMessagesInSpecificDate,
    90  		Tags: []string{EngineAll},
    91  	},
    92  	{
    93  		Name: "Should be able to exclude messages that contain a serch term",
    94  		Fn:   testFilterMessagesWithATerm,
    95  		Tags: []string{EngineMySql, EnginePostgres},
    96  	},
    97  	{
    98  		Name: "Should be able to search using boolean operators",
    99  		Fn:   testSearchUsingBooleanOperators,
   100  		Tags: []string{EngineMySql, EnginePostgres, EngineElasticSearch},
   101  	},
   102  	{
   103  		Name: "Should be able to search with combined filters",
   104  		Fn:   testSearchUsingCombinedFilters,
   105  		Tags: []string{EngineAll},
   106  	},
   107  	{
   108  		Name: "Should be able to ignore stop words",
   109  		Fn:   testSearchIgnoringStopWords,
   110  		Tags: []string{EngineMySql, EngineElasticSearch},
   111  	},
   112  	{
   113  		Name: "Should support search stemming",
   114  		Fn:   testSupportStemming,
   115  		Tags: []string{EnginePostgres, EngineElasticSearch},
   116  	},
   117  	{
   118  		Name: "Should support search with wildcards",
   119  		Fn:   testSupportWildcards,
   120  		Tags: []string{EngineAll},
   121  	},
   122  	{
   123  		Name: "Should not support search with preceding wildcards",
   124  		Fn:   testNotSupportPrecedingWildcards,
   125  		Tags: []string{EngineAll},
   126  	},
   127  	{
   128  		Name: "Should discard a wildcard if it's not placed immediately by text",
   129  		Fn:   testSearchDiscardWildcardAlone,
   130  		Tags: []string{EngineAll},
   131  	},
   132  	{
   133  		Name: "Should support terms with dash",
   134  		Fn:   testSupportTermsWithDash,
   135  		Tags: []string{EngineAll},
   136  		Skip: true,
   137  	},
   138  	{
   139  		Name: "Should support terms with underscore",
   140  		Fn:   testSupportTermsWithUnderscore,
   141  		Tags: []string{EngineMySql, EngineElasticSearch},
   142  	},
   143  	{
   144  		Name: "Should search or exclude post using hashtags",
   145  		Fn:   testSearchOrExcludePostsWithHashtags,
   146  		Tags: []string{EngineAll},
   147  	},
   148  	{
   149  		Name: "Should support searching for hashtags surrounded by markdown",
   150  		Fn:   testSearchHashtagWithMarkdown,
   151  		Tags: []string{EngineAll},
   152  	},
   153  	{
   154  		Name: "Should support searching for multiple hashtags",
   155  		Fn:   testSearcWithMultipleHashtags,
   156  		Tags: []string{EngineElasticSearch},
   157  	},
   158  	{
   159  		Name: "Should support searching hashtags with dots",
   160  		Fn:   testSearchPostsWithDotsInHashtags,
   161  		Tags: []string{EngineAll},
   162  	},
   163  	{
   164  		Name: "Should be able to search or exclude messages with hashtags in a case insensitive manner",
   165  		Fn:   testSearchHashtagCaseInsensitive,
   166  		Tags: []string{EngineAll},
   167  	},
   168  	{
   169  		Name: "Should be able to search by hashtags with dashes",
   170  		Fn:   testSearchHashtagWithDash,
   171  		Tags: []string{EngineAll},
   172  	},
   173  	{
   174  		Name: "Should be able to search by hashtags with numbers",
   175  		Fn:   testSearchHashtagWithNumbers,
   176  		Tags: []string{EngineAll},
   177  	},
   178  	{
   179  		Name: "Should be able to search by hashtags with dots",
   180  		Fn:   testSearchHashtagWithDots,
   181  		Tags: []string{EngineAll},
   182  	},
   183  	{
   184  		Name: "Should be able to search by hashtags with underscores",
   185  		Fn:   testSearchHashtagWithUnderscores,
   186  		Tags: []string{EngineAll},
   187  	},
   188  	{
   189  		Name: "Should not return system messages",
   190  		Fn:   testSearchShouldExcludeSytemMessages,
   191  		Tags: []string{EngineAll},
   192  	},
   193  	{
   194  		Name: "Should be able to search matching by mentions",
   195  		Fn:   testSearchShouldBeAbleToMatchByMentions,
   196  		Tags: []string{EngineAll},
   197  	},
   198  	{
   199  		Name: "Should be able to search in deleted/archived channels",
   200  		Fn:   testSearchInDeletedOrArchivedChannels,
   201  		Tags: []string{EngineMySql, EnginePostgres},
   202  	},
   203  	{
   204  		Name:        "Should be able to search terms with dashes",
   205  		Fn:          testSearchTermsWithDashes,
   206  		Tags:        []string{EngineAll},
   207  		Skip:        true,
   208  		SkipMessage: "Not working",
   209  	},
   210  	{
   211  		Name: "Should be able to search terms with dots",
   212  		Fn:   testSearchTermsWithDots,
   213  		Tags: []string{EnginePostgres, EngineElasticSearch},
   214  	},
   215  	{
   216  		Name: "Should be able to search terms with underscores",
   217  		Fn:   testSearchTermsWithUnderscores,
   218  		Tags: []string{EngineMySql, EngineElasticSearch},
   219  	},
   220  	{
   221  		Name: "Should be able to search posts made by bot accounts",
   222  		Fn:   testSearchBotAccountsPosts,
   223  		Tags: []string{EngineAll},
   224  	},
   225  	{
   226  		Name: "Should be able to combine stemming and wildcards",
   227  		Fn:   testSupportStemmingAndWildcards,
   228  		Tags: []string{EngineElasticSearch},
   229  	},
   230  	{
   231  		Name: "Should support wildcard outside quotes",
   232  		Fn:   testSupportWildcardOutsideQuotes,
   233  		Tags: []string{EngineElasticSearch},
   234  	},
   235  	{
   236  		Name: "Should support hashtags with 3 or more characters",
   237  		Fn:   testHashtagSearchShouldSupportThreeOrMoreCharacters,
   238  		Tags: []string{EngineAll},
   239  	},
   240  	{
   241  		Name: "Should not support slash as character separator",
   242  		Fn:   testSlashShouldNotBeCharSeparator,
   243  		Tags: []string{EngineMySql, EngineElasticSearch},
   244  	},
   245  	{
   246  		Name: "Should be able to search emails without quoting them",
   247  		Fn:   testSearchEmailsWithoutQuotes,
   248  		Tags: []string{EngineElasticSearch},
   249  	},
   250  	{
   251  		Name: "Should be able to search in comments",
   252  		Fn:   testSupportSearchInComments,
   253  		Tags: []string{EngineAll},
   254  	},
   255  	{
   256  		Name: "Should be able to search terms within links",
   257  		Fn:   testSupportSearchTermsWithinLinks,
   258  		Tags: []string{EngineMySql, EngineElasticSearch},
   259  	},
   260  	{
   261  		Name: "Should not return links that are embedded in markdown",
   262  		Fn:   testShouldNotReturnLinksEmbeddedInMarkdown,
   263  		Tags: []string{EnginePostgres, EngineElasticSearch},
   264  	},
   265  }
   266  
   267  func TestSearchPostStore(t *testing.T, s store.Store, testEngine *SearchTestEngine) {
   268  	th := &SearchTestHelper{
   269  		Store: s,
   270  	}
   271  	err := th.SetupBasicFixtures()
   272  	require.NoError(t, err)
   273  	defer th.CleanFixtures()
   274  
   275  	runTestSearch(t, testEngine, searchPostStoreTests, th)
   276  }
   277  
   278  func testSearchPostsIncludingDMs(t *testing.T, th *SearchTestHelper) {
   279  	direct, err := th.createDirectChannel(th.Team.Id, "direct", "direct", []*model.User{th.User, th.User2})
   280  	require.NoError(t, err)
   281  	defer th.deleteChannel(direct)
   282  
   283  	p1, err := th.createPost(th.User.Id, direct.Id, "dm test", "", model.POST_DEFAULT, 0, false)
   284  	require.NoError(t, err)
   285  	_, err = th.createPost(th.User.Id, direct.Id, "dm other", "", model.POST_DEFAULT, 0, false)
   286  	require.NoError(t, err)
   287  	p2, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "channel test", "", model.POST_DEFAULT, 0, false)
   288  	require.NoError(t, err)
   289  	defer th.deleteUserPosts(th.User.Id)
   290  
   291  	params := &model.SearchParams{Terms: "test"}
   292  	results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   293  	require.NoError(t, err)
   294  
   295  	require.Len(t, results.Posts, 2)
   296  	th.checkPostInSearchResults(t, p1.Id, results.Posts)
   297  	th.checkPostInSearchResults(t, p2.Id, results.Posts)
   298  }
   299  
   300  func testSearchPostsWithPagination(t *testing.T, th *SearchTestHelper) {
   301  	direct, err := th.createDirectChannel(th.Team.Id, "direct", "direct", []*model.User{th.User, th.User2})
   302  	require.NoError(t, err)
   303  	defer th.deleteChannel(direct)
   304  
   305  	p1, err := th.createPost(th.User.Id, direct.Id, "dm test", "", model.POST_DEFAULT, 10000, false)
   306  	require.NoError(t, err)
   307  	_, err = th.createPost(th.User.Id, direct.Id, "dm other", "", model.POST_DEFAULT, 20000, false)
   308  	require.NoError(t, err)
   309  	p2, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "channel test", "", model.POST_DEFAULT, 0, false)
   310  	require.NoError(t, err)
   311  	defer th.deleteUserPosts(th.User.Id)
   312  
   313  	params := &model.SearchParams{Terms: "test"}
   314  	results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 1)
   315  	require.NoError(t, err)
   316  
   317  	require.Len(t, results.Posts, 1)
   318  	th.checkPostInSearchResults(t, p2.Id, results.Posts)
   319  
   320  	results, err = th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 1, 1)
   321  	require.NoError(t, err)
   322  
   323  	require.Len(t, results.Posts, 1)
   324  	th.checkPostInSearchResults(t, p1.Id, results.Posts)
   325  }
   326  
   327  func testSearchReturnPinnedAndUnpinned(t *testing.T, th *SearchTestHelper) {
   328  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "channel test unpinned", "", model.POST_DEFAULT, 0, false)
   329  	require.NoError(t, err)
   330  	p2, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "channel test pinned", "", model.POST_DEFAULT, 0, true)
   331  	require.NoError(t, err)
   332  	defer th.deleteUserPosts(th.User.Id)
   333  
   334  	params := &model.SearchParams{Terms: "test"}
   335  	results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   336  	require.NoError(t, err)
   337  
   338  	require.Len(t, results.Posts, 2)
   339  	th.checkPostInSearchResults(t, p1.Id, results.Posts)
   340  	th.checkPostInSearchResults(t, p2.Id, results.Posts)
   341  }
   342  
   343  func testSearchExactPhraseInQuotes(t *testing.T, th *SearchTestHelper) {
   344  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "channel test 1 2 3", "", model.POST_DEFAULT, 0, false)
   345  	require.NoError(t, err)
   346  	_, err = th.createPost(th.User.Id, th.ChannelBasic.Id, "channel test 123", "", model.POST_DEFAULT, 0, false)
   347  	require.NoError(t, err)
   348  	_, err = th.createPost(th.User.Id, th.ChannelBasic.Id, "channel something test 1 2 3", "", model.POST_DEFAULT, 0, false)
   349  	require.NoError(t, err)
   350  	_, err = th.createPost(th.User.Id, th.ChannelBasic.Id, "channel 1 2 3", "", model.POST_DEFAULT, 0, false)
   351  	require.NoError(t, err)
   352  
   353  	defer th.deleteUserPosts(th.User.Id)
   354  
   355  	params := &model.SearchParams{Terms: "\"channel test 1 2 3\""}
   356  	results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   357  	require.NoError(t, err)
   358  
   359  	require.Len(t, results.Posts, 1)
   360  	th.checkPostInSearchResults(t, p1.Id, results.Posts)
   361  }
   362  
   363  func testSearchEmailAddresses(t *testing.T, th *SearchTestHelper) {
   364  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "test email test@test.com", "", model.POST_DEFAULT, 0, false)
   365  	require.NoError(t, err)
   366  	_, err = th.createPost(th.User.Id, th.ChannelBasic.Id, "test email test2@test.com", "", model.POST_DEFAULT, 0, false)
   367  	require.NoError(t, err)
   368  	defer th.deleteUserPosts(th.User.Id)
   369  
   370  	t.Run("Should search email addresses enclosed by quotes", func(t *testing.T) {
   371  		params := &model.SearchParams{Terms: "\"test@test.com\""}
   372  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   373  		require.NoError(t, err)
   374  
   375  		require.Len(t, results.Posts, 1)
   376  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
   377  	})
   378  
   379  	t.Run("Should search email addresses without quotes", func(t *testing.T) {
   380  		params := &model.SearchParams{Terms: "test@test.com"}
   381  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   382  		require.NoError(t, err)
   383  
   384  		require.Len(t, results.Posts, 1)
   385  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
   386  	})
   387  }
   388  
   389  func testSearchMarkdownUnderscores(t *testing.T, th *SearchTestHelper) {
   390  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "_start middle end_ _another_", "", model.POST_DEFAULT, 0, false)
   391  	require.NoError(t, err)
   392  	defer th.deleteUserPosts(th.User.Id)
   393  
   394  	t.Run("Should search the start inside the markdown underscore", func(t *testing.T) {
   395  		params := &model.SearchParams{Terms: "start"}
   396  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   397  		require.NoError(t, err)
   398  
   399  		require.Len(t, results.Posts, 1)
   400  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
   401  	})
   402  
   403  	t.Run("Should search a word in the middle of the markdown underscore", func(t *testing.T) {
   404  		params := &model.SearchParams{Terms: "middle"}
   405  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   406  		require.NoError(t, err)
   407  
   408  		require.Len(t, results.Posts, 1)
   409  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
   410  	})
   411  
   412  	t.Run("Should search in the end of the markdown underscore", func(t *testing.T) {
   413  		params := &model.SearchParams{Terms: "end"}
   414  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   415  		require.NoError(t, err)
   416  
   417  		require.Len(t, results.Posts, 1)
   418  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
   419  	})
   420  
   421  	t.Run("Should search inside markdown underscore", func(t *testing.T) {
   422  		params := &model.SearchParams{Terms: "another"}
   423  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   424  		require.NoError(t, err)
   425  
   426  		require.Len(t, results.Posts, 1)
   427  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
   428  	})
   429  }
   430  
   431  func testSearchNonLatinWords(t *testing.T, th *SearchTestHelper) {
   432  	t.Run("Should be able to search chinese words", func(t *testing.T) {
   433  		p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "你好", "", model.POST_DEFAULT, 0, false)
   434  		require.NoError(t, err)
   435  		p2, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "你", "", model.POST_DEFAULT, 0, false)
   436  		require.NoError(t, err)
   437  		defer th.deleteUserPosts(th.User.Id)
   438  
   439  		t.Run("Should search one word", func(t *testing.T) {
   440  			params := &model.SearchParams{Terms: "你"}
   441  			results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   442  			require.NoError(t, err)
   443  
   444  			require.Len(t, results.Posts, 1)
   445  			th.checkPostInSearchResults(t, p2.Id, results.Posts)
   446  		})
   447  		t.Run("Should search two words", func(t *testing.T) {
   448  			params := &model.SearchParams{Terms: "你好"}
   449  			results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   450  			require.NoError(t, err)
   451  
   452  			require.Len(t, results.Posts, 1)
   453  			th.checkPostInSearchResults(t, p1.Id, results.Posts)
   454  		})
   455  		t.Run("Should search with wildcard", func(t *testing.T) {
   456  			params := &model.SearchParams{Terms: "你*"}
   457  			results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   458  			require.NoError(t, err)
   459  
   460  			require.Len(t, results.Posts, 2)
   461  			th.checkPostInSearchResults(t, p1.Id, results.Posts)
   462  			th.checkPostInSearchResults(t, p2.Id, results.Posts)
   463  		})
   464  	})
   465  	t.Run("Should be able to search cyrillic words", func(t *testing.T) {
   466  		p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "слово test", "", model.POST_DEFAULT, 0, false)
   467  		require.NoError(t, err)
   468  		defer th.deleteUserPosts(th.User.Id)
   469  
   470  		t.Run("Should search one word", func(t *testing.T) {
   471  			params := &model.SearchParams{Terms: "слово"}
   472  			results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   473  			require.NoError(t, err)
   474  
   475  			require.Len(t, results.Posts, 1)
   476  			th.checkPostInSearchResults(t, p1.Id, results.Posts)
   477  		})
   478  		t.Run("Should search using wildcard", func(t *testing.T) {
   479  			params := &model.SearchParams{Terms: "слов*"}
   480  			results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   481  			require.NoError(t, err)
   482  
   483  			require.Len(t, results.Posts, 1)
   484  			th.checkPostInSearchResults(t, p1.Id, results.Posts)
   485  		})
   486  	})
   487  
   488  	t.Run("Should be able to search japanese words", func(t *testing.T) {
   489  		p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "本", "", model.POST_DEFAULT, 0, false)
   490  		require.NoError(t, err)
   491  		p2, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "本木", "", model.POST_DEFAULT, 0, false)
   492  		require.NoError(t, err)
   493  		defer th.deleteUserPosts(th.User.Id)
   494  
   495  		t.Run("Should search one word", func(t *testing.T) {
   496  			params := &model.SearchParams{Terms: "本"}
   497  			results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   498  			require.NoError(t, err)
   499  
   500  			require.Len(t, results.Posts, 2)
   501  			th.checkPostInSearchResults(t, p1.Id, results.Posts)
   502  			th.checkPostInSearchResults(t, p2.Id, results.Posts)
   503  		})
   504  		t.Run("Should search two words", func(t *testing.T) {
   505  			params := &model.SearchParams{Terms: "本木"}
   506  			results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   507  			require.NoError(t, err)
   508  
   509  			require.Len(t, results.Posts, 1)
   510  			th.checkPostInSearchResults(t, p2.Id, results.Posts)
   511  		})
   512  		t.Run("Should search with wildcard", func(t *testing.T) {
   513  			params := &model.SearchParams{Terms: "本*"}
   514  			results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   515  			require.NoError(t, err)
   516  
   517  			require.Len(t, results.Posts, 2)
   518  			th.checkPostInSearchResults(t, p1.Id, results.Posts)
   519  			th.checkPostInSearchResults(t, p2.Id, results.Posts)
   520  		})
   521  	})
   522  
   523  	t.Run("Should be able to search korean words", func(t *testing.T) {
   524  		p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "불", "", model.POST_DEFAULT, 0, false)
   525  		require.NoError(t, err)
   526  		p2, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "불다", "", model.POST_DEFAULT, 0, false)
   527  		require.NoError(t, err)
   528  		defer th.deleteUserPosts(th.User.Id)
   529  
   530  		t.Run("Should search one word", func(t *testing.T) {
   531  			params := &model.SearchParams{Terms: "불"}
   532  			results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   533  			require.NoError(t, err)
   534  
   535  			require.Len(t, results.Posts, 1)
   536  			th.checkPostInSearchResults(t, p1.Id, results.Posts)
   537  		})
   538  		t.Run("Should search two words", func(t *testing.T) {
   539  			params := &model.SearchParams{Terms: "불다"}
   540  			results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   541  			require.NoError(t, err)
   542  
   543  			require.Len(t, results.Posts, 1)
   544  			th.checkPostInSearchResults(t, p2.Id, results.Posts)
   545  		})
   546  		t.Run("Should search with wildcard", func(t *testing.T) {
   547  			params := &model.SearchParams{Terms: "불*"}
   548  			results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   549  			require.NoError(t, err)
   550  
   551  			require.Len(t, results.Posts, 2)
   552  			th.checkPostInSearchResults(t, p1.Id, results.Posts)
   553  			th.checkPostInSearchResults(t, p2.Id, results.Posts)
   554  		})
   555  	})
   556  }
   557  
   558  func testSearchAlternativeSpellings(t *testing.T, th *SearchTestHelper) {
   559  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "Straße test", "", model.POST_DEFAULT, 0, false)
   560  	require.NoError(t, err)
   561  	p2, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "Strasse test", "", model.POST_DEFAULT, 0, false)
   562  	require.NoError(t, err)
   563  	defer th.deleteUserPosts(th.User.Id)
   564  
   565  	params := &model.SearchParams{Terms: "Straße"}
   566  	results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   567  	require.NoError(t, err)
   568  
   569  	require.Len(t, results.Posts, 2)
   570  	th.checkPostInSearchResults(t, p1.Id, results.Posts)
   571  	th.checkPostInSearchResults(t, p2.Id, results.Posts)
   572  
   573  	params = &model.SearchParams{Terms: "Strasse"}
   574  	results, err = th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   575  	require.NoError(t, err)
   576  
   577  	require.Len(t, results.Posts, 2)
   578  	th.checkPostInSearchResults(t, p1.Id, results.Posts)
   579  	th.checkPostInSearchResults(t, p2.Id, results.Posts)
   580  }
   581  
   582  func testSearchAlternativeSpellingsAccents(t *testing.T, th *SearchTestHelper) {
   583  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "café", "", model.POST_DEFAULT, 0, false)
   584  	require.NoError(t, err)
   585  	p2, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "café", "", model.POST_DEFAULT, 0, false)
   586  	require.NoError(t, err)
   587  	defer th.deleteUserPosts(th.User.Id)
   588  
   589  	params := &model.SearchParams{Terms: "café"}
   590  	results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   591  	require.NoError(t, err)
   592  
   593  	require.Len(t, results.Posts, 2)
   594  	th.checkPostInSearchResults(t, p1.Id, results.Posts)
   595  	th.checkPostInSearchResults(t, p2.Id, results.Posts)
   596  
   597  	params = &model.SearchParams{Terms: "café"}
   598  	results, err = th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   599  	require.NoError(t, err)
   600  
   601  	require.Len(t, results.Posts, 2)
   602  	th.checkPostInSearchResults(t, p1.Id, results.Posts)
   603  	th.checkPostInSearchResults(t, p2.Id, results.Posts)
   604  
   605  	params = &model.SearchParams{Terms: "cafe"}
   606  	results, err = th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   607  	require.NoError(t, err)
   608  
   609  	require.Len(t, results.Posts, 0)
   610  }
   611  
   612  func testSearchOrExcludePostsBySpecificUser(t *testing.T, th *SearchTestHelper) {
   613  	p1, err := th.createPost(th.User.Id, th.ChannelPrivate.Id, "test fromuser", "", model.POST_DEFAULT, 0, false)
   614  	require.NoError(t, err)
   615  	_, err = th.createPost(th.User2.Id, th.ChannelPrivate.Id, "test fromuser 2", "", model.POST_DEFAULT, 0, false)
   616  	require.NoError(t, err)
   617  	defer th.deleteUserPosts(th.User.Id)
   618  	defer th.deleteUserPosts(th.User2.Id)
   619  
   620  	params := &model.SearchParams{
   621  		Terms:     "fromuser",
   622  		FromUsers: []string{th.User.Id},
   623  	}
   624  	results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   625  	require.NoError(t, err)
   626  
   627  	require.Len(t, results.Posts, 1)
   628  	th.checkPostInSearchResults(t, p1.Id, results.Posts)
   629  }
   630  
   631  func testSearchOrExcludePostsInChannel(t *testing.T, th *SearchTestHelper) {
   632  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "test fromuser", "", model.POST_DEFAULT, 0, false)
   633  	require.NoError(t, err)
   634  	_, err = th.createPost(th.User2.Id, th.ChannelPrivate.Id, "test fromuser 2", "", model.POST_DEFAULT, 0, false)
   635  	require.NoError(t, err)
   636  	defer th.deleteUserPosts(th.User.Id)
   637  	defer th.deleteUserPosts(th.User2.Id)
   638  
   639  	params := &model.SearchParams{
   640  		Terms:      "fromuser",
   641  		InChannels: []string{th.ChannelBasic.Id},
   642  	}
   643  	results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   644  	require.NoError(t, err)
   645  
   646  	require.Len(t, results.Posts, 1)
   647  	th.checkPostInSearchResults(t, p1.Id, results.Posts)
   648  }
   649  
   650  func testSearchOrExcludePostsInDMGM(t *testing.T, th *SearchTestHelper) {
   651  	direct, err := th.createDirectChannel(th.Team.Id, "direct", "direct", []*model.User{th.User, th.User2})
   652  	require.NoError(t, err)
   653  	defer th.deleteChannel(direct)
   654  
   655  	group, err := th.createGroupChannel(th.Team.Id, "test group", []*model.User{th.User, th.User2})
   656  	require.NoError(t, err)
   657  	defer th.deleteChannel(group)
   658  
   659  	p1, err := th.createPost(th.User.Id, direct.Id, "test fromuser", "", model.POST_DEFAULT, 0, false)
   660  	require.NoError(t, err)
   661  	p2, err := th.createPost(th.User2.Id, group.Id, "test fromuser 2", "", model.POST_DEFAULT, 0, false)
   662  	require.NoError(t, err)
   663  	defer th.deleteUserPosts(th.User.Id)
   664  	defer th.deleteUserPosts(th.User2.Id)
   665  
   666  	t.Run("Should be able to search in both DM and GM channels", func(t *testing.T) {
   667  		params := &model.SearchParams{
   668  			Terms:      "fromuser",
   669  			InChannels: []string{direct.Id, group.Id},
   670  		}
   671  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   672  		require.NoError(t, err)
   673  
   674  		require.Len(t, results.Posts, 2)
   675  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
   676  		th.checkPostInSearchResults(t, p2.Id, results.Posts)
   677  	})
   678  
   679  	t.Run("Should be able to search only in DM channel", func(t *testing.T) {
   680  		params := &model.SearchParams{
   681  			Terms:      "fromuser",
   682  			InChannels: []string{direct.Id},
   683  		}
   684  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   685  		require.NoError(t, err)
   686  
   687  		require.Len(t, results.Posts, 1)
   688  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
   689  	})
   690  
   691  	t.Run("Should be able to search only in GM channel", func(t *testing.T) {
   692  		params := &model.SearchParams{
   693  			Terms:      "fromuser",
   694  			InChannels: []string{group.Id},
   695  		}
   696  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   697  		require.NoError(t, err)
   698  
   699  		require.Len(t, results.Posts, 1)
   700  		th.checkPostInSearchResults(t, p2.Id, results.Posts)
   701  	})
   702  }
   703  
   704  func testFilterMessagesInSpecificDate(t *testing.T, th *SearchTestHelper) {
   705  	creationDate := model.GetMillisForTime(time.Date(2020, 03, 22, 12, 0, 0, 0, time.UTC))
   706  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "test in specific date", "", model.POST_DEFAULT, creationDate, false)
   707  	require.NoError(t, err)
   708  	creationDate2 := model.GetMillisForTime(time.Date(2020, 03, 23, 0, 0, 0, 0, time.UTC))
   709  	p2, err := th.createPost(th.User.Id, th.ChannelPrivate.Id, "test in the present", "", model.POST_DEFAULT, creationDate2, false)
   710  	require.NoError(t, err)
   711  	creationDate3 := model.GetMillisForTime(time.Date(2020, 03, 21, 23, 59, 59, 0, time.UTC))
   712  	p3, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "test in the present", "", model.POST_DEFAULT, creationDate3, false)
   713  	require.NoError(t, err)
   714  	defer th.deleteUserPosts(th.User.Id)
   715  
   716  	t.Run("Should be able to search posts on date", func(t *testing.T) {
   717  		params := &model.SearchParams{
   718  			Terms:  "test",
   719  			OnDate: "2020-03-22",
   720  		}
   721  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   722  		require.NoError(t, err)
   723  
   724  		require.Len(t, results.Posts, 1)
   725  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
   726  	})
   727  	t.Run("Should be able to exclude posts on date", func(t *testing.T) {
   728  		params := &model.SearchParams{
   729  			Terms:        "test",
   730  			ExcludedDate: "2020-03-22",
   731  		}
   732  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   733  		require.NoError(t, err)
   734  
   735  		require.Len(t, results.Posts, 2)
   736  		th.checkPostInSearchResults(t, p2.Id, results.Posts)
   737  		th.checkPostInSearchResults(t, p3.Id, results.Posts)
   738  	})
   739  }
   740  
   741  func testFilterMessagesBeforeSpecificDate(t *testing.T, th *SearchTestHelper) {
   742  	creationDate := model.GetMillisForTime(time.Date(2020, 03, 01, 12, 0, 0, 0, time.UTC))
   743  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "test in specific date", "", model.POST_DEFAULT, creationDate, false)
   744  	require.NoError(t, err)
   745  	creationDate2 := model.GetMillisForTime(time.Date(2020, 03, 22, 23, 59, 59, 0, time.UTC))
   746  	p2, err := th.createPost(th.User.Id, th.ChannelPrivate.Id, "test in specific date 2", "", model.POST_DEFAULT, creationDate2, false)
   747  	require.NoError(t, err)
   748  	creationDate3 := model.GetMillisForTime(time.Date(2020, 03, 26, 16, 55, 0, 0, time.UTC))
   749  	p3, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "test in the present", "", model.POST_DEFAULT, creationDate3, false)
   750  	require.NoError(t, err)
   751  	defer th.deleteUserPosts(th.User.Id)
   752  
   753  	t.Run("Should be able to search posts before a date", func(t *testing.T) {
   754  		params := &model.SearchParams{
   755  			Terms:      "test",
   756  			BeforeDate: "2020-03-23",
   757  		}
   758  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   759  		require.NoError(t, err)
   760  
   761  		require.Len(t, results.Posts, 2)
   762  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
   763  		th.checkPostInSearchResults(t, p2.Id, results.Posts)
   764  	})
   765  
   766  	t.Run("Should be able to exclude posts before a date", func(t *testing.T) {
   767  		params := &model.SearchParams{
   768  			Terms:              "test",
   769  			ExcludedBeforeDate: "2020-03-23",
   770  		}
   771  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   772  		require.NoError(t, err)
   773  
   774  		require.Len(t, results.Posts, 1)
   775  		th.checkPostInSearchResults(t, p3.Id, results.Posts)
   776  	})
   777  }
   778  
   779  func testFilterMessagesAfterSpecificDate(t *testing.T, th *SearchTestHelper) {
   780  	creationDate := model.GetMillisForTime(time.Date(2020, 03, 01, 12, 0, 0, 0, time.UTC))
   781  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "test in specific date", "", model.POST_DEFAULT, creationDate, false)
   782  	require.NoError(t, err)
   783  	creationDate2 := model.GetMillisForTime(time.Date(2020, 03, 22, 23, 59, 59, 0, time.UTC))
   784  	p2, err := th.createPost(th.User.Id, th.ChannelPrivate.Id, "test in specific date 2", "", model.POST_DEFAULT, creationDate2, false)
   785  	require.NoError(t, err)
   786  	creationDate3 := model.GetMillisForTime(time.Date(2020, 03, 26, 16, 55, 0, 0, time.UTC))
   787  	p3, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "test in the present", "", model.POST_DEFAULT, creationDate3, false)
   788  	require.NoError(t, err)
   789  	defer th.deleteUserPosts(th.User.Id)
   790  
   791  	t.Run("Should be able to search posts after a date", func(t *testing.T) {
   792  		params := &model.SearchParams{
   793  			Terms:     "test",
   794  			AfterDate: "2020-03-23",
   795  		}
   796  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   797  		require.NoError(t, err)
   798  
   799  		require.Len(t, results.Posts, 1)
   800  		th.checkPostInSearchResults(t, p3.Id, results.Posts)
   801  	})
   802  
   803  	t.Run("Should be able to exclude posts after a date", func(t *testing.T) {
   804  		params := &model.SearchParams{
   805  			Terms:             "test",
   806  			ExcludedAfterDate: "2020-03-23",
   807  		}
   808  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   809  		require.NoError(t, err)
   810  
   811  		require.Len(t, results.Posts, 2)
   812  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
   813  		th.checkPostInSearchResults(t, p2.Id, results.Posts)
   814  	})
   815  }
   816  
   817  func testFilterMessagesWithATerm(t *testing.T, th *SearchTestHelper) {
   818  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "one two three", "", model.POST_DEFAULT, 0, false)
   819  	require.NoError(t, err)
   820  	p2, err := th.createPost(th.User.Id, th.ChannelPrivate.Id, "one four five six", "", model.POST_DEFAULT, 0, false)
   821  	require.NoError(t, err)
   822  	_, err = th.createPost(th.User.Id, th.ChannelBasic.Id, "one seven eight nine", "", model.POST_DEFAULT, 0, false)
   823  	require.NoError(t, err)
   824  	defer th.deleteUserPosts(th.User.Id)
   825  
   826  	t.Run("Should exclude terms", func(t *testing.T) {
   827  		params := &model.SearchParams{
   828  			Terms:         "one",
   829  			ExcludedTerms: "five eight",
   830  		}
   831  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   832  		require.NoError(t, err)
   833  
   834  		require.Len(t, results.Posts, 1)
   835  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
   836  	})
   837  
   838  	t.Run("Should exclude quoted terms", func(t *testing.T) {
   839  		params := &model.SearchParams{
   840  			Terms:         "one",
   841  			ExcludedTerms: "\"eight nine\"",
   842  		}
   843  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   844  		require.NoError(t, err)
   845  
   846  		require.Len(t, results.Posts, 2)
   847  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
   848  		th.checkPostInSearchResults(t, p2.Id, results.Posts)
   849  	})
   850  }
   851  
   852  func testSearchUsingBooleanOperators(t *testing.T, th *SearchTestHelper) {
   853  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "one two three message", "", model.POST_DEFAULT, 0, false)
   854  	require.NoError(t, err)
   855  	p2, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "two messages", "", model.POST_DEFAULT, 0, false)
   856  	require.NoError(t, err)
   857  	_, err = th.createPost(th.User.Id, th.ChannelBasic.Id, "another message", "", model.POST_DEFAULT, 0, false)
   858  	require.NoError(t, err)
   859  	defer th.deleteUserPosts(th.User.Id)
   860  
   861  	t.Run("Should search posts using OR operator", func(t *testing.T) {
   862  		params := &model.SearchParams{
   863  			Terms:   "one two",
   864  			OrTerms: true,
   865  		}
   866  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   867  		require.NoError(t, err)
   868  
   869  		require.Len(t, results.Posts, 2)
   870  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
   871  		th.checkPostInSearchResults(t, p2.Id, results.Posts)
   872  	})
   873  
   874  	t.Run("Should search posts using AND operator", func(t *testing.T) {
   875  		params := &model.SearchParams{
   876  			Terms:   "one two",
   877  			OrTerms: false,
   878  		}
   879  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   880  		require.NoError(t, err)
   881  
   882  		require.Len(t, results.Posts, 1)
   883  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
   884  	})
   885  }
   886  
   887  func testSearchUsingCombinedFilters(t *testing.T, th *SearchTestHelper) {
   888  	creationDate := model.GetMillisForTime(time.Date(2020, 03, 01, 12, 0, 0, 0, time.UTC))
   889  	p1, err := th.createPost(th.User.Id, th.ChannelPrivate.Id, "one two three message", "", model.POST_DEFAULT, creationDate, false)
   890  	require.NoError(t, err)
   891  	creationDate2 := model.GetMillisForTime(time.Date(2020, 03, 10, 12, 0, 0, 0, time.UTC))
   892  	p2, err := th.createPost(th.User2.Id, th.ChannelPrivate.Id, "two messages", "", model.POST_DEFAULT, creationDate2, false)
   893  	require.NoError(t, err)
   894  	creationDate3 := model.GetMillisForTime(time.Date(2020, 03, 20, 12, 0, 0, 0, time.UTC))
   895  	p3, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "two another message", "", model.POST_DEFAULT, creationDate3, false)
   896  	require.NoError(t, err)
   897  	defer th.deleteUserPosts(th.User.Id)
   898  	defer th.deleteUserPosts(th.User2.Id)
   899  
   900  	t.Run("Should search combining from user and in channel filters", func(t *testing.T) {
   901  		params := &model.SearchParams{
   902  			Terms:      "two",
   903  			FromUsers:  []string{th.User2.Id},
   904  			InChannels: []string{th.ChannelPrivate.Id},
   905  		}
   906  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   907  		require.NoError(t, err)
   908  
   909  		require.Len(t, results.Posts, 1)
   910  		th.checkPostInSearchResults(t, p2.Id, results.Posts)
   911  	})
   912  
   913  	t.Run("Should search combining excluding users and in channel filters", func(t *testing.T) {
   914  		params := &model.SearchParams{
   915  			Terms:         "two",
   916  			ExcludedUsers: []string{th.User2.Id},
   917  			InChannels:    []string{th.ChannelPrivate.Id},
   918  		}
   919  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   920  		require.NoError(t, err)
   921  
   922  		require.Len(t, results.Posts, 1)
   923  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
   924  	})
   925  
   926  	t.Run("Should search combining excluding dates and in channel filters", func(t *testing.T) {
   927  		params := &model.SearchParams{
   928  			Terms:              "two",
   929  			ExcludedBeforeDate: "2020-03-09",
   930  			ExcludedAfterDate:  "2020-03-11",
   931  			InChannels:         []string{th.ChannelPrivate.Id},
   932  		}
   933  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   934  		require.NoError(t, err)
   935  
   936  		require.Len(t, results.Posts, 1)
   937  		th.checkPostInSearchResults(t, p2.Id, results.Posts)
   938  	})
   939  	t.Run("Should search combining excluding dates and in channel filters", func(t *testing.T) {
   940  		params := &model.SearchParams{
   941  			Terms:            "two",
   942  			AfterDate:        "2020-03-11",
   943  			ExcludedChannels: []string{th.ChannelPrivate.Id},
   944  		}
   945  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   946  		require.NoError(t, err)
   947  
   948  		require.Len(t, results.Posts, 1)
   949  		th.checkPostInSearchResults(t, p3.Id, results.Posts)
   950  	})
   951  }
   952  
   953  func testSearchIgnoringStopWords(t *testing.T, th *SearchTestHelper) {
   954  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "the search for a bunch of stop words", "", model.POST_DEFAULT, 0, false)
   955  	require.NoError(t, err)
   956  	p2, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "the objective is to avoid a bunch of stop words", "", model.POST_DEFAULT, 0, false)
   957  	require.NoError(t, err)
   958  	p3, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "in the a on to where you", "", model.POST_DEFAULT, 0, false)
   959  	require.NoError(t, err)
   960  	p4, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "where is the car?", "", model.POST_DEFAULT, 0, false)
   961  	require.NoError(t, err)
   962  	defer th.deleteUserPosts(th.User.Id)
   963  
   964  	t.Run("Should avoid stop word 'the'", func(t *testing.T) {
   965  		params := &model.SearchParams{
   966  			Terms: "the search",
   967  		}
   968  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   969  		require.NoError(t, err)
   970  
   971  		require.Len(t, results.Posts, 1)
   972  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
   973  	})
   974  
   975  	t.Run("Should avoid stop word 'a'", func(t *testing.T) {
   976  		params := &model.SearchParams{
   977  			Terms: "a avoid",
   978  		}
   979  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   980  		require.NoError(t, err)
   981  
   982  		require.Len(t, results.Posts, 1)
   983  		th.checkPostInSearchResults(t, p2.Id, results.Posts)
   984  	})
   985  
   986  	t.Run("Should avoid stop word 'in'", func(t *testing.T) {
   987  		params := &model.SearchParams{
   988  			Terms: "in where you",
   989  		}
   990  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
   991  		require.NoError(t, err)
   992  
   993  		require.Len(t, results.Posts, 1)
   994  		th.checkPostInSearchResults(t, p3.Id, results.Posts)
   995  	})
   996  
   997  	t.Run("Should avoid stop words 'where', 'is' and 'the'", func(t *testing.T) {
   998  		results, err := th.Store.Post().Search(th.Team.Id, th.User.Id, &model.SearchParams{Terms: "where is the car"})
   999  		require.NoError(t, err)
  1000  		require.Len(t, results.Posts, 1)
  1001  		th.checkPostInSearchResults(t, p4.Id, results.Posts)
  1002  	})
  1003  
  1004  	t.Run("Should remove all terms and return empty list", func(t *testing.T) {
  1005  		results, err := th.Store.Post().Search(th.Team.Id, th.User.Id, &model.SearchParams{Terms: "where is the"})
  1006  		require.NoError(t, err)
  1007  		require.Empty(t, results.Posts)
  1008  	})
  1009  }
  1010  
  1011  func testSupportStemming(t *testing.T, th *SearchTestHelper) {
  1012  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "search post", "", model.POST_DEFAULT, 0, false)
  1013  	require.NoError(t, err)
  1014  	p2, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "searching post", "", model.POST_DEFAULT, 0, false)
  1015  	require.NoError(t, err)
  1016  	_, err = th.createPost(th.User.Id, th.ChannelBasic.Id, "another post", "", model.POST_DEFAULT, 0, false)
  1017  	require.NoError(t, err)
  1018  	defer th.deleteUserPosts(th.User.Id)
  1019  
  1020  	params := &model.SearchParams{
  1021  		Terms: "search",
  1022  	}
  1023  	results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1024  	require.NoError(t, err)
  1025  
  1026  	require.Len(t, results.Posts, 2)
  1027  	th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1028  	th.checkPostInSearchResults(t, p2.Id, results.Posts)
  1029  }
  1030  
  1031  func testSupportWildcards(t *testing.T, th *SearchTestHelper) {
  1032  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "search post", "", model.POST_DEFAULT, 0, false)
  1033  	require.NoError(t, err)
  1034  	p2, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "searching", "", model.POST_DEFAULT, 0, false)
  1035  	require.NoError(t, err)
  1036  	_, err = th.createPost(th.User.Id, th.ChannelBasic.Id, "another post", "", model.POST_DEFAULT, 0, false)
  1037  	require.NoError(t, err)
  1038  	defer th.deleteUserPosts(th.User.Id)
  1039  
  1040  	t.Run("Simple wildcard-only search", func(t *testing.T) {
  1041  		params := &model.SearchParams{
  1042  			Terms: "search*",
  1043  		}
  1044  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1045  		require.NoError(t, err)
  1046  
  1047  		require.Len(t, results.Posts, 2)
  1048  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1049  		th.checkPostInSearchResults(t, p2.Id, results.Posts)
  1050  	})
  1051  
  1052  	t.Run("Wildcard search with another term placed after", func(t *testing.T) {
  1053  		params := &model.SearchParams{
  1054  			Terms: "sear* post",
  1055  		}
  1056  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1057  		require.NoError(t, err)
  1058  
  1059  		require.Len(t, results.Posts, 1)
  1060  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1061  	})
  1062  }
  1063  
  1064  func testNotSupportPrecedingWildcards(t *testing.T, th *SearchTestHelper) {
  1065  	_, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "search post", "", model.POST_DEFAULT, 0, false)
  1066  	require.NoError(t, err)
  1067  	_, err = th.createPost(th.User.Id, th.ChannelBasic.Id, "searching post", "", model.POST_DEFAULT, 0, false)
  1068  	require.NoError(t, err)
  1069  	_, err = th.createPost(th.User.Id, th.ChannelBasic.Id, "another post", "", model.POST_DEFAULT, 0, false)
  1070  	require.NoError(t, err)
  1071  	defer th.deleteUserPosts(th.User.Id)
  1072  
  1073  	params := &model.SearchParams{
  1074  		Terms: "*earch",
  1075  	}
  1076  	results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1077  	require.NoError(t, err)
  1078  
  1079  	require.Len(t, results.Posts, 0)
  1080  }
  1081  
  1082  func testSearchDiscardWildcardAlone(t *testing.T, th *SearchTestHelper) {
  1083  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "qwerty", "", model.POST_DEFAULT, 0, false)
  1084  	require.NoError(t, err)
  1085  	_, err = th.createPost(th.User.Id, th.ChannelBasic.Id, "qwertyjkl", "", model.POST_DEFAULT, 0, false)
  1086  	require.NoError(t, err)
  1087  	defer th.deleteUserPosts(th.User.Id)
  1088  
  1089  	params := &model.SearchParams{
  1090  		Terms: "qwerty *",
  1091  	}
  1092  	results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1093  	require.NoError(t, err)
  1094  
  1095  	require.Len(t, results.Posts, 1)
  1096  	th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1097  }
  1098  
  1099  func testSupportTermsWithDash(t *testing.T, th *SearchTestHelper) {
  1100  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "search term-with-dash", "", model.POST_DEFAULT, 0, false)
  1101  	require.NoError(t, err)
  1102  	_, err = th.createPost(th.User.Id, th.ChannelBasic.Id, "searching term with dash", "", model.POST_DEFAULT, 0, false)
  1103  	require.NoError(t, err)
  1104  	defer th.deleteUserPosts(th.User.Id)
  1105  
  1106  	t.Run("Should search terms with dash", func(t *testing.T) {
  1107  		params := &model.SearchParams{
  1108  			Terms: "term-with-dash",
  1109  		}
  1110  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1111  		require.NoError(t, err)
  1112  
  1113  		require.Len(t, results.Posts, 1)
  1114  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1115  	})
  1116  
  1117  	t.Run("Should search terms with dash using quotes", func(t *testing.T) {
  1118  		params := &model.SearchParams{
  1119  			Terms: "\"term-with-dash\"",
  1120  		}
  1121  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1122  		require.NoError(t, err)
  1123  
  1124  		require.Len(t, results.Posts, 1)
  1125  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1126  	})
  1127  }
  1128  
  1129  func testSupportTermsWithUnderscore(t *testing.T, th *SearchTestHelper) {
  1130  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "search term_with_underscore", "", model.POST_DEFAULT, 0, false)
  1131  	require.NoError(t, err)
  1132  	_, err = th.createPost(th.User.Id, th.ChannelBasic.Id, "searching term with underscore", "", model.POST_DEFAULT, 0, false)
  1133  	require.NoError(t, err)
  1134  	defer th.deleteUserPosts(th.User.Id)
  1135  
  1136  	t.Run("Should search terms with underscore", func(t *testing.T) {
  1137  		params := &model.SearchParams{
  1138  			Terms: "term_with_underscore",
  1139  		}
  1140  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1141  		require.NoError(t, err)
  1142  
  1143  		require.Len(t, results.Posts, 1)
  1144  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1145  	})
  1146  
  1147  	t.Run("Should search terms with underscore using quotes", func(t *testing.T) {
  1148  		params := &model.SearchParams{
  1149  			Terms: "\"term_with_underscore\"",
  1150  		}
  1151  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1152  		require.NoError(t, err)
  1153  
  1154  		require.Len(t, results.Posts, 1)
  1155  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1156  	})
  1157  }
  1158  
  1159  func testSearchOrExcludePostsWithHashtags(t *testing.T, th *SearchTestHelper) {
  1160  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "search post with #hashtag", "#hashtag", model.POST_DEFAULT, 0, false)
  1161  	require.NoError(t, err)
  1162  	p2, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "searching term with hashtag", "", model.POST_DEFAULT, 0, false)
  1163  	require.NoError(t, err)
  1164  	p3, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "searching term with", "#hashtag", model.POST_DEFAULT, 0, false)
  1165  	require.NoError(t, err)
  1166  	defer th.deleteUserPosts(th.User.Id)
  1167  
  1168  	t.Run("Should search terms with hashtags", func(t *testing.T) {
  1169  		params := &model.SearchParams{
  1170  			Terms:     "#hashtag",
  1171  			IsHashtag: true,
  1172  		}
  1173  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1174  		require.NoError(t, err)
  1175  
  1176  		require.Len(t, results.Posts, 2)
  1177  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1178  		th.checkPostInSearchResults(t, p3.Id, results.Posts)
  1179  	})
  1180  
  1181  	t.Run("Should search hashtag terms without hashtag option", func(t *testing.T) {
  1182  		params := &model.SearchParams{
  1183  			Terms:     "#hashtag",
  1184  			IsHashtag: false,
  1185  		}
  1186  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1187  		require.NoError(t, err)
  1188  
  1189  		require.Len(t, results.Posts, 2)
  1190  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1191  		th.checkPostInSearchResults(t, p2.Id, results.Posts)
  1192  	})
  1193  }
  1194  
  1195  func testSearchHashtagWithMarkdown(t *testing.T, th *SearchTestHelper) {
  1196  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "searching hashtag #hashtag", "#hashtag", model.POST_DEFAULT, 0, false)
  1197  	require.NoError(t, err)
  1198  	p2, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "searching term with `#hashtag`", "#hashtag", model.POST_DEFAULT, 0, false)
  1199  	require.NoError(t, err)
  1200  	p3, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "searching term with **#hashtag**", "#hashtag", model.POST_DEFAULT, 0, false)
  1201  	require.NoError(t, err)
  1202  	p4, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "searching term with ~~#hashtag~~", "#hashtag", model.POST_DEFAULT, 0, false)
  1203  	require.NoError(t, err)
  1204  	p5, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "searching term with _#hashtag_", "#hashtag", model.POST_DEFAULT, 0, false)
  1205  	require.NoError(t, err)
  1206  	defer th.deleteUserPosts(th.User.Id)
  1207  
  1208  	params := &model.SearchParams{
  1209  		Terms:     "#hashtag",
  1210  		IsHashtag: true,
  1211  	}
  1212  	results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1213  	require.NoError(t, err)
  1214  
  1215  	require.Len(t, results.Posts, 5)
  1216  	th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1217  	th.checkPostInSearchResults(t, p2.Id, results.Posts)
  1218  	th.checkPostInSearchResults(t, p3.Id, results.Posts)
  1219  	th.checkPostInSearchResults(t, p4.Id, results.Posts)
  1220  	th.checkPostInSearchResults(t, p5.Id, results.Posts)
  1221  }
  1222  
  1223  func testSearcWithMultipleHashtags(t *testing.T, th *SearchTestHelper) {
  1224  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "searching hashtag #hashtag", "#hashtwo #hashone", model.POST_DEFAULT, 0, false)
  1225  	require.NoError(t, err)
  1226  	p2, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "searching term with `#hashtag`", "#hashtwo #hashthree", model.POST_DEFAULT, 0, false)
  1227  	require.NoError(t, err)
  1228  	defer th.deleteUserPosts(th.User.Id)
  1229  
  1230  	t.Run("Should search posts with multiple hashtags", func(t *testing.T) {
  1231  		params := &model.SearchParams{
  1232  			Terms:     "#hashone #hashtwo",
  1233  			IsHashtag: true,
  1234  		}
  1235  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1236  		require.NoError(t, err)
  1237  
  1238  		require.Len(t, results.Posts, 1)
  1239  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1240  	})
  1241  
  1242  	t.Run("Should search posts with multiple hashtags using OR", func(t *testing.T) {
  1243  		params := &model.SearchParams{
  1244  			Terms:     "#hashone #hashtwo",
  1245  			IsHashtag: true,
  1246  			OrTerms:   true,
  1247  		}
  1248  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1249  		require.NoError(t, err)
  1250  
  1251  		require.Len(t, results.Posts, 2)
  1252  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1253  		th.checkPostInSearchResults(t, p2.Id, results.Posts)
  1254  	})
  1255  }
  1256  
  1257  func testSearchPostsWithDotsInHashtags(t *testing.T, th *SearchTestHelper) {
  1258  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "searching hashtag #hashtag.dot", "#hashtag.dot", model.POST_DEFAULT, 0, false)
  1259  	require.NoError(t, err)
  1260  	defer th.deleteUserPosts(th.User.Id)
  1261  
  1262  	params := &model.SearchParams{
  1263  		Terms:     "#hashtag.dot",
  1264  		IsHashtag: true,
  1265  	}
  1266  	results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1267  	require.NoError(t, err)
  1268  
  1269  	require.Len(t, results.Posts, 1)
  1270  	th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1271  }
  1272  
  1273  func testSearchHashtagCaseInsensitive(t *testing.T, th *SearchTestHelper) {
  1274  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "searching hashtag #HaShTaG", "#HaShTaG", model.POST_DEFAULT, 0, false)
  1275  	require.NoError(t, err)
  1276  	p2, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "searching hashtag #hashtag", "#hashtag", model.POST_DEFAULT, 0, false)
  1277  	require.NoError(t, err)
  1278  	p3, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "searching hashtag #HASHTAG", "#HASHTAG", model.POST_DEFAULT, 0, false)
  1279  	require.NoError(t, err)
  1280  	defer th.deleteUserPosts(th.User.Id)
  1281  
  1282  	t.Run("Lower case hashtag", func(t *testing.T) {
  1283  		params := &model.SearchParams{
  1284  			Terms:     "#hashtag",
  1285  			IsHashtag: true,
  1286  		}
  1287  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1288  		require.NoError(t, err)
  1289  
  1290  		require.Len(t, results.Posts, 3)
  1291  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1292  		th.checkPostInSearchResults(t, p2.Id, results.Posts)
  1293  		th.checkPostInSearchResults(t, p3.Id, results.Posts)
  1294  	})
  1295  
  1296  	t.Run("Upper case hashtag", func(t *testing.T) {
  1297  		params := &model.SearchParams{
  1298  			Terms:     "#HASHTAG",
  1299  			IsHashtag: true,
  1300  		}
  1301  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1302  		require.NoError(t, err)
  1303  
  1304  		require.Len(t, results.Posts, 3)
  1305  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1306  		th.checkPostInSearchResults(t, p2.Id, results.Posts)
  1307  		th.checkPostInSearchResults(t, p3.Id, results.Posts)
  1308  	})
  1309  
  1310  	t.Run("Mixed case hashtag", func(t *testing.T) {
  1311  		params := &model.SearchParams{
  1312  			Terms:     "#HaShTaG",
  1313  			IsHashtag: true,
  1314  		}
  1315  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1316  		require.NoError(t, err)
  1317  
  1318  		require.Len(t, results.Posts, 3)
  1319  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1320  		th.checkPostInSearchResults(t, p2.Id, results.Posts)
  1321  		th.checkPostInSearchResults(t, p3.Id, results.Posts)
  1322  	})
  1323  }
  1324  
  1325  func testSearchHashtagWithDash(t *testing.T, th *SearchTestHelper) {
  1326  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "searching hashtag #hashtag-test", "#hashtag-test", model.POST_DEFAULT, 0, false)
  1327  	require.NoError(t, err)
  1328  	_, err = th.createPost(th.User.Id, th.ChannelBasic.Id, "searching hashtag #hashtagtest", "#hashtagtest", model.POST_DEFAULT, 0, false)
  1329  	require.NoError(t, err)
  1330  	defer th.deleteUserPosts(th.User.Id)
  1331  
  1332  	params := &model.SearchParams{
  1333  		Terms:     "#hashtag-test",
  1334  		IsHashtag: true,
  1335  	}
  1336  	results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1337  	require.NoError(t, err)
  1338  
  1339  	require.Len(t, results.Posts, 1)
  1340  	th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1341  }
  1342  
  1343  func testSearchHashtagWithNumbers(t *testing.T, th *SearchTestHelper) {
  1344  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "searching hashtag #h4sht4g", "#h4sht4g", model.POST_DEFAULT, 0, false)
  1345  	require.NoError(t, err)
  1346  	_, err = th.createPost(th.User.Id, th.ChannelBasic.Id, "searching hashtag #hashtag", "#hashtag", model.POST_DEFAULT, 0, false)
  1347  	require.NoError(t, err)
  1348  	defer th.deleteUserPosts(th.User.Id)
  1349  
  1350  	params := &model.SearchParams{
  1351  		Terms:     "#h4sht4g",
  1352  		IsHashtag: true,
  1353  	}
  1354  	results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1355  	require.NoError(t, err)
  1356  
  1357  	require.Len(t, results.Posts, 1)
  1358  	th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1359  }
  1360  
  1361  func testSearchHashtagWithDots(t *testing.T, th *SearchTestHelper) {
  1362  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "searching hashtag #hashtag.test", "#hashtag.test", model.POST_DEFAULT, 0, false)
  1363  	require.NoError(t, err)
  1364  	_, err = th.createPost(th.User.Id, th.ChannelBasic.Id, "searching hashtag #hashtagtest", "#hashtagtest", model.POST_DEFAULT, 0, false)
  1365  	require.NoError(t, err)
  1366  	defer th.deleteUserPosts(th.User.Id)
  1367  
  1368  	params := &model.SearchParams{
  1369  		Terms:     "#hashtag.test",
  1370  		IsHashtag: true,
  1371  	}
  1372  	results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1373  	require.NoError(t, err)
  1374  
  1375  	require.Len(t, results.Posts, 1)
  1376  	th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1377  }
  1378  
  1379  func testSearchHashtagWithUnderscores(t *testing.T, th *SearchTestHelper) {
  1380  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "searching hashtag #hashtag_test", "#hashtag_test", model.POST_DEFAULT, 0, false)
  1381  	require.NoError(t, err)
  1382  	_, err = th.createPost(th.User.Id, th.ChannelBasic.Id, "searching hashtag #hashtagtest", "#hashtagtest", model.POST_DEFAULT, 0, false)
  1383  	require.NoError(t, err)
  1384  	defer th.deleteUserPosts(th.User.Id)
  1385  
  1386  	params := &model.SearchParams{
  1387  		Terms:     "#hashtag_test",
  1388  		IsHashtag: true,
  1389  	}
  1390  	results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1391  	require.NoError(t, err)
  1392  
  1393  	require.Len(t, results.Posts, 1)
  1394  	th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1395  }
  1396  
  1397  func testSearchShouldExcludeSytemMessages(t *testing.T, th *SearchTestHelper) {
  1398  	_, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "test system message one", "", model.POST_JOIN_CHANNEL, 0, false)
  1399  	require.NoError(t, err)
  1400  	_, err = th.createPost(th.User.Id, th.ChannelBasic.Id, "test system message two", "", model.POST_LEAVE_CHANNEL, 0, false)
  1401  	require.NoError(t, err)
  1402  	_, err = th.createPost(th.User.Id, th.ChannelBasic.Id, "test system message three", "", model.POST_LEAVE_TEAM, 0, false)
  1403  	require.NoError(t, err)
  1404  	_, err = th.createPost(th.User.Id, th.ChannelBasic.Id, "test system message four", "", model.POST_ADD_TO_CHANNEL, 0, false)
  1405  	require.NoError(t, err)
  1406  	_, err = th.createPost(th.User.Id, th.ChannelBasic.Id, "test system message five", "", model.POST_ADD_TO_TEAM, 0, false)
  1407  	require.NoError(t, err)
  1408  	_, err = th.createPost(th.User.Id, th.ChannelBasic.Id, "test system message six", "", model.POST_HEADER_CHANGE, 0, false)
  1409  	require.NoError(t, err)
  1410  	defer th.deleteUserPosts(th.User.Id)
  1411  
  1412  	params := &model.SearchParams{Terms: "test system"}
  1413  	results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1414  	require.NoError(t, err)
  1415  
  1416  	require.Len(t, results.Posts, 0)
  1417  }
  1418  
  1419  func testSearchShouldBeAbleToMatchByMentions(t *testing.T, th *SearchTestHelper) {
  1420  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "test system @testuser", "", model.POST_DEFAULT, 0, false)
  1421  	require.NoError(t, err)
  1422  	p2, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "test system testuser", "", model.POST_DEFAULT, 0, false)
  1423  	require.NoError(t, err)
  1424  	p3, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "test system #testuser", "#testuser", model.POST_DEFAULT, 0, false)
  1425  	require.NoError(t, err)
  1426  	defer th.deleteUserPosts(th.User.Id)
  1427  
  1428  	params := &model.SearchParams{Terms: "@testuser"}
  1429  	results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1430  	require.NoError(t, err)
  1431  
  1432  	require.Len(t, results.Posts, 3)
  1433  	th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1434  	th.checkPostInSearchResults(t, p2.Id, results.Posts)
  1435  	th.checkPostInSearchResults(t, p3.Id, results.Posts)
  1436  }
  1437  
  1438  func testSearchInDeletedOrArchivedChannels(t *testing.T, th *SearchTestHelper) {
  1439  	p1, err := th.createPost(th.User.Id, th.ChannelDeleted.Id, "message in deleted channel", "", model.POST_DEFAULT, 0, false)
  1440  	require.NoError(t, err)
  1441  	p2, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "message in regular channel", "", model.POST_DEFAULT, 0, false)
  1442  	require.NoError(t, err)
  1443  	p3, err := th.createPost(th.User.Id, th.ChannelPrivate.Id, "message in private channel", "", model.POST_DEFAULT, 0, false)
  1444  	require.NoError(t, err)
  1445  	defer th.deleteUserPosts(th.User.Id)
  1446  
  1447  	t.Run("Doesn't include posts in deleted channels", func(t *testing.T) {
  1448  		params := &model.SearchParams{Terms: "message", IncludeDeletedChannels: false}
  1449  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1450  		require.NoError(t, err)
  1451  
  1452  		require.Len(t, results.Posts, 2)
  1453  		th.checkPostInSearchResults(t, p2.Id, results.Posts)
  1454  		th.checkPostInSearchResults(t, p3.Id, results.Posts)
  1455  	})
  1456  
  1457  	t.Run("Include posts in deleted channels", func(t *testing.T) {
  1458  		params := &model.SearchParams{Terms: "message", IncludeDeletedChannels: true}
  1459  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1460  		require.NoError(t, err)
  1461  
  1462  		require.Len(t, results.Posts, 3)
  1463  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1464  		th.checkPostInSearchResults(t, p2.Id, results.Posts)
  1465  		th.checkPostInSearchResults(t, p3.Id, results.Posts)
  1466  	})
  1467  
  1468  	t.Run("Include posts in deleted channels using multiple terms", func(t *testing.T) {
  1469  		params := &model.SearchParams{Terms: "message channel", IncludeDeletedChannels: true}
  1470  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1471  		require.NoError(t, err)
  1472  
  1473  		require.Len(t, results.Posts, 3)
  1474  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1475  		th.checkPostInSearchResults(t, p2.Id, results.Posts)
  1476  		th.checkPostInSearchResults(t, p3.Id, results.Posts)
  1477  	})
  1478  
  1479  	t.Run("Include posts in deleted channels using multiple OR terms", func(t *testing.T) {
  1480  		params := &model.SearchParams{
  1481  			Terms:                  "message channel",
  1482  			IncludeDeletedChannels: true,
  1483  			OrTerms:                true,
  1484  		}
  1485  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1486  		require.NoError(t, err)
  1487  
  1488  		require.Len(t, results.Posts, 3)
  1489  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1490  		th.checkPostInSearchResults(t, p2.Id, results.Posts)
  1491  		th.checkPostInSearchResults(t, p3.Id, results.Posts)
  1492  	})
  1493  
  1494  	t.Run("All IncludeDeletedChannels params should have same value if multiple SearchParams provided", func(t *testing.T) {
  1495  		params1 := &model.SearchParams{
  1496  			Terms:                  "message channel",
  1497  			IncludeDeletedChannels: true,
  1498  		}
  1499  		params2 := &model.SearchParams{
  1500  			Terms:                  "#hashtag",
  1501  			IncludeDeletedChannels: false,
  1502  		}
  1503  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params1, params2}, th.User.Id, th.Team.Id, 0, 20)
  1504  		require.Nil(t, results)
  1505  		require.Error(t, err)
  1506  	})
  1507  }
  1508  
  1509  func testSearchTermsWithDashes(t *testing.T, th *SearchTestHelper) {
  1510  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "message with-dash-term", "", model.POST_DEFAULT, 0, false)
  1511  	require.NoError(t, err)
  1512  	p2, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "message with dash term", "", model.POST_DEFAULT, 0, false)
  1513  	require.NoError(t, err)
  1514  	defer th.deleteUserPosts(th.User.Id)
  1515  
  1516  	t.Run("Search for terms with dash", func(t *testing.T) {
  1517  		params := &model.SearchParams{Terms: "with-dash-term"}
  1518  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1519  		require.NoError(t, err)
  1520  
  1521  		require.Len(t, results.Posts, 1)
  1522  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1523  	})
  1524  
  1525  	t.Run("Search for terms with quoted dash", func(t *testing.T) {
  1526  		params := &model.SearchParams{Terms: "\"with-dash-term\""}
  1527  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1528  		require.NoError(t, err)
  1529  
  1530  		require.Len(t, results.Posts, 1)
  1531  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1532  	})
  1533  
  1534  	t.Run("Search for multiple terms with one having dash", func(t *testing.T) {
  1535  		params := &model.SearchParams{Terms: "with-dash-term message"}
  1536  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1537  		require.NoError(t, err)
  1538  
  1539  		require.Len(t, results.Posts, 1)
  1540  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1541  	})
  1542  
  1543  	t.Run("Search for multiple OR terms with one having dash", func(t *testing.T) {
  1544  		params := &model.SearchParams{Terms: "with-dash-term message", OrTerms: true}
  1545  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1546  		require.NoError(t, err)
  1547  
  1548  		require.Len(t, results.Posts, 2)
  1549  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1550  		th.checkPostInSearchResults(t, p2.Id, results.Posts)
  1551  	})
  1552  }
  1553  
  1554  func testSearchTermsWithDots(t *testing.T, th *SearchTestHelper) {
  1555  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "message with.dots.term", "", model.POST_DEFAULT, 0, false)
  1556  	require.NoError(t, err)
  1557  	p2, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "message with dots term", "", model.POST_DEFAULT, 0, false)
  1558  	require.NoError(t, err)
  1559  	defer th.deleteUserPosts(th.User.Id)
  1560  
  1561  	t.Run("Search for terms with dots", func(t *testing.T) {
  1562  		params := &model.SearchParams{Terms: "with.dots.term"}
  1563  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1564  		require.NoError(t, err)
  1565  
  1566  		require.Len(t, results.Posts, 1)
  1567  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1568  	})
  1569  
  1570  	t.Run("Search for terms with quoted dots", func(t *testing.T) {
  1571  		params := &model.SearchParams{Terms: "\"with.dots.term\""}
  1572  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1573  		require.NoError(t, err)
  1574  
  1575  		require.Len(t, results.Posts, 1)
  1576  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1577  	})
  1578  
  1579  	t.Run("Search for multiple terms with one having dots", func(t *testing.T) {
  1580  		params := &model.SearchParams{Terms: "with.dots.term message"}
  1581  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1582  		require.NoError(t, err)
  1583  
  1584  		require.Len(t, results.Posts, 1)
  1585  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1586  	})
  1587  
  1588  	t.Run("Search for multiple OR terms with one having dots", func(t *testing.T) {
  1589  		params := &model.SearchParams{Terms: "with.dots.term message", OrTerms: true}
  1590  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1591  		require.NoError(t, err)
  1592  
  1593  		require.Len(t, results.Posts, 2)
  1594  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1595  		th.checkPostInSearchResults(t, p2.Id, results.Posts)
  1596  	})
  1597  }
  1598  
  1599  func testSearchTermsWithUnderscores(t *testing.T, th *SearchTestHelper) {
  1600  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "message with_underscores_term", "", model.POST_DEFAULT, 0, false)
  1601  	require.NoError(t, err)
  1602  	p2, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "message with underscores term", "", model.POST_DEFAULT, 0, false)
  1603  	require.NoError(t, err)
  1604  	defer th.deleteUserPosts(th.User.Id)
  1605  
  1606  	t.Run("Search for terms with underscores", func(t *testing.T) {
  1607  		params := &model.SearchParams{Terms: "with_underscores_term"}
  1608  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1609  		require.NoError(t, err)
  1610  
  1611  		require.Len(t, results.Posts, 1)
  1612  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1613  	})
  1614  
  1615  	t.Run("Search for terms with quoted underscores", func(t *testing.T) {
  1616  		params := &model.SearchParams{Terms: "\"with_underscores_term\""}
  1617  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1618  		require.NoError(t, err)
  1619  
  1620  		require.Len(t, results.Posts, 1)
  1621  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1622  	})
  1623  
  1624  	t.Run("Search for multiple terms with one having underscores", func(t *testing.T) {
  1625  		params := &model.SearchParams{Terms: "with_underscores_term message"}
  1626  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1627  		require.NoError(t, err)
  1628  
  1629  		require.Len(t, results.Posts, 1)
  1630  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1631  	})
  1632  
  1633  	t.Run("Search for multiple OR terms with one having underscores", func(t *testing.T) {
  1634  		params := &model.SearchParams{Terms: "with_underscores_term message", OrTerms: true}
  1635  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1636  		require.NoError(t, err)
  1637  
  1638  		require.Len(t, results.Posts, 2)
  1639  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1640  		th.checkPostInSearchResults(t, p2.Id, results.Posts)
  1641  	})
  1642  }
  1643  
  1644  func testSearchBotAccountsPosts(t *testing.T, th *SearchTestHelper) {
  1645  	bot, err := th.createBot("testbot", "Test Bot", th.User.Id)
  1646  	require.NoError(t, err)
  1647  	defer th.deleteBot(bot.UserId)
  1648  	err = th.addUserToTeams(model.UserFromBot(bot), []string{th.Team.Id})
  1649  	require.NoError(t, err)
  1650  	p1, err := th.createPost(bot.UserId, th.ChannelBasic.Id, "bot test message", "", model.POST_DEFAULT, 0, false)
  1651  	require.NoError(t, err)
  1652  	p2, err := th.createPost(bot.UserId, th.ChannelPrivate.Id, "bot test message in private", "", model.POST_DEFAULT, 0, false)
  1653  	require.NoError(t, err)
  1654  	defer th.deleteUserPosts(bot.UserId)
  1655  
  1656  	params := &model.SearchParams{Terms: "bot"}
  1657  	results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1658  	require.NoError(t, err)
  1659  
  1660  	require.Len(t, results.Posts, 2)
  1661  	th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1662  	th.checkPostInSearchResults(t, p2.Id, results.Posts)
  1663  }
  1664  
  1665  func testSupportStemmingAndWildcards(t *testing.T, th *SearchTestHelper) {
  1666  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "approve", "", model.POST_DEFAULT, 0, false)
  1667  	require.NoError(t, err)
  1668  	p2, err := th.createPost(th.User.Id, th.ChannelPrivate.Id, "approved", "", model.POST_DEFAULT, 0, false)
  1669  	require.NoError(t, err)
  1670  	p3, err := th.createPost(th.User.Id, th.ChannelPrivate.Id, "approvedz", "", model.POST_DEFAULT, 0, false)
  1671  	require.NoError(t, err)
  1672  	defer th.deleteUserPosts(th.User.Id)
  1673  
  1674  	t.Run("Should stem appr", func(t *testing.T) {
  1675  		params := &model.SearchParams{Terms: "appr*"}
  1676  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1677  		require.NoError(t, err)
  1678  
  1679  		require.Len(t, results.Posts, 3)
  1680  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1681  		th.checkPostInSearchResults(t, p2.Id, results.Posts)
  1682  		th.checkPostInSearchResults(t, p3.Id, results.Posts)
  1683  	})
  1684  
  1685  	t.Run("Should stem approve", func(t *testing.T) {
  1686  		params := &model.SearchParams{Terms: "approve*"}
  1687  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1688  		require.NoError(t, err)
  1689  
  1690  		require.Len(t, results.Posts, 1)
  1691  		th.checkPostInSearchResults(t, p3.Id, results.Posts)
  1692  	})
  1693  }
  1694  
  1695  func testSupportWildcardOutsideQuotes(t *testing.T, th *SearchTestHelper) {
  1696  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "hello world", "", model.POST_DEFAULT, 0, false)
  1697  	require.NoError(t, err)
  1698  	p2, err := th.createPost(th.User.Id, th.ChannelPrivate.Id, "hell or heaven", "", model.POST_DEFAULT, 0, false)
  1699  	require.NoError(t, err)
  1700  	defer th.deleteUserPosts(th.User.Id)
  1701  
  1702  	t.Run("Should return results without quotes", func(t *testing.T) {
  1703  		params := &model.SearchParams{Terms: "hell*"}
  1704  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1705  		require.NoError(t, err)
  1706  
  1707  		require.Len(t, results.Posts, 2)
  1708  		th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1709  		th.checkPostInSearchResults(t, p2.Id, results.Posts)
  1710  	})
  1711  
  1712  	t.Run("Should return just one result with quotes", func(t *testing.T) {
  1713  		params := &model.SearchParams{Terms: "\"hell\"*"}
  1714  		results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1715  		require.NoError(t, err)
  1716  
  1717  		require.Len(t, results.Posts, 1)
  1718  		th.checkPostInSearchResults(t, p2.Id, results.Posts)
  1719  	})
  1720  
  1721  }
  1722  
  1723  func testHashtagSearchShouldSupportThreeOrMoreCharacters(t *testing.T, th *SearchTestHelper) {
  1724  	_, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "one char hashtag #1", "#1", model.POST_DEFAULT, 0, false)
  1725  	require.NoError(t, err)
  1726  	_, err = th.createPost(th.User.Id, th.ChannelPrivate.Id, "two chars hashtag #12", "#12", model.POST_DEFAULT, 0, false)
  1727  	require.NoError(t, err)
  1728  	p3, err := th.createPost(th.User.Id, th.ChannelPrivate.Id, "three chars hashtag #123", "#123", model.POST_DEFAULT, 0, false)
  1729  	require.NoError(t, err)
  1730  	defer th.deleteUserPosts(th.User.Id)
  1731  
  1732  	params := &model.SearchParams{Terms: "#123", IsHashtag: true}
  1733  	results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1734  	require.NoError(t, err)
  1735  
  1736  	require.Len(t, results.Posts, 1)
  1737  	th.checkPostInSearchResults(t, p3.Id, results.Posts)
  1738  }
  1739  
  1740  func testSlashShouldNotBeCharSeparator(t *testing.T, th *SearchTestHelper) {
  1741  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "alpha/beta gamma, theta", "", model.POST_DEFAULT, 0, false)
  1742  	require.NoError(t, err)
  1743  	defer th.deleteUserPosts(th.User.Id)
  1744  
  1745  	params := &model.SearchParams{Terms: "gamma"}
  1746  	results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1747  	require.NoError(t, err)
  1748  
  1749  	require.Len(t, results.Posts, 1)
  1750  	th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1751  
  1752  	params = &model.SearchParams{Terms: "beta"}
  1753  	results, err = th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1754  	require.NoError(t, err)
  1755  
  1756  	require.Len(t, results.Posts, 1)
  1757  	th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1758  
  1759  	params = &model.SearchParams{Terms: "alpha"}
  1760  	results, err = th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1761  	require.NoError(t, err)
  1762  
  1763  	require.Len(t, results.Posts, 1)
  1764  	th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1765  }
  1766  
  1767  func testSearchEmailsWithoutQuotes(t *testing.T, th *SearchTestHelper) {
  1768  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "message test@test.com", "", model.POST_DEFAULT, 0, false)
  1769  	require.NoError(t, err)
  1770  	_, err = th.createPost(th.User.Id, th.ChannelBasic.Id, "message test2@test.com", "", model.POST_DEFAULT, 0, false)
  1771  	require.NoError(t, err)
  1772  	defer th.deleteUserPosts(th.User.Id)
  1773  
  1774  	params := &model.SearchParams{Terms: "test@test.com"}
  1775  	results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1776  	require.NoError(t, err)
  1777  
  1778  	require.Len(t, results.Posts, 1)
  1779  	th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1780  }
  1781  
  1782  func testSupportSearchInComments(t *testing.T, th *SearchTestHelper) {
  1783  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "message test@test.com", "", model.POST_DEFAULT, 0, false)
  1784  	require.NoError(t, err)
  1785  	r1, err := th.createReply(th.User.Id, "reply check", "", p1, 0, false)
  1786  	require.NoError(t, err)
  1787  	defer th.deleteUserPosts(th.User.Id)
  1788  
  1789  	params := &model.SearchParams{Terms: "reply"}
  1790  	results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1791  	require.NoError(t, err)
  1792  
  1793  	require.Len(t, results.Posts, 1)
  1794  	th.checkPostInSearchResults(t, r1.Id, results.Posts)
  1795  }
  1796  
  1797  func testSupportSearchTermsWithinLinks(t *testing.T, th *SearchTestHelper) {
  1798  	p1, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "message with link http://www.wikipedia.org/dolphins", "", model.POST_DEFAULT, 0, false)
  1799  	require.NoError(t, err)
  1800  	defer th.deleteUserPosts(th.User.Id)
  1801  
  1802  	params := &model.SearchParams{Terms: "wikipedia"}
  1803  	results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1804  	require.NoError(t, err)
  1805  
  1806  	require.Len(t, results.Posts, 1)
  1807  	th.checkPostInSearchResults(t, p1.Id, results.Posts)
  1808  }
  1809  
  1810  func testShouldNotReturnLinksEmbeddedInMarkdown(t *testing.T, th *SearchTestHelper) {
  1811  	_, err := th.createPost(th.User.Id, th.ChannelBasic.Id, "message with link [here](http://www.wikipedia.org/dolphins)", "", model.POST_DEFAULT, 0, false)
  1812  	require.NoError(t, err)
  1813  	defer th.deleteUserPosts(th.User.Id)
  1814  
  1815  	params := &model.SearchParams{Terms: "wikipedia"}
  1816  	results, err := th.Store.Post().SearchPostsInTeamForUser([]*model.SearchParams{params}, th.User.Id, th.Team.Id, 0, 20)
  1817  	require.NoError(t, err)
  1818  
  1819  	require.Len(t, results.Posts, 0)
  1820  }