github.com/rajatvaryani/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 }