github.com/hahmadia/mattermost-server@v5.11.1+incompatible/model/search_params.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package model
     5  
     6  import (
     7  	"regexp"
     8  	"strings"
     9  	"time"
    10  )
    11  
    12  var searchTermPuncStart = regexp.MustCompile(`^[^\pL\d\s#"]+`)
    13  var searchTermPuncEnd = regexp.MustCompile(`[^\pL\d\s*"]+$`)
    14  
    15  type SearchParams struct {
    16  	Terms                  string
    17  	IsHashtag              bool
    18  	InChannels             []string
    19  	FromUsers              []string
    20  	AfterDate              string
    21  	BeforeDate             string
    22  	OnDate                 string
    23  	OrTerms                bool
    24  	IncludeDeletedChannels bool
    25  	TimeZoneOffset         int
    26  	// True if this search doesn't originate from a "current user".
    27  	SearchWithoutUserId bool
    28  }
    29  
    30  // Returns the epoch timestamp of the start of the day specified by SearchParams.AfterDate
    31  func (p *SearchParams) GetAfterDateMillis() int64 {
    32  	date, err := time.Parse("2006-01-02", PadDateStringZeros(p.AfterDate))
    33  	if err != nil {
    34  		date = time.Now()
    35  	}
    36  
    37  	// travel forward 1 day
    38  	oneDay := time.Hour * 24
    39  	afterDate := date.Add(oneDay)
    40  	return GetStartOfDayMillis(afterDate, p.TimeZoneOffset)
    41  }
    42  
    43  // Returns the epoch timestamp of the end of the day specified by SearchParams.BeforeDate
    44  func (p *SearchParams) GetBeforeDateMillis() int64 {
    45  	date, err := time.Parse("2006-01-02", PadDateStringZeros(p.BeforeDate))
    46  	if err != nil {
    47  		return 0
    48  	}
    49  
    50  	// travel back 1 day
    51  	oneDay := time.Hour * -24
    52  	beforeDate := date.Add(oneDay)
    53  	return GetEndOfDayMillis(beforeDate, p.TimeZoneOffset)
    54  }
    55  
    56  // Returns the epoch timestamps of the start and end of the day specified by SearchParams.OnDate
    57  func (p *SearchParams) GetOnDateMillis() (int64, int64) {
    58  	date, err := time.Parse("2006-01-02", PadDateStringZeros(p.OnDate))
    59  	if err != nil {
    60  		return 0, 0
    61  	}
    62  
    63  	return GetStartOfDayMillis(date, p.TimeZoneOffset), GetEndOfDayMillis(date, p.TimeZoneOffset)
    64  }
    65  
    66  var searchFlags = [...]string{"from", "channel", "in", "before", "after", "on"}
    67  
    68  func splitWords(text string) []string {
    69  	words := []string{}
    70  
    71  	foundQuote := false
    72  	location := 0
    73  	for i, char := range text {
    74  		if char == '"' {
    75  			if foundQuote {
    76  				// Grab the quoted section
    77  				word := text[location : i+1]
    78  				words = append(words, word)
    79  				foundQuote = false
    80  				location = i + 1
    81  			} else {
    82  				words = append(words, strings.Fields(text[location:i])...)
    83  				foundQuote = true
    84  				location = i
    85  			}
    86  		}
    87  	}
    88  
    89  	words = append(words, strings.Fields(text[location:])...)
    90  
    91  	return words
    92  }
    93  
    94  func parseSearchFlags(input []string) ([]string, [][2]string) {
    95  	words := []string{}
    96  	flags := [][2]string{}
    97  
    98  	skipNextWord := false
    99  	for i, word := range input {
   100  		if skipNextWord {
   101  			skipNextWord = false
   102  			continue
   103  		}
   104  
   105  		isFlag := false
   106  
   107  		if colon := strings.Index(word, ":"); colon != -1 {
   108  			flag := word[:colon]
   109  			value := word[colon+1:]
   110  
   111  			for _, searchFlag := range searchFlags {
   112  				// check for case insensitive equality
   113  				if strings.EqualFold(flag, searchFlag) {
   114  					if value != "" {
   115  						flags = append(flags, [2]string{searchFlag, value})
   116  						isFlag = true
   117  					} else if i < len(input)-1 {
   118  						flags = append(flags, [2]string{searchFlag, input[i+1]})
   119  						skipNextWord = true
   120  						isFlag = true
   121  					}
   122  
   123  					if isFlag {
   124  						break
   125  					}
   126  				}
   127  			}
   128  		}
   129  
   130  		if !isFlag {
   131  			// trim off surrounding punctuation (note that we leave trailing asterisks to allow wildcards)
   132  			word = searchTermPuncStart.ReplaceAllString(word, "")
   133  			word = searchTermPuncEnd.ReplaceAllString(word, "")
   134  
   135  			// and remove extra pound #s
   136  			word = hashtagStart.ReplaceAllString(word, "#")
   137  
   138  			if len(word) != 0 {
   139  				words = append(words, word)
   140  			}
   141  		}
   142  	}
   143  
   144  	return words, flags
   145  }
   146  
   147  func ParseSearchParams(text string, timeZoneOffset int) []*SearchParams {
   148  	words, flags := parseSearchFlags(splitWords(text))
   149  
   150  	hashtagTermList := []string{}
   151  	plainTermList := []string{}
   152  
   153  	for _, word := range words {
   154  		if validHashtag.MatchString(word) {
   155  			hashtagTermList = append(hashtagTermList, word)
   156  		} else {
   157  			plainTermList = append(plainTermList, word)
   158  		}
   159  	}
   160  
   161  	hashtagTerms := strings.Join(hashtagTermList, " ")
   162  	plainTerms := strings.Join(plainTermList, " ")
   163  
   164  	inChannels := []string{}
   165  	fromUsers := []string{}
   166  	afterDate := ""
   167  	beforeDate := ""
   168  	onDate := ""
   169  
   170  	for _, flagPair := range flags {
   171  		flag := flagPair[0]
   172  		value := flagPair[1]
   173  
   174  		if flag == "in" || flag == "channel" {
   175  			inChannels = append(inChannels, value)
   176  		} else if flag == "from" {
   177  			fromUsers = append(fromUsers, value)
   178  		} else if flag == "after" {
   179  			afterDate = value
   180  		} else if flag == "before" {
   181  			beforeDate = value
   182  		} else if flag == "on" {
   183  			onDate = value
   184  		}
   185  	}
   186  
   187  	paramsList := []*SearchParams{}
   188  
   189  	if len(plainTerms) > 0 {
   190  		paramsList = append(paramsList, &SearchParams{
   191  			Terms:          plainTerms,
   192  			IsHashtag:      false,
   193  			InChannels:     inChannels,
   194  			FromUsers:      fromUsers,
   195  			AfterDate:      afterDate,
   196  			BeforeDate:     beforeDate,
   197  			OnDate:         onDate,
   198  			TimeZoneOffset: timeZoneOffset,
   199  		})
   200  	}
   201  
   202  	if len(hashtagTerms) > 0 {
   203  		paramsList = append(paramsList, &SearchParams{
   204  			Terms:          hashtagTerms,
   205  			IsHashtag:      true,
   206  			InChannels:     inChannels,
   207  			FromUsers:      fromUsers,
   208  			AfterDate:      afterDate,
   209  			BeforeDate:     beforeDate,
   210  			OnDate:         onDate,
   211  			TimeZoneOffset: timeZoneOffset,
   212  		})
   213  	}
   214  
   215  	// special case for when no terms are specified but we still have a filter
   216  	if len(plainTerms) == 0 && len(hashtagTerms) == 0 && (len(inChannels) != 0 || len(fromUsers) != 0 || len(afterDate) != 0 || len(beforeDate) != 0 || len(onDate) != 0) {
   217  		paramsList = append(paramsList, &SearchParams{
   218  			Terms:          "",
   219  			IsHashtag:      false,
   220  			InChannels:     inChannels,
   221  			FromUsers:      fromUsers,
   222  			AfterDate:      afterDate,
   223  			BeforeDate:     beforeDate,
   224  			OnDate:         onDate,
   225  			TimeZoneOffset: timeZoneOffset,
   226  		})
   227  	}
   228  
   229  	return paramsList
   230  }