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