2016-06-27 68 views
0

我正在開發一個API,它需要運算符作爲值的過濾器。例如。 &val=true&Amount>33"。我認爲內部表示形式是map [string] struct {Operator string,Val [] string},但它似乎不被標準庫支持。查詢字符串運算符

是否有任何外部軟件包支持此類查詢?這是一個常見的用例(例如搜索API),所以我很驚訝我找不到Godoc上的任何東西。

+0

我不認爲我見過它接受查詢字符串那樣的搜索API。出於好奇,你能舉個例子嗎? – smarx

+0

我從來沒有見過這種「常見」的情況,我不會推薦它,因爲你受限於保留字符和尷尬逃逸。 (加上這是脫離主題,因爲你明確要求一個外部包。) – JimB

+0

同上這不是常見的,我想幾乎所有的URL庫會扼住'&val = true&amount> 33',我只見過??k = v&k = v ...' – Plato

回答

2

看起來用例並不像我想的那麼普遍。我實現了一個自定義的「解析器」。希望這不會違反任何RFC。歡迎任何補丁!

package query 

import(
    "net/url" 
    "strings" 
) 


//ParseQueryOp parses the URL-encoded query string and returns a map 
// listing the values specified for each key. ParseQueryOp always returns 
// a non-nil map containing all the valid query parameters found; 
// err describes the first decoding error encountered, if any. 
// If the query has operators (e.g. &amount>300) it is Add-ed in the map 
// with a prefixed key ({{prefix}}key). 
/* 
e.g. for a query &Amount>300 the operator(">") is returnd 
    s := "query &Amount>300" 
    q, _ := ParseQueryOp(s, "_op_") 
    print(q.Get("_op_Amount")) // prints `>` 
    print(q.Get("Amount")) // prints 300 
*/ 
func ParseQueryOp(query, prefix string) (m url.Values, err error){ 
    m = make(url.Values) 

    for query != "" { 
     var opKey string 
     key := query 
     if i := strings.IndexAny(key, "&;"); i >= 0 { 
      key, query = key[:i], key[i+1:] 
     } else { 
      query = "" 
     } 
     if key == "" { 
      continue 
     } 
     value := "" 
     var err1 error 
     if i := strings.Index(key, ">="); i >= 0{ 
      key, value = key[:i], key[i+2:] 
      opKey = prefix + key 
      opKey, err1 = url.QueryUnescape(opKey) 
      if err1 != nil { 
       if err == nil { 
        err = err1 
       } 
       continue 
      } 
      m[opKey] = append(m[opKey], ">=") 
     }else if i = strings.Index(key, "<="); i >= 0{ 
      key, value = key[:i], key[i+2:] 
      opKey = prefix + key 
      opKey, err1 = url.QueryUnescape(opKey) 
      if err1 != nil { 
       if err == nil { 
        err = err1 
       } 
       continue 
      } 
      m[opKey] = append(m[opKey], "<=") 
     }else if i = strings.Index(key, "="); i >= 0{ 
      key, value = key[:i], key[i+1:] 
     }else if i = strings.Index(key, ">"); i >= 0{ 
      key, value = key[:i], key[i+1:] 
      opKey = prefix + key 
      opKey, err1 = url.QueryUnescape(opKey) 
      if err1 != nil { 
       if err == nil { 
        err = err1 
       } 
       continue 
      } 
      m[opKey] = append(m[opKey], ">") 
     }else if i = strings.Index(key, "<"); i >= 0{ 
      key, value = key[:i], key[i+1:] 
      opKey = prefix + key 
      opKey, err1 = url.QueryUnescape(opKey) 
      if err1 != nil { 
       if err == nil { 
        err = err1 
       } 
       continue 
      } 
      m[opKey] = append(m[opKey], "<") 
     } 

     key, err1 = url.QueryUnescape(key) 
     if err1 != nil { 
      if err == nil { 
       err = err1 
      } 
      continue 
     } 
     value, err1 = url.QueryUnescape(value) 
     if err1 != nil { 
      if err == nil { 
       err = err1 
      } 
      continue 
     } 
     m[key] = append(m[key], value) 
    } 
    return m, err 
} 

而且有些測試

package query 


import(
    "testing" 
    "net/url" 
) 


type parseTest struct { 
    query string 
    out url.Values 
} 


var parseTests = []parseTest{ 
    { 
     query: "a=1&b=2", 
     out: url.Values{"a": []string{"1"}, "b": []string{"2"}}, 
    }, 
    { 
     query: "a=1&a=2&a=banana", 
     out: url.Values{"a": []string{"1", "2", "banana"}}, 
    }, 
    { 
     query: "ascii=%3Ckey%3A+0x90%3E", 
     out: url.Values{"ascii": []string{"<key: 0x90>"}}, 
    }, 
    { 
     query: "a=1;b=2", 
     out: url.Values{"a": []string{"1"}, "b": []string{"2"}}, 
    }, 
    { 
     query: "a=1&a=2;a=banana", 
     out: url.Values{"a": []string{"1", "2", "banana"}}, 
    }, 
    { 
     query: "a=1&b>2", 
     out: url.Values{"a": []string{"1"}, "_op_b": []string{">"}, "b": []string{"2"}}, 
    }, 
    { 
     query: "a=1&b<2", 
     out: url.Values{"a": []string{"1"}, "_op_b": []string{"<"}, "b": []string{"2"}}, 
    }, 
    { 
     query: "a=1&b>=2", 
     out: url.Values{"a": []string{"1"}, "_op_b": []string{">="}, "b": []string{"2"}}, 
    }, 
    { 
     query: "a=1&b<=2", 
     out: url.Values{"a": []string{"1"}, "_op_b": []string{"<="}, "b": []string{"2"}}, 
    }, 
} 

func TestParseQueryOut(t *testing.T) { 
    for i, test := range parseTests { 
     form, err := ParseQueryOp(test.query, "_op_") 
     if err != nil { 
      t.Errorf("test %d: Unexpected error: %v", i, err) 
      continue 
     } 
     if len(form) != len(test.out) { 
      t.Errorf("test %d: len(form) = %d, want %d", i, len(form), len(test.out)) 
     } 
     for k, evs := range test.out { 
      vs, ok := form[k] 
      if !ok { 
       t.Errorf("test %d: Missing key %q", i, k) 
       continue 
      } 
      if len(vs) != len(evs) { 
       t.Errorf("test %d: len(form[%q]) = %d, want %d", i, k, len(vs), len(evs)) 
       continue 
      } 
      for j, ev := range evs { 
       if v := vs[j]; v != ev { 
        t.Errorf("test %d: form[%q][%d] = %q, want %q", i, k, j, v, ev) 
       } 
      } 
     } 
    } 
}