2016-06-27 10 views
0

Ich entwickle eine API, die Operatoren als Filter für Werte benötigt. Z.B. &val=true&Amount>33". Ich denke, die interne Repräsentation wäre map [string] struct {Operator string, Val [] string}, aber es scheint nicht von der Standardbibliothek unterstützt zu werden.Abfrage Zeichenfolge Operator

Gibt es ein externes Paket mit Unterstützung für solche Abfragen? Es ist ein häufiger Anwendungsfall (z. B. für eine Such-API), also bin ich überrascht, dass ich nichts über godoc finden konnte.

+0

Ich glaube nicht, dass ich eine Suche API gesehen haben, die Abfrage-Strings wie das akzeptiert. Kannst du aus Neugier ein Beispiel geben? – smarx

+0

Ich habe diesen "gewöhnlichen" Fall auch noch nie gesehen, und ich würde ihn nicht empfehlen, da Sie durch reservierte Charaktere und umständliches Entkommen eingeschränkt sind. (plus das ist Off-Thema, da Sie ausdrücklich nach einem externen Paket fragen.) – JimB

+0

dito das ist nicht üblich, ich denke, fast jede URL lib wird ersticken auf '& val = wahr & Betrag> 33', ich habe nur '' k gesehen = v & k = v ... ' – Plato

Antwort

2

Es scheint, dass der Anwendungsfall nicht so häufig ist, wie ich dachte. Ich habe einen benutzerdefinierten "Parser" implementiert. Hoffentlich verletzt das keinen RFC. Jeder Patch ist willkommen!

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 
} 

Und einige Tests

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) 
       } 
      } 
     } 
    } 
} 
Verwandte Themen