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