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