From 06fd374c1907add3faeeba7916b279e0a3302a4e Mon Sep 17 00:00:00 2001 From: hmhealey Date: Sat, 17 Oct 2015 14:37:51 -0400 Subject: Added from:, in:, and channel: search modifiers --- model/search_params.go | 130 ++++++++++++++++++++++++++++++++++++++++++++ model/search_params_test.go | 70 ++++++++++++++++++++++++ model/utils.go | 4 +- 3 files changed, 202 insertions(+), 2 deletions(-) create mode 100644 model/search_params.go create mode 100644 model/search_params_test.go (limited to 'model') diff --git a/model/search_params.go b/model/search_params.go new file mode 100644 index 000000000..7eeeed10f --- /dev/null +++ b/model/search_params.go @@ -0,0 +1,130 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "strings" +) + +type SearchParams struct { + Terms string + IsHashtag bool + InChannel string + FromUser string +} + +var searchFlags = [...]string{"from", "channel", "in"} + +func splitWords(text string) []string { + words := []string{} + + for _, word := range strings.Fields(text) { + word = puncStart.ReplaceAllString(word, "") + word = puncEnd.ReplaceAllString(word, "") + + if len(word) != 0 { + words = append(words, word) + } + } + + return words +} + +func parseSearchFlags(input []string) ([]string, map[string]string) { + words := []string{} + flags := make(map[string]string) + + skipNextWord := false + for i, word := range input { + if skipNextWord { + skipNextWord = false + continue + } + + isFlag := false + + if colon := strings.Index(word, ":"); colon != -1 { + flag := word[:colon] + value := word[colon+1:] + + for _, searchFlag := range searchFlags { + // check for case insensitive equality + if strings.EqualFold(flag, searchFlag) { + if value != "" { + flags[searchFlag] = value + isFlag = true + } else if i < len(input)-1 { + flags[searchFlag] = input[i+1] + skipNextWord = true + isFlag = true + } + + if isFlag { + break + } + } + } + } + + if !isFlag { + words = append(words, word) + } + } + + return words, flags +} + +func ParseSearchParams(text string) (*SearchParams, *SearchParams) { + words, flags := parseSearchFlags(splitWords(text)) + + hashtagTerms := []string{} + plainTerms := []string{} + + for _, word := range words { + if validHashtag.MatchString(word) { + hashtagTerms = append(hashtagTerms, word) + } else { + plainTerms = append(plainTerms, word) + } + } + + inChannel := flags["channel"] + if inChannel == "" { + inChannel = flags["in"] + } + + fromUser := flags["from"] + + var plainParams *SearchParams + if len(plainTerms) > 0 { + plainParams = &SearchParams{ + Terms: strings.Join(plainTerms, " "), + IsHashtag: false, + InChannel: inChannel, + FromUser: fromUser, + } + } + + var hashtagParams *SearchParams + if len(hashtagTerms) > 0 { + hashtagParams = &SearchParams{ + Terms: strings.Join(hashtagTerms, " "), + IsHashtag: true, + InChannel: inChannel, + FromUser: fromUser, + } + } + + // special case for when no terms are specified but we still have a filter + if plainParams == nil && hashtagParams == nil && (inChannel != "" || fromUser != "") { + plainParams = &SearchParams{ + Terms: "", + IsHashtag: false, + InChannel: inChannel, + FromUser: fromUser, + } + } + + return plainParams, hashtagParams +} diff --git a/model/search_params_test.go b/model/search_params_test.go new file mode 100644 index 000000000..2eba20f4c --- /dev/null +++ b/model/search_params_test.go @@ -0,0 +1,70 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "testing" +) + +func TestParseSearchFlags(t *testing.T) { + if words, flags := parseSearchFlags(splitWords("")); len(words) != 0 { + t.Fatal("got words from empty input") + } else if len(flags) != 0 { + t.Fatal("got flags from empty input") + } + + if words, flags := parseSearchFlags(splitWords("word")); len(words) != 1 || words[0] != "word" { + t.Fatalf("got incorrect words %v", words) + } else if len(flags) != 0 { + t.Fatalf("got incorrect flags %v", flags) + } + + if words, flags := parseSearchFlags(splitWords("apple banana cherry")); len(words) != 3 || words[0] != "apple" || words[1] != "banana" || words[2] != "cherry" { + t.Fatalf("got incorrect words %v", words) + } else if len(flags) != 0 { + t.Fatalf("got incorrect flags %v", flags) + } + + if words, flags := parseSearchFlags(splitWords("apple banana from:chan")); len(words) != 2 || words[0] != "apple" || words[1] != "banana" { + t.Fatalf("got incorrect words %v", words) + } else if len(flags) != 1 || flags["from"] != "chan" { + t.Fatalf("got incorrect flags %v", flags) + } + + if words, flags := parseSearchFlags(splitWords("apple banana from: chan")); len(words) != 2 || words[0] != "apple" || words[1] != "banana" { + t.Fatalf("got incorrect words %v", words) + } else if len(flags) != 1 || flags["from"] != "chan" { + t.Fatalf("got incorrect flags %v", flags) + } + + if words, flags := parseSearchFlags(splitWords("apple banana in: chan")); len(words) != 2 || words[0] != "apple" || words[1] != "banana" { + t.Fatalf("got incorrect words %v", words) + } else if len(flags) != 1 || flags["in"] != "chan" { + t.Fatalf("got incorrect flags %v", flags) + } + + if words, flags := parseSearchFlags(splitWords("apple banana channel:chan")); len(words) != 2 || words[0] != "apple" || words[1] != "banana" { + t.Fatalf("got incorrect words %v", words) + } else if len(flags) != 1 || flags["channel"] != "chan" { + t.Fatalf("got incorrect flags %v", flags) + } + + if words, flags := parseSearchFlags(splitWords("fruit: cherry")); len(words) != 2 || words[0] != "fruit:" || words[1] != "cherry" { + t.Fatalf("got incorrect words %v", words) + } else if len(flags) != 0 { + t.Fatalf("got incorrect flags %v", flags) + } + + if words, flags := parseSearchFlags(splitWords("channel:")); len(words) != 1 || words[0] != "channel:" { + t.Fatalf("got incorrect words %v", words) + } else if len(flags) != 0 { + t.Fatalf("got incorrect flags %v", flags) + } + + if words, flags := parseSearchFlags(splitWords("channel: first in: second from:")); len(words) != 1 || words[0] != "from:" { + t.Fatalf("got incorrect words %v", words) + } else if len(flags) != 2 || flags["channel"] != "first" || flags["in"] != "second" { + t.Fatalf("got incorrect flags %v", flags) + } +} diff --git a/model/utils.go b/model/utils.go index 269144afc..bb0669df7 100644 --- a/model/utils.go +++ b/model/utils.go @@ -242,10 +242,10 @@ func Etag(parts ...interface{}) string { var validHashtag = regexp.MustCompile(`^(#[A-Za-z]+[A-Za-z0-9_\-]*[A-Za-z0-9])$`) var puncStart = regexp.MustCompile(`^[.,()&$!\[\]{}"':;\\]+`) -var puncEnd = regexp.MustCompile(`[.,()&$#!\[\]{}"':;\\]+$`) +var puncEnd = regexp.MustCompile(`[.,()&$#!\[\]{}"';\\]+$`) func ParseHashtags(text string) (string, string) { - words := strings.Split(strings.Replace(text, "\n", " ", -1), " ") + words := strings.Fields(text) hashtagString := "" plainString := "" -- cgit v1.2.3-1-g7c22