MM-11359: support excluding results from search (#11196)
Этот коммит содержится в:
52
app/post.go
52
app/post.go
@@ -938,6 +938,29 @@ func (a *App) searchPostsInTeam(teamId string, userId string, paramsList []*mode
|
|||||||
return posts, nil
|
return posts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) convertChannelNamesToChannelIds(channels []string, userId string, teamId string, includeDeletedChannels bool) []string {
|
||||||
|
for idx, channelName := range channels {
|
||||||
|
channel, err := a.parseAndFetchChannelIdByNameFromInFilter(channelName, userId, teamId, includeDeletedChannels)
|
||||||
|
if err != nil {
|
||||||
|
mlog.Error(fmt.Sprint(err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
channels[idx] = channel.Id
|
||||||
|
}
|
||||||
|
return channels
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) convertUserNameToUserIds(usernames []string) []string {
|
||||||
|
for idx, username := range usernames {
|
||||||
|
if user, err := a.GetUserByUsername(username); err != nil {
|
||||||
|
mlog.Error(fmt.Sprint(err))
|
||||||
|
} else {
|
||||||
|
usernames[idx] = user.Id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return usernames
|
||||||
|
}
|
||||||
|
|
||||||
func (a *App) SearchPostsInTeam(teamId string, paramsList []*model.SearchParams) (*model.PostList, *model.AppError) {
|
func (a *App) SearchPostsInTeam(teamId string, paramsList []*model.SearchParams) (*model.PostList, *model.AppError) {
|
||||||
if !*a.Config().ServiceSettings.EnablePostSearch {
|
if !*a.Config().ServiceSettings.EnablePostSearch {
|
||||||
return nil, model.NewAppError("SearchPostsInTeam", "store.sql_post.search.disabled", nil, fmt.Sprintf("teamId=%v", teamId), http.StatusNotImplemented)
|
return nil, model.NewAppError("SearchPostsInTeam", "store.sql_post.search.disabled", nil, fmt.Sprintf("teamId=%v", teamId), http.StatusNotImplemented)
|
||||||
@@ -956,23 +979,12 @@ func (a *App) esSearchPostsInTeamForUser(paramsList []*model.SearchParams, userI
|
|||||||
// Don't allow users to search for "*"
|
// Don't allow users to search for "*"
|
||||||
if params.Terms != "*" {
|
if params.Terms != "*" {
|
||||||
// Convert channel names to channel IDs
|
// Convert channel names to channel IDs
|
||||||
for idx, channelName := range params.InChannels {
|
params.InChannels = a.convertChannelNamesToChannelIds(params.InChannels, userId, teamId, includeDeletedChannels)
|
||||||
channel, err := a.parseAndFetchChannelIdByNameFromInFilter(channelName, userId, teamId, includeDeletedChannels)
|
params.ExcludedChannels = a.convertChannelNamesToChannelIds(params.ExcludedChannels, userId, teamId, includeDeletedChannels)
|
||||||
if err != nil {
|
|
||||||
mlog.Error(fmt.Sprint(err))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
params.InChannels[idx] = channel.Id
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert usernames to user IDs
|
// Convert usernames to user IDs
|
||||||
for idx, username := range params.FromUsers {
|
params.FromUsers = a.convertUserNameToUserIds(params.FromUsers)
|
||||||
if user, err := a.GetUserByUsername(username); err != nil {
|
params.ExcludedUsers = a.convertUserNameToUserIds(params.ExcludedUsers)
|
||||||
mlog.Error(fmt.Sprint(err))
|
|
||||||
} else {
|
|
||||||
params.FromUsers[idx] = user.Id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
finalParamsList = append(finalParamsList, params)
|
finalParamsList = append(finalParamsList, params)
|
||||||
}
|
}
|
||||||
@@ -1049,6 +1061,16 @@ func (a *App) SearchPostsInTeamForUser(terms string, userId string, teamId strin
|
|||||||
params.InChannels[idx] = channel.Name
|
params.InChannels[idx] = channel.Name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for idx, channelName := range params.ExcludedChannels {
|
||||||
|
if strings.HasPrefix(channelName, "@") {
|
||||||
|
channel, err := a.parseAndFetchChannelIdByNameFromInFilter(channelName, userId, teamId, includeDeletedChannels)
|
||||||
|
if err != nil {
|
||||||
|
mlog.Error(fmt.Sprint(err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
params.ExcludedChannels[idx] = channel.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -14,12 +14,18 @@ var searchTermPuncEnd = regexp.MustCompile(`[^\pL\d\s*"]+$`)
|
|||||||
|
|
||||||
type SearchParams struct {
|
type SearchParams struct {
|
||||||
Terms string
|
Terms string
|
||||||
|
ExcludedTerms string
|
||||||
IsHashtag bool
|
IsHashtag bool
|
||||||
InChannels []string
|
InChannels []string
|
||||||
|
ExcludedChannels []string
|
||||||
FromUsers []string
|
FromUsers []string
|
||||||
|
ExcludedUsers []string
|
||||||
AfterDate string
|
AfterDate string
|
||||||
|
ExcludedAfterDate string
|
||||||
BeforeDate string
|
BeforeDate string
|
||||||
|
ExcludedBeforeDate string
|
||||||
OnDate string
|
OnDate string
|
||||||
|
ExcludedDate string
|
||||||
OrTerms bool
|
OrTerms bool
|
||||||
IncludeDeletedChannels bool
|
IncludeDeletedChannels bool
|
||||||
TimeZoneOffset int
|
TimeZoneOffset int
|
||||||
@@ -40,6 +46,19 @@ func (p *SearchParams) GetAfterDateMillis() int64 {
|
|||||||
return GetStartOfDayMillis(afterDate, p.TimeZoneOffset)
|
return GetStartOfDayMillis(afterDate, p.TimeZoneOffset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the epoch timestamp of the start of the day specified by SearchParams.ExcludedAfterDate
|
||||||
|
func (p *SearchParams) GetExcludedAfterDateMillis() int64 {
|
||||||
|
date, err := time.Parse("2006-01-02", PadDateStringZeros(p.ExcludedAfterDate))
|
||||||
|
if err != nil {
|
||||||
|
date = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
// travel forward 1 day
|
||||||
|
oneDay := time.Hour * 24
|
||||||
|
afterDate := date.Add(oneDay)
|
||||||
|
return GetStartOfDayMillis(afterDate, p.TimeZoneOffset)
|
||||||
|
}
|
||||||
|
|
||||||
// Returns the epoch timestamp of the end of the day specified by SearchParams.BeforeDate
|
// Returns the epoch timestamp of the end of the day specified by SearchParams.BeforeDate
|
||||||
func (p *SearchParams) GetBeforeDateMillis() int64 {
|
func (p *SearchParams) GetBeforeDateMillis() int64 {
|
||||||
date, err := time.Parse("2006-01-02", PadDateStringZeros(p.BeforeDate))
|
date, err := time.Parse("2006-01-02", PadDateStringZeros(p.BeforeDate))
|
||||||
@@ -53,6 +72,19 @@ func (p *SearchParams) GetBeforeDateMillis() int64 {
|
|||||||
return GetEndOfDayMillis(beforeDate, p.TimeZoneOffset)
|
return GetEndOfDayMillis(beforeDate, p.TimeZoneOffset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the epoch timestamp of the end of the day specified by SearchParams.ExcludedBeforeDate
|
||||||
|
func (p *SearchParams) GetExcludedBeforeDateMillis() int64 {
|
||||||
|
date, err := time.Parse("2006-01-02", PadDateStringZeros(p.ExcludedBeforeDate))
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// travel back 1 day
|
||||||
|
oneDay := time.Hour * -24
|
||||||
|
beforeDate := date.Add(oneDay)
|
||||||
|
return GetEndOfDayMillis(beforeDate, p.TimeZoneOffset)
|
||||||
|
}
|
||||||
|
|
||||||
// Returns the epoch timestamps of the start and end of the day specified by SearchParams.OnDate
|
// Returns the epoch timestamps of the start and end of the day specified by SearchParams.OnDate
|
||||||
func (p *SearchParams) GetOnDateMillis() (int64, int64) {
|
func (p *SearchParams) GetOnDateMillis() (int64, int64) {
|
||||||
date, err := time.Parse("2006-01-02", PadDateStringZeros(p.OnDate))
|
date, err := time.Parse("2006-01-02", PadDateStringZeros(p.OnDate))
|
||||||
@@ -63,8 +95,29 @@ func (p *SearchParams) GetOnDateMillis() (int64, int64) {
|
|||||||
return GetStartOfDayMillis(date, p.TimeZoneOffset), GetEndOfDayMillis(date, p.TimeZoneOffset)
|
return GetStartOfDayMillis(date, p.TimeZoneOffset), GetEndOfDayMillis(date, p.TimeZoneOffset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the epoch timestamps of the start and end of the day specified by SearchParams.ExcludedDate
|
||||||
|
func (p *SearchParams) GetExcludedDateMillis() (int64, int64) {
|
||||||
|
date, err := time.Parse("2006-01-02", PadDateStringZeros(p.ExcludedDate))
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetStartOfDayMillis(date, p.TimeZoneOffset), GetEndOfDayMillis(date, p.TimeZoneOffset)
|
||||||
|
}
|
||||||
|
|
||||||
var searchFlags = [...]string{"from", "channel", "in", "before", "after", "on"}
|
var searchFlags = [...]string{"from", "channel", "in", "before", "after", "on"}
|
||||||
|
|
||||||
|
type flag struct {
|
||||||
|
name string
|
||||||
|
value string
|
||||||
|
exclude bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type searchWord struct {
|
||||||
|
value string
|
||||||
|
exclude bool
|
||||||
|
}
|
||||||
|
|
||||||
func splitWords(text string) []string {
|
func splitWords(text string) []string {
|
||||||
words := []string{}
|
words := []string{}
|
||||||
|
|
||||||
@@ -79,9 +132,13 @@ func splitWords(text string) []string {
|
|||||||
foundQuote = false
|
foundQuote = false
|
||||||
location = i + 1
|
location = i + 1
|
||||||
} else {
|
} else {
|
||||||
words = append(words, strings.Fields(text[location:i])...)
|
nextStart := i
|
||||||
|
if i > 0 && text[i-1] == '-' {
|
||||||
|
nextStart = i - 1
|
||||||
|
}
|
||||||
|
words = append(words, strings.Fields(text[location:nextStart])...)
|
||||||
foundQuote = true
|
foundQuote = true
|
||||||
location = i
|
location = nextStart
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,9 +148,9 @@ func splitWords(text string) []string {
|
|||||||
return words
|
return words
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseSearchFlags(input []string) ([]string, [][2]string) {
|
func parseSearchFlags(input []string) ([]searchWord, []flag) {
|
||||||
words := []string{}
|
words := []searchWord{}
|
||||||
flags := [][2]string{}
|
flags := []flag{}
|
||||||
|
|
||||||
skipNextWord := false
|
skipNextWord := false
|
||||||
for i, word := range input {
|
for i, word := range input {
|
||||||
@@ -105,17 +162,34 @@ func parseSearchFlags(input []string) ([]string, [][2]string) {
|
|||||||
isFlag := false
|
isFlag := false
|
||||||
|
|
||||||
if colon := strings.Index(word, ":"); colon != -1 {
|
if colon := strings.Index(word, ":"); colon != -1 {
|
||||||
flag := word[:colon]
|
var flagName string
|
||||||
|
var exclude bool
|
||||||
|
if strings.HasPrefix(word, "-") {
|
||||||
|
flagName = word[1:colon]
|
||||||
|
exclude = true
|
||||||
|
} else {
|
||||||
|
flagName = word[:colon]
|
||||||
|
exclude = false
|
||||||
|
}
|
||||||
|
|
||||||
value := word[colon+1:]
|
value := word[colon+1:]
|
||||||
|
|
||||||
for _, searchFlag := range searchFlags {
|
for _, searchFlag := range searchFlags {
|
||||||
// check for case insensitive equality
|
// check for case insensitive equality
|
||||||
if strings.EqualFold(flag, searchFlag) {
|
if strings.EqualFold(flagName, searchFlag) {
|
||||||
if value != "" {
|
if value != "" {
|
||||||
flags = append(flags, [2]string{searchFlag, value})
|
flags = append(flags, flag{
|
||||||
|
searchFlag,
|
||||||
|
value,
|
||||||
|
exclude,
|
||||||
|
})
|
||||||
isFlag = true
|
isFlag = true
|
||||||
} else if i < len(input)-1 {
|
} else if i < len(input)-1 {
|
||||||
flags = append(flags, [2]string{searchFlag, input[i+1]})
|
flags = append(flags, flag{
|
||||||
|
searchFlag,
|
||||||
|
input[i+1],
|
||||||
|
exclude,
|
||||||
|
})
|
||||||
skipNextWord = true
|
skipNextWord = true
|
||||||
isFlag = true
|
isFlag = true
|
||||||
}
|
}
|
||||||
@@ -128,6 +202,10 @@ func parseSearchFlags(input []string) ([]string, [][2]string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !isFlag {
|
if !isFlag {
|
||||||
|
exclude := false
|
||||||
|
if strings.HasPrefix(word, "-") {
|
||||||
|
exclude = true
|
||||||
|
}
|
||||||
// trim off surrounding punctuation (note that we leave trailing asterisks to allow wildcards)
|
// trim off surrounding punctuation (note that we leave trailing asterisks to allow wildcards)
|
||||||
word = searchTermPuncStart.ReplaceAllString(word, "")
|
word = searchTermPuncStart.ReplaceAllString(word, "")
|
||||||
word = searchTermPuncEnd.ReplaceAllString(word, "")
|
word = searchTermPuncEnd.ReplaceAllString(word, "")
|
||||||
@@ -136,7 +214,10 @@ func parseSearchFlags(input []string) ([]string, [][2]string) {
|
|||||||
word = hashtagStart.ReplaceAllString(word, "#")
|
word = hashtagStart.ReplaceAllString(word, "#")
|
||||||
|
|
||||||
if len(word) != 0 {
|
if len(word) != 0 {
|
||||||
words = append(words, word)
|
words = append(words, searchWord{
|
||||||
|
word,
|
||||||
|
exclude,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,81 +229,139 @@ func ParseSearchParams(text string, timeZoneOffset int) []*SearchParams {
|
|||||||
words, flags := parseSearchFlags(splitWords(text))
|
words, flags := parseSearchFlags(splitWords(text))
|
||||||
|
|
||||||
hashtagTermList := []string{}
|
hashtagTermList := []string{}
|
||||||
|
excludedHashtagTermList := []string{}
|
||||||
plainTermList := []string{}
|
plainTermList := []string{}
|
||||||
|
excludedPlainTermList := []string{}
|
||||||
|
|
||||||
for _, word := range words {
|
for _, word := range words {
|
||||||
if validHashtag.MatchString(word) {
|
if validHashtag.MatchString(word.value) {
|
||||||
hashtagTermList = append(hashtagTermList, word)
|
if word.exclude {
|
||||||
|
excludedHashtagTermList = append(excludedHashtagTermList, word.value)
|
||||||
|
} else {
|
||||||
|
hashtagTermList = append(hashtagTermList, word.value)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
plainTermList = append(plainTermList, word)
|
if word.exclude {
|
||||||
|
excludedPlainTermList = append(excludedPlainTermList, word.value)
|
||||||
|
} else {
|
||||||
|
plainTermList = append(plainTermList, word.value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hashtagTerms := strings.Join(hashtagTermList, " ")
|
hashtagTerms := strings.Join(hashtagTermList, " ")
|
||||||
|
excludedHashtagTerms := strings.Join(excludedHashtagTermList, " ")
|
||||||
plainTerms := strings.Join(plainTermList, " ")
|
plainTerms := strings.Join(plainTermList, " ")
|
||||||
|
excludedPlainTerms := strings.Join(excludedPlainTermList, " ")
|
||||||
|
|
||||||
inChannels := []string{}
|
inChannels := []string{}
|
||||||
|
excludedChannels := []string{}
|
||||||
fromUsers := []string{}
|
fromUsers := []string{}
|
||||||
|
excludedUsers := []string{}
|
||||||
afterDate := ""
|
afterDate := ""
|
||||||
|
excludedAfterDate := ""
|
||||||
beforeDate := ""
|
beforeDate := ""
|
||||||
|
excludedBeforeDate := ""
|
||||||
onDate := ""
|
onDate := ""
|
||||||
|
excludedDate := ""
|
||||||
|
|
||||||
for _, flagPair := range flags {
|
for _, flag := range flags {
|
||||||
flag := flagPair[0]
|
if flag.name == "in" || flag.name == "channel" {
|
||||||
value := flagPair[1]
|
if flag.exclude {
|
||||||
|
excludedChannels = append(excludedChannels, flag.value)
|
||||||
if flag == "in" || flag == "channel" {
|
} else {
|
||||||
inChannels = append(inChannels, value)
|
inChannels = append(inChannels, flag.value)
|
||||||
} else if flag == "from" {
|
}
|
||||||
fromUsers = append(fromUsers, value)
|
} else if flag.name == "from" {
|
||||||
} else if flag == "after" {
|
if flag.exclude {
|
||||||
afterDate = value
|
excludedUsers = append(excludedUsers, flag.value)
|
||||||
} else if flag == "before" {
|
} else {
|
||||||
beforeDate = value
|
fromUsers = append(fromUsers, flag.value)
|
||||||
} else if flag == "on" {
|
}
|
||||||
onDate = value
|
} else if flag.name == "after" {
|
||||||
|
if flag.exclude {
|
||||||
|
excludedAfterDate = flag.value
|
||||||
|
} else {
|
||||||
|
afterDate = flag.value
|
||||||
|
}
|
||||||
|
} else if flag.name == "before" {
|
||||||
|
if flag.exclude {
|
||||||
|
excludedBeforeDate = flag.value
|
||||||
|
} else {
|
||||||
|
beforeDate = flag.value
|
||||||
|
}
|
||||||
|
} else if flag.name == "on" {
|
||||||
|
if flag.exclude {
|
||||||
|
excludedDate = flag.value
|
||||||
|
} else {
|
||||||
|
onDate = flag.value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
paramsList := []*SearchParams{}
|
paramsList := []*SearchParams{}
|
||||||
|
|
||||||
if len(plainTerms) > 0 {
|
if len(plainTerms) > 0 || len(excludedPlainTerms) > 0 {
|
||||||
paramsList = append(paramsList, &SearchParams{
|
paramsList = append(paramsList, &SearchParams{
|
||||||
Terms: plainTerms,
|
Terms: plainTerms,
|
||||||
IsHashtag: false,
|
ExcludedTerms: excludedPlainTerms,
|
||||||
InChannels: inChannels,
|
IsHashtag: false,
|
||||||
FromUsers: fromUsers,
|
InChannels: inChannels,
|
||||||
AfterDate: afterDate,
|
ExcludedChannels: excludedChannels,
|
||||||
BeforeDate: beforeDate,
|
FromUsers: fromUsers,
|
||||||
OnDate: onDate,
|
ExcludedUsers: excludedUsers,
|
||||||
TimeZoneOffset: timeZoneOffset,
|
AfterDate: afterDate,
|
||||||
|
ExcludedAfterDate: excludedAfterDate,
|
||||||
|
BeforeDate: beforeDate,
|
||||||
|
ExcludedBeforeDate: excludedBeforeDate,
|
||||||
|
OnDate: onDate,
|
||||||
|
ExcludedDate: excludedDate,
|
||||||
|
TimeZoneOffset: timeZoneOffset,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(hashtagTerms) > 0 {
|
if len(hashtagTerms) > 0 || len(excludedHashtagTerms) > 0 {
|
||||||
paramsList = append(paramsList, &SearchParams{
|
paramsList = append(paramsList, &SearchParams{
|
||||||
Terms: hashtagTerms,
|
Terms: hashtagTerms,
|
||||||
IsHashtag: true,
|
ExcludedTerms: excludedHashtagTerms,
|
||||||
InChannels: inChannels,
|
IsHashtag: true,
|
||||||
FromUsers: fromUsers,
|
InChannels: inChannels,
|
||||||
AfterDate: afterDate,
|
ExcludedChannels: excludedChannels,
|
||||||
BeforeDate: beforeDate,
|
FromUsers: fromUsers,
|
||||||
OnDate: onDate,
|
ExcludedUsers: excludedUsers,
|
||||||
TimeZoneOffset: timeZoneOffset,
|
AfterDate: afterDate,
|
||||||
|
ExcludedAfterDate: excludedAfterDate,
|
||||||
|
BeforeDate: beforeDate,
|
||||||
|
ExcludedBeforeDate: excludedBeforeDate,
|
||||||
|
OnDate: onDate,
|
||||||
|
ExcludedDate: excludedDate,
|
||||||
|
TimeZoneOffset: timeZoneOffset,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// special case for when no terms are specified but we still have a filter
|
// special case for when no terms are specified but we still have a filter
|
||||||
if len(plainTerms) == 0 && len(hashtagTerms) == 0 && (len(inChannels) != 0 || len(fromUsers) != 0 || len(afterDate) != 0 || len(beforeDate) != 0 || len(onDate) != 0) {
|
if len(plainTerms) == 0 && len(hashtagTerms) == 0 &&
|
||||||
|
len(excludedPlainTerms) == 0 && len(excludedHashtagTerms) == 0 &&
|
||||||
|
(len(inChannels) != 0 || len(fromUsers) != 0 ||
|
||||||
|
len(excludedChannels) != 0 || len(excludedUsers) != 0 ||
|
||||||
|
len(afterDate) != 0 || len(excludedAfterDate) != 0 ||
|
||||||
|
len(beforeDate) != 0 || len(excludedBeforeDate) != 0 ||
|
||||||
|
len(onDate) != 0 || len(excludedDate) != 0) {
|
||||||
paramsList = append(paramsList, &SearchParams{
|
paramsList = append(paramsList, &SearchParams{
|
||||||
Terms: "",
|
Terms: "",
|
||||||
IsHashtag: false,
|
ExcludedTerms: "",
|
||||||
InChannels: inChannels,
|
IsHashtag: false,
|
||||||
FromUsers: fromUsers,
|
InChannels: inChannels,
|
||||||
AfterDate: afterDate,
|
ExcludedChannels: excludedChannels,
|
||||||
BeforeDate: beforeDate,
|
FromUsers: fromUsers,
|
||||||
OnDate: onDate,
|
ExcludedUsers: excludedUsers,
|
||||||
TimeZoneOffset: timeZoneOffset,
|
AfterDate: afterDate,
|
||||||
|
ExcludedAfterDate: excludedAfterDate,
|
||||||
|
BeforeDate: beforeDate,
|
||||||
|
ExcludedBeforeDate: excludedBeforeDate,
|
||||||
|
OnDate: onDate,
|
||||||
|
ExcludedDate: excludedDate,
|
||||||
|
TimeZoneOffset: timeZoneOffset,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
@@ -799,33 +799,136 @@ var specialSearchChar = []string{
|
|||||||
":",
|
":",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SqlPostStore) buildCreateDateFilterClause(params *model.SearchParams, queryParams map[string]interface{}) (string, map[string]interface{}) {
|
||||||
|
searchQuery := ""
|
||||||
|
// handle after: before: on: filters
|
||||||
|
if len(params.OnDate) > 0 {
|
||||||
|
onDateStart, onDateEnd := params.GetOnDateMillis()
|
||||||
|
queryParams["OnDateStart"] = strconv.FormatInt(onDateStart, 10)
|
||||||
|
queryParams["OnDateEnd"] = strconv.FormatInt(onDateEnd, 10)
|
||||||
|
|
||||||
|
// between `on date` start of day and end of day
|
||||||
|
searchQuery += "AND CreateAt BETWEEN :OnDateStart AND :OnDateEnd "
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if len(params.ExcludedDate) > 0 {
|
||||||
|
excludedDateStart, excludedDateEnd := params.GetExcludedDateMillis()
|
||||||
|
queryParams["ExcludedDateStart"] = strconv.FormatInt(excludedDateStart, 10)
|
||||||
|
queryParams["ExcludedDateEnd"] = strconv.FormatInt(excludedDateEnd, 10)
|
||||||
|
|
||||||
|
searchQuery += "AND CreateAt NOT BETWEEN :ExcludedDateStart AND :ExcludedDateEnd "
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(params.AfterDate) > 0 {
|
||||||
|
afterDate := params.GetAfterDateMillis()
|
||||||
|
queryParams["AfterDate"] = strconv.FormatInt(afterDate, 10)
|
||||||
|
|
||||||
|
// greater than `after date`
|
||||||
|
searchQuery += "AND CreateAt >= :AfterDate "
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(params.BeforeDate) > 0 {
|
||||||
|
beforeDate := params.GetBeforeDateMillis()
|
||||||
|
queryParams["BeforeDate"] = strconv.FormatInt(beforeDate, 10)
|
||||||
|
|
||||||
|
// less than `before date`
|
||||||
|
searchQuery += "AND CreateAt <= :BeforeDate "
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(params.ExcludedAfterDate) > 0 {
|
||||||
|
afterDate := params.GetExcludedAfterDateMillis()
|
||||||
|
queryParams["ExcludedAfterDate"] = strconv.FormatInt(afterDate, 10)
|
||||||
|
|
||||||
|
searchQuery += "AND CreateAt < :ExcludedAfterDate "
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(params.ExcludedBeforeDate) > 0 {
|
||||||
|
beforeDate := params.GetExcludedBeforeDateMillis()
|
||||||
|
queryParams["ExcludedBeforeDate"] = strconv.FormatInt(beforeDate, 10)
|
||||||
|
|
||||||
|
searchQuery += "AND CreateAt > :ExcludedBeforeDate "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return searchQuery, queryParams
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SqlPostStore) buildSearchChannelFilterClause(channels []string, paramPrefix string, exclusion bool, queryParams map[string]interface{}) (string, map[string]interface{}) {
|
||||||
|
if len(channels) == 0 {
|
||||||
|
return "", queryParams
|
||||||
|
}
|
||||||
|
|
||||||
|
clauseSlice := []string{}
|
||||||
|
for i, channel := range channels {
|
||||||
|
paramName := paramPrefix + strconv.FormatInt(int64(i), 10)
|
||||||
|
clauseSlice = append(clauseSlice, ":"+paramName)
|
||||||
|
queryParams[paramName] = channel
|
||||||
|
}
|
||||||
|
clause := strings.Join(clauseSlice, ", ")
|
||||||
|
if exclusion {
|
||||||
|
return "AND Name NOT IN (" + clause + ")", queryParams
|
||||||
|
}
|
||||||
|
return "AND Name IN (" + clause + ")", queryParams
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SqlPostStore) buildSearchUserFilterClause(users []string, paramPrefix string, exclusion bool, queryParams map[string]interface{}) (string, map[string]interface{}) {
|
||||||
|
if len(users) == 0 {
|
||||||
|
return "", queryParams
|
||||||
|
}
|
||||||
|
clauseSlice := []string{}
|
||||||
|
for i, user := range users {
|
||||||
|
paramName := paramPrefix + strconv.FormatInt(int64(i), 10)
|
||||||
|
clauseSlice = append(clauseSlice, ":"+paramName)
|
||||||
|
queryParams[paramName] = user
|
||||||
|
}
|
||||||
|
clause := strings.Join(clauseSlice, ", ")
|
||||||
|
if exclusion {
|
||||||
|
return "AND Username NOT IN (" + clause + ")", queryParams
|
||||||
|
}
|
||||||
|
return "AND Username IN (" + clause + ")", queryParams
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SqlPostStore) buildSearchPostFilterClause(fromUsers []string, excludedUsers []string, queryParams map[string]interface{}) (string, map[string]interface{}) {
|
||||||
|
if len(fromUsers) == 0 && len(excludedUsers) == 0 {
|
||||||
|
return "", queryParams
|
||||||
|
}
|
||||||
|
|
||||||
|
filterQuery := `
|
||||||
|
AND UserId IN (
|
||||||
|
SELECT
|
||||||
|
Id
|
||||||
|
FROM
|
||||||
|
Users,
|
||||||
|
TeamMembers
|
||||||
|
WHERE
|
||||||
|
TeamMembers.TeamId = :TeamId
|
||||||
|
AND Users.Id = TeamMembers.UserId
|
||||||
|
FROM_USER_FILTER
|
||||||
|
EXCLUDED_USER_FILTER)`
|
||||||
|
|
||||||
|
fromUserClause, queryParams := s.buildSearchUserFilterClause(fromUsers, "FromUser", false, queryParams)
|
||||||
|
filterQuery = strings.Replace(filterQuery, "FROM_USER_FILTER", fromUserClause, 1)
|
||||||
|
|
||||||
|
excludedUserClause, queryParams := s.buildSearchUserFilterClause(excludedUsers, "ExcludedUser", true, queryParams)
|
||||||
|
filterQuery = strings.Replace(filterQuery, "EXCLUDED_USER_FILTER", excludedUserClause, 1)
|
||||||
|
|
||||||
|
return filterQuery, queryParams
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SqlPostStore) Search(teamId string, userId string, params *model.SearchParams) (*model.PostList, *model.AppError) {
|
func (s *SqlPostStore) Search(teamId string, userId string, params *model.SearchParams) (*model.PostList, *model.AppError) {
|
||||||
queryParams := map[string]interface{}{
|
queryParams := map[string]interface{}{
|
||||||
"TeamId": teamId,
|
"TeamId": teamId,
|
||||||
"UserId": userId,
|
"UserId": userId,
|
||||||
}
|
}
|
||||||
|
|
||||||
termMap := map[string]bool{}
|
|
||||||
terms := params.Terms
|
|
||||||
list := model.NewPostList()
|
list := model.NewPostList()
|
||||||
|
if params.Terms == "" && params.ExcludedTerms == "" &&
|
||||||
if terms == "" && len(params.InChannels) == 0 && len(params.FromUsers) == 0 && len(params.OnDate) == 0 && len(params.AfterDate) == 0 && len(params.BeforeDate) == 0 {
|
len(params.InChannels) == 0 && len(params.ExcludedChannels) == 0 &&
|
||||||
|
len(params.FromUsers) == 0 && len(params.ExcludedUsers) == 0 &&
|
||||||
|
len(params.OnDate) == 0 && len(params.AfterDate) == 0 && len(params.BeforeDate) == 0 {
|
||||||
return list, nil
|
return list, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
searchType := "Message"
|
|
||||||
if params.IsHashtag {
|
|
||||||
searchType = "Hashtags"
|
|
||||||
for _, term := range strings.Split(terms, " ") {
|
|
||||||
termMap[strings.ToUpper(term)] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// these chars have special meaning and can be treated as spaces
|
|
||||||
for _, c := range specialSearchChar {
|
|
||||||
terms = strings.Replace(terms, c, " ", -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
var posts []*model.Post
|
var posts []*model.Post
|
||||||
|
|
||||||
deletedQueryPart := "AND DeleteAt = 0"
|
deletedQueryPart := "AND DeleteAt = 0"
|
||||||
@@ -858,116 +961,62 @@ func (s *SqlPostStore) Search(teamId string, userId string, params *model.Search
|
|||||||
AND (TeamId = :TeamId OR TeamId = '')
|
AND (TeamId = :TeamId OR TeamId = '')
|
||||||
` + userIdPart + `
|
` + userIdPart + `
|
||||||
` + deletedQueryPart + `
|
` + deletedQueryPart + `
|
||||||
CHANNEL_FILTER)
|
IN_CHANNEL_FILTER
|
||||||
|
EXCLUDED_CHANNEL_FILTER)
|
||||||
CREATEDATE_CLAUSE
|
CREATEDATE_CLAUSE
|
||||||
SEARCH_CLAUSE
|
SEARCH_CLAUSE
|
||||||
ORDER BY CreateAt DESC
|
ORDER BY CreateAt DESC
|
||||||
LIMIT 100`
|
LIMIT 100`
|
||||||
|
|
||||||
if len(params.InChannels) > 1 {
|
inChannelClause, queryParams := s.buildSearchChannelFilterClause(params.InChannels, "InChannel", false, queryParams)
|
||||||
inClause := ":InChannel0"
|
searchQuery = strings.Replace(searchQuery, "IN_CHANNEL_FILTER", inChannelClause, 1)
|
||||||
queryParams["InChannel0"] = params.InChannels[0]
|
|
||||||
|
|
||||||
for i := 1; i < len(params.InChannels); i++ {
|
excludedChannelClause, queryParams := s.buildSearchChannelFilterClause(params.ExcludedChannels, "ExcludedChannel", true, queryParams)
|
||||||
paramName := "InChannel" + strconv.FormatInt(int64(i), 10)
|
searchQuery = strings.Replace(searchQuery, "EXCLUDED_CHANNEL_FILTER", excludedChannelClause, 1)
|
||||||
inClause += ", :" + paramName
|
|
||||||
queryParams[paramName] = params.InChannels[i]
|
postFilterClause, queryParams := s.buildSearchPostFilterClause(params.FromUsers, params.ExcludedUsers, queryParams)
|
||||||
|
searchQuery = strings.Replace(searchQuery, "POST_FILTER", postFilterClause, 1)
|
||||||
|
|
||||||
|
createDateFilterClause, queryParams := s.buildCreateDateFilterClause(params, queryParams)
|
||||||
|
searchQuery = strings.Replace(searchQuery, "CREATEDATE_CLAUSE", createDateFilterClause, 1)
|
||||||
|
|
||||||
|
termMap := map[string]bool{}
|
||||||
|
terms := params.Terms
|
||||||
|
excludedTerms := params.ExcludedTerms
|
||||||
|
|
||||||
|
searchType := "Message"
|
||||||
|
if params.IsHashtag {
|
||||||
|
searchType = "Hashtags"
|
||||||
|
for _, term := range strings.Split(terms, " ") {
|
||||||
|
termMap[strings.ToUpper(term)] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
searchQuery = strings.Replace(searchQuery, "CHANNEL_FILTER", "AND Name IN ("+inClause+")", 1)
|
|
||||||
} else if len(params.InChannels) == 1 {
|
|
||||||
queryParams["InChannel"] = params.InChannels[0]
|
|
||||||
searchQuery = strings.Replace(searchQuery, "CHANNEL_FILTER", "AND Name = :InChannel", 1)
|
|
||||||
} else {
|
|
||||||
searchQuery = strings.Replace(searchQuery, "CHANNEL_FILTER", "", 1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(params.FromUsers) > 1 {
|
// these chars have special meaning and can be treated as spaces
|
||||||
inClause := ":FromUser0"
|
for _, c := range specialSearchChar {
|
||||||
queryParams["FromUser0"] = params.FromUsers[0]
|
terms = strings.Replace(terms, c, " ", -1)
|
||||||
|
excludedTerms = strings.Replace(excludedTerms, c, " ", -1)
|
||||||
for i := 1; i < len(params.FromUsers); i++ {
|
|
||||||
paramName := "FromUser" + strconv.FormatInt(int64(i), 10)
|
|
||||||
inClause += ", :" + paramName
|
|
||||||
queryParams[paramName] = params.FromUsers[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
searchQuery = strings.Replace(searchQuery, "POST_FILTER", `
|
|
||||||
AND UserId IN (
|
|
||||||
SELECT
|
|
||||||
Id
|
|
||||||
FROM
|
|
||||||
Users,
|
|
||||||
TeamMembers
|
|
||||||
WHERE
|
|
||||||
TeamMembers.TeamId = :TeamId
|
|
||||||
AND Users.Id = TeamMembers.UserId
|
|
||||||
AND Username IN (`+inClause+`))`, 1)
|
|
||||||
} else if len(params.FromUsers) == 1 {
|
|
||||||
queryParams["FromUser"] = params.FromUsers[0]
|
|
||||||
searchQuery = strings.Replace(searchQuery, "POST_FILTER", `
|
|
||||||
AND UserId IN (
|
|
||||||
SELECT
|
|
||||||
Id
|
|
||||||
FROM
|
|
||||||
Users,
|
|
||||||
TeamMembers
|
|
||||||
WHERE
|
|
||||||
TeamMembers.TeamId = :TeamId
|
|
||||||
AND Users.Id = TeamMembers.UserId
|
|
||||||
AND Username = :FromUser)`, 1)
|
|
||||||
} else {
|
|
||||||
searchQuery = strings.Replace(searchQuery, "POST_FILTER", "", 1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle after: before: on: filters
|
if terms == "" && excludedTerms == "" {
|
||||||
if len(params.AfterDate) > 1 || len(params.BeforeDate) > 1 || len(params.OnDate) > 1 {
|
|
||||||
if len(params.OnDate) > 1 {
|
|
||||||
onDateStart, onDateEnd := params.GetOnDateMillis()
|
|
||||||
queryParams["OnDateStart"] = strconv.FormatInt(onDateStart, 10)
|
|
||||||
queryParams["OnDateEnd"] = strconv.FormatInt(onDateEnd, 10)
|
|
||||||
|
|
||||||
// between `on date` start of day and end of day
|
|
||||||
searchQuery = strings.Replace(searchQuery, "CREATEDATE_CLAUSE", "AND CreateAt BETWEEN :OnDateStart AND :OnDateEnd ", 1)
|
|
||||||
} else if len(params.AfterDate) > 1 && len(params.BeforeDate) > 1 {
|
|
||||||
afterDate := params.GetAfterDateMillis()
|
|
||||||
beforeDate := params.GetBeforeDateMillis()
|
|
||||||
queryParams["OnDateStart"] = strconv.FormatInt(afterDate, 10)
|
|
||||||
queryParams["OnDateEnd"] = strconv.FormatInt(beforeDate, 10)
|
|
||||||
|
|
||||||
// between clause
|
|
||||||
searchQuery = strings.Replace(searchQuery, "CREATEDATE_CLAUSE", "AND CreateAt BETWEEN :OnDateStart AND :OnDateEnd ", 1)
|
|
||||||
} else if len(params.AfterDate) > 1 {
|
|
||||||
afterDate := params.GetAfterDateMillis()
|
|
||||||
queryParams["AfterDate"] = strconv.FormatInt(afterDate, 10)
|
|
||||||
|
|
||||||
// greater than `after date`
|
|
||||||
searchQuery = strings.Replace(searchQuery, "CREATEDATE_CLAUSE", "AND CreateAt >= :AfterDate ", 1)
|
|
||||||
} else if len(params.BeforeDate) > 1 {
|
|
||||||
beforeDate := params.GetBeforeDateMillis()
|
|
||||||
queryParams["BeforeDate"] = strconv.FormatInt(beforeDate, 10)
|
|
||||||
|
|
||||||
// less than `before date`
|
|
||||||
searchQuery = strings.Replace(searchQuery, "CREATEDATE_CLAUSE", "AND CreateAt <= :BeforeDate ", 1)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// no create date filters set
|
|
||||||
searchQuery = strings.Replace(searchQuery, "CREATEDATE_CLAUSE", "", 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if terms == "" {
|
|
||||||
// we've already confirmed that we have a channel or user to search for
|
// we've already confirmed that we have a channel or user to search for
|
||||||
searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", "", 1)
|
searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", "", 1)
|
||||||
} else if s.DriverName() == model.DATABASE_DRIVER_POSTGRES {
|
} else if s.DriverName() == model.DATABASE_DRIVER_POSTGRES {
|
||||||
// Parse text for wildcards
|
// Parse text for wildcards
|
||||||
if wildcard, err := regexp.Compile(`\*($| )`); err == nil {
|
if wildcard, err := regexp.Compile(`\*($| )`); err == nil {
|
||||||
terms = wildcard.ReplaceAllLiteralString(terms, ":* ")
|
terms = wildcard.ReplaceAllLiteralString(terms, ":* ")
|
||||||
|
excludedTerms = wildcard.ReplaceAllLiteralString(excludedTerms, ":* ")
|
||||||
|
}
|
||||||
|
|
||||||
|
excludeClause := ""
|
||||||
|
if excludedTerms != "" {
|
||||||
|
excludeClause = " & !(" + strings.Join(strings.Fields(excludedTerms), " | ") + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
if params.OrTerms {
|
if params.OrTerms {
|
||||||
terms = strings.Join(strings.Fields(terms), " | ")
|
queryParams["Terms"] = "(" + strings.Join(strings.Fields(terms), " | ") + ")" + excludeClause
|
||||||
} else {
|
} else {
|
||||||
terms = strings.Join(strings.Fields(terms), " & ")
|
queryParams["Terms"] = "(" + strings.Join(strings.Fields(terms), " & ") + ")" + excludeClause
|
||||||
}
|
}
|
||||||
|
|
||||||
searchClause := fmt.Sprintf("AND to_tsvector('english', %s) @@ to_tsquery(:Terms)", searchType)
|
searchClause := fmt.Sprintf("AND to_tsvector('english', %s) @@ to_tsquery(:Terms)", searchType)
|
||||||
@@ -976,43 +1025,45 @@ func (s *SqlPostStore) Search(teamId string, userId string, params *model.Search
|
|||||||
searchClause := fmt.Sprintf("AND MATCH (%s) AGAINST (:Terms IN BOOLEAN MODE)", searchType)
|
searchClause := fmt.Sprintf("AND MATCH (%s) AGAINST (:Terms IN BOOLEAN MODE)", searchType)
|
||||||
searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", searchClause, 1)
|
searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", searchClause, 1)
|
||||||
|
|
||||||
if !params.OrTerms {
|
excludeClause := ""
|
||||||
splitTerms := strings.Fields(terms)
|
if excludedTerms != "" {
|
||||||
for i, t := range strings.Fields(terms) {
|
excludeClause = " -(" + excludedTerms + ")"
|
||||||
splitTerms[i] = "+" + t
|
}
|
||||||
}
|
|
||||||
|
|
||||||
terms = strings.Join(splitTerms, " ")
|
if params.OrTerms {
|
||||||
|
queryParams["Terms"] = terms + excludeClause
|
||||||
|
} else {
|
||||||
|
splitTerms := []string{}
|
||||||
|
for _, t := range strings.Fields(terms) {
|
||||||
|
splitTerms = append(splitTerms, "+"+t)
|
||||||
|
}
|
||||||
|
queryParams["Terms"] = strings.Join(splitTerms, " ") + excludeClause
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
queryParams["Terms"] = terms
|
|
||||||
|
|
||||||
_, err := s.GetSearchReplica().Select(&posts, searchQuery, queryParams)
|
_, err := s.GetSearchReplica().Select(&posts, searchQuery, queryParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
mlog.Warn(fmt.Sprintf("Query error searching posts: %v", err.Error()))
|
mlog.Warn(fmt.Sprintf("Query error searching posts: %v", err.Error()))
|
||||||
// Don't return the error to the caller as it is of no use to the user. Instead return an empty set of search results.
|
// Don't return the error to the caller as it is of no use to the user. Instead return an empty set of search results.
|
||||||
return list, nil
|
} else {
|
||||||
}
|
for _, p := range posts {
|
||||||
|
if searchType == "Hashtags" {
|
||||||
for _, p := range posts {
|
exactMatch := false
|
||||||
if searchType == "Hashtags" {
|
for _, tag := range strings.Split(p.Hashtags, " ") {
|
||||||
exactMatch := false
|
if termMap[strings.ToUpper(tag)] {
|
||||||
for _, tag := range strings.Split(p.Hashtags, " ") {
|
exactMatch = true
|
||||||
if termMap[strings.ToUpper(tag)] {
|
break
|
||||||
exactMatch = true
|
}
|
||||||
|
}
|
||||||
|
if !exactMatch {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !exactMatch {
|
list.AddPost(p)
|
||||||
continue
|
list.AddOrder(p.Id)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
list.AddPost(p)
|
|
||||||
list.AddOrder(p.Id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
list.MakeNonNil()
|
list.MakeNonNil()
|
||||||
|
|
||||||
return list, nil
|
return list, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1170,10 +1170,46 @@ func testPostStoreSearch(t *testing.T, ss store.Store) {
|
|||||||
teamId := model.NewId()
|
teamId := model.NewId()
|
||||||
userId := model.NewId()
|
userId := model.NewId()
|
||||||
|
|
||||||
|
u1 := &model.User{}
|
||||||
|
u1.Username = "usera1"
|
||||||
|
u1.Email = MakeEmail()
|
||||||
|
u1, err := ss.User().Save(u1)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
t1 := &model.TeamMember{}
|
||||||
|
t1.TeamId = teamId
|
||||||
|
t1.UserId = u1.Id
|
||||||
|
_, err = ss.Team().SaveMember(t1, 1000)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
u2 := &model.User{}
|
||||||
|
u2.Username = "userb2"
|
||||||
|
u2.Email = MakeEmail()
|
||||||
|
u2, err = ss.User().Save(u2)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
t2 := &model.TeamMember{}
|
||||||
|
t2.TeamId = teamId
|
||||||
|
t2.UserId = u2.Id
|
||||||
|
_, err = ss.Team().SaveMember(t2, 1000)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
u3 := &model.User{}
|
||||||
|
u3.Username = "userc3"
|
||||||
|
u3.Email = MakeEmail()
|
||||||
|
u3, err = ss.User().Save(u3)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
t3 := &model.TeamMember{}
|
||||||
|
t3.TeamId = teamId
|
||||||
|
t3.UserId = u3.Id
|
||||||
|
_, err = ss.Team().SaveMember(t3, 1000)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
c1 := &model.Channel{}
|
c1 := &model.Channel{}
|
||||||
c1.TeamId = teamId
|
c1.TeamId = teamId
|
||||||
c1.DisplayName = "Channel1"
|
c1.DisplayName = "Channel1"
|
||||||
c1.Name = "zz" + model.NewId() + "b"
|
c1.Name = "channel-x"
|
||||||
c1.Type = model.CHANNEL_OPEN
|
c1.Type = model.CHANNEL_OPEN
|
||||||
c1, _ = ss.Channel().Save(c1, -1)
|
c1, _ = ss.Channel().Save(c1, -1)
|
||||||
|
|
||||||
@@ -1181,20 +1217,20 @@ func testPostStoreSearch(t *testing.T, ss store.Store) {
|
|||||||
m1.ChannelId = c1.Id
|
m1.ChannelId = c1.Id
|
||||||
m1.UserId = userId
|
m1.UserId = userId
|
||||||
m1.NotifyProps = model.GetDefaultChannelNotifyProps()
|
m1.NotifyProps = model.GetDefaultChannelNotifyProps()
|
||||||
_, err := ss.Channel().SaveMember(&m1)
|
_, err = ss.Channel().SaveMember(&m1)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
c2 := &model.Channel{}
|
c2 := &model.Channel{}
|
||||||
c2.TeamId = teamId
|
c2.TeamId = teamId
|
||||||
c2.DisplayName = "Channel1"
|
c2.DisplayName = "Channel2"
|
||||||
c2.Name = "zz" + model.NewId() + "b"
|
c2.Name = "channel-y"
|
||||||
c2.Type = model.CHANNEL_OPEN
|
c2.Type = model.CHANNEL_OPEN
|
||||||
c2, _ = ss.Channel().Save(c2, -1)
|
c2, _ = ss.Channel().Save(c2, -1)
|
||||||
|
|
||||||
c3 := &model.Channel{}
|
c3 := &model.Channel{}
|
||||||
c3.TeamId = teamId
|
c3.TeamId = teamId
|
||||||
c3.DisplayName = "Channel1"
|
c3.DisplayName = "Channel3"
|
||||||
c3.Name = "zz" + model.NewId() + "b"
|
c3.Name = "channel-z"
|
||||||
c3.Type = model.CHANNEL_OPEN
|
c3.Type = model.CHANNEL_OPEN
|
||||||
c3, _ = ss.Channel().Save(c3, -1)
|
c3, _ = ss.Channel().Save(c3, -1)
|
||||||
|
|
||||||
@@ -1209,37 +1245,37 @@ func testPostStoreSearch(t *testing.T, ss store.Store) {
|
|||||||
|
|
||||||
o1 := &model.Post{}
|
o1 := &model.Post{}
|
||||||
o1.ChannelId = c1.Id
|
o1.ChannelId = c1.Id
|
||||||
o1.UserId = model.NewId()
|
o1.UserId = u1.Id
|
||||||
o1.Message = "corey mattermost new york"
|
o1.Message = "corey mattermost new york United States"
|
||||||
o1, err = ss.Post().Save(o1)
|
o1, err = ss.Post().Save(o1)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
o1a := &model.Post{}
|
o1a := &model.Post{}
|
||||||
o1a.ChannelId = c1.Id
|
o1a.ChannelId = c1.Id
|
||||||
o1a.UserId = model.NewId()
|
o1a.UserId = model.NewId()
|
||||||
o1a.Message = "corey mattermost new york"
|
o1a.Message = "corey mattermost new york United States"
|
||||||
o1a.Type = model.POST_JOIN_CHANNEL
|
o1a.Type = model.POST_JOIN_CHANNEL
|
||||||
_, err = ss.Post().Save(o1a)
|
_, err = ss.Post().Save(o1a)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
o2 := &model.Post{}
|
o2 := &model.Post{}
|
||||||
o2.ChannelId = c1.Id
|
o2.ChannelId = c1.Id
|
||||||
o2.UserId = model.NewId()
|
o2.UserId = u2.Id
|
||||||
o2.Message = "New Jersey is where John is from"
|
o2.Message = "New Jersey United States is where John is from"
|
||||||
o2, err = ss.Post().Save(o2)
|
o2, err = ss.Post().Save(o2)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
o3 := &model.Post{}
|
o3 := &model.Post{}
|
||||||
o3.ChannelId = c2.Id
|
o3.ChannelId = c2.Id
|
||||||
o3.UserId = model.NewId()
|
o3.UserId = model.NewId()
|
||||||
o3.Message = "New Jersey is where John is from corey new york"
|
o3.Message = "New Jersey United States is where John is from corey new york"
|
||||||
_, err = ss.Post().Save(o3)
|
_, err = ss.Post().Save(o3)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
o4 := &model.Post{}
|
o4 := &model.Post{}
|
||||||
o4.ChannelId = c1.Id
|
o4.ChannelId = c1.Id
|
||||||
o4.UserId = model.NewId()
|
o4.UserId = model.NewId()
|
||||||
o4.Hashtags = "#hashtag"
|
o4.Hashtags = "#hashtag #tagme"
|
||||||
o4.Message = "(message)blargh"
|
o4.Message = "(message)blargh"
|
||||||
o4, err = ss.Post().Save(o4)
|
o4, err = ss.Post().Save(o4)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
@@ -1247,7 +1283,7 @@ func testPostStoreSearch(t *testing.T, ss store.Store) {
|
|||||||
o5 := &model.Post{}
|
o5 := &model.Post{}
|
||||||
o5.ChannelId = c1.Id
|
o5.ChannelId = c1.Id
|
||||||
o5.UserId = model.NewId()
|
o5.UserId = model.NewId()
|
||||||
o5.Hashtags = "#secret #howdy"
|
o5.Hashtags = "#secret #howdy #tagme"
|
||||||
o5, err = ss.Post().Save(o5)
|
o5, err = ss.Post().Save(o5)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
@@ -1260,8 +1296,8 @@ func testPostStoreSearch(t *testing.T, ss store.Store) {
|
|||||||
|
|
||||||
o7 := &model.Post{}
|
o7 := &model.Post{}
|
||||||
o7.ChannelId = c3.Id
|
o7.ChannelId = c3.Id
|
||||||
o7.UserId = model.NewId()
|
o7.UserId = u3.Id
|
||||||
o7.Message = "New Jersey is where John is from corey new york"
|
o7.Message = "New Jersey United States is where John is from corey new york"
|
||||||
o7, err = ss.Post().Save(o7)
|
o7, err = ss.Post().Save(o7)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
@@ -1314,6 +1350,12 @@ func testPostStoreSearch(t *testing.T, ss store.Store) {
|
|||||||
1,
|
1,
|
||||||
[]string{o5.Id},
|
[]string{o5.Id},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"hashtag-search-with-exclusion",
|
||||||
|
&model.SearchParams{Terms: "#tagme", ExcludedTerms: "#hashtag", IsHashtag: true},
|
||||||
|
1,
|
||||||
|
[]string{o5.Id},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"no-match-mention",
|
"no-match-mention",
|
||||||
&model.SearchParams{Terms: "@thisshouldmatchnothing", IsHashtag: true},
|
&model.SearchParams{Terms: "@thisshouldmatchnothing", IsHashtag: true},
|
||||||
@@ -1326,18 +1368,48 @@ func testPostStoreSearch(t *testing.T, ss store.Store) {
|
|||||||
0,
|
0,
|
||||||
[]string{},
|
[]string{},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"exclude-search",
|
||||||
|
&model.SearchParams{Terms: "united", ExcludedTerms: "jersey"},
|
||||||
|
1,
|
||||||
|
[]string{o1.Id},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"multiple-words-search",
|
"multiple-words-search",
|
||||||
&model.SearchParams{Terms: "corey new york"},
|
&model.SearchParams{Terms: "corey new york"},
|
||||||
1,
|
1,
|
||||||
[]string{o1.Id},
|
[]string{o1.Id},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"multiple-words-with-exclusion-search",
|
||||||
|
&model.SearchParams{Terms: "united states", ExcludedTerms: "jersey"},
|
||||||
|
1,
|
||||||
|
[]string{o1.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"multiple-excluded-words-search",
|
||||||
|
&model.SearchParams{Terms: "united", ExcludedTerms: "corey john"},
|
||||||
|
0,
|
||||||
|
[]string{},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"multiple-wildcard-search",
|
"multiple-wildcard-search",
|
||||||
&model.SearchParams{Terms: "matter* jer*"},
|
&model.SearchParams{Terms: "matter* jer*"},
|
||||||
0,
|
0,
|
||||||
[]string{},
|
[]string{},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"multiple-wildcard-with-exclusion-search",
|
||||||
|
&model.SearchParams{Terms: "unite* state*", ExcludedTerms: "jers*"},
|
||||||
|
1,
|
||||||
|
[]string{o1.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"multiple-wildcard-excluded-words-search",
|
||||||
|
&model.SearchParams{Terms: "united states", ExcludedTerms: "jers* yor*"},
|
||||||
|
0,
|
||||||
|
[]string{},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"search-with-work-next-to-a-symbol",
|
"search-with-work-next-to-a-symbol",
|
||||||
&model.SearchParams{Terms: "message blargh"},
|
&model.SearchParams{Terms: "message blargh"},
|
||||||
@@ -1350,6 +1422,60 @@ func testPostStoreSearch(t *testing.T, ss store.Store) {
|
|||||||
2,
|
2,
|
||||||
[]string{o1.Id, o2.Id},
|
[]string{o1.Id, o2.Id},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"exclude-search-with-or",
|
||||||
|
&model.SearchParams{Terms: "york jersey", ExcludedTerms: "john", OrTerms: true},
|
||||||
|
1,
|
||||||
|
[]string{o1.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"search-with-from-user",
|
||||||
|
&model.SearchParams{Terms: "united states", FromUsers: []string{"usera1"}, IncludeDeletedChannels: true},
|
||||||
|
1,
|
||||||
|
[]string{o1.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"search-with-multiple-from-user",
|
||||||
|
&model.SearchParams{Terms: "united states", FromUsers: []string{"usera1", "userc3"}, IncludeDeletedChannels: true},
|
||||||
|
2,
|
||||||
|
[]string{o1.Id, o7.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"search-with-excluded-user",
|
||||||
|
&model.SearchParams{Terms: "united states", ExcludedUsers: []string{"usera1"}, IncludeDeletedChannels: true},
|
||||||
|
2,
|
||||||
|
[]string{o2.Id, o7.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"search-with-multiple-excluded-user",
|
||||||
|
&model.SearchParams{Terms: "united states", ExcludedUsers: []string{"usera1", "userb2"}, IncludeDeletedChannels: true},
|
||||||
|
1,
|
||||||
|
[]string{o7.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"search-with-deleted-and-channel-filter",
|
||||||
|
&model.SearchParams{Terms: "Jersey corey", InChannels: []string{"channel-x"}, IncludeDeletedChannels: true, OrTerms: true},
|
||||||
|
2,
|
||||||
|
[]string{o1.Id, o2.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"search-with-deleted-and-multiple-channel-filter",
|
||||||
|
&model.SearchParams{Terms: "Jersey corey", InChannels: []string{"channel-x", "channel-z"}, IncludeDeletedChannels: true, OrTerms: true},
|
||||||
|
3,
|
||||||
|
[]string{o1.Id, o2.Id, o7.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"search-with-deleted-and-excluded-channel-filter",
|
||||||
|
&model.SearchParams{Terms: "Jersey corey", ExcludedChannels: []string{"channel-x"}, IncludeDeletedChannels: true, OrTerms: true},
|
||||||
|
1,
|
||||||
|
[]string{o7.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"search-with-deleted-and-multiple-excluded-channel-filter",
|
||||||
|
&model.SearchParams{Terms: "Jersey corey", ExcludedChannels: []string{"channel-x", "channel-z"}, IncludeDeletedChannels: true, OrTerms: true},
|
||||||
|
0,
|
||||||
|
[]string{},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"search-with-or-and-deleted",
|
"search-with-or-and-deleted",
|
||||||
&model.SearchParams{Terms: "Jersey corey", OrTerms: true, IncludeDeletedChannels: true},
|
&model.SearchParams{Terms: "Jersey corey", OrTerms: true, IncludeDeletedChannels: true},
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user