doupaoyan6083 2016-06-27 21:29
浏览 58
已采纳

查询字符串运算符

I'm developing an API which requires operators as filters on values. E.g. &val=true&Amount>33". I think the internal representation would be map[string]struct{Operator string, Val []string} but it doesn't seem supported by the standard library.

Is there any external package with support for such queries? It's a common use case(e.g. for a search API) so I'm surprised I couldn't find anything on godoc.

  • 写回答

1条回答 默认 最新

  • douyi9447 2016-06-28 01:13
    关注

    It seems the use case is not as common as I thought. I implemented a custom "parser". Hopefully this doesn't violate any RFC. Any patch is welcome!

    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
    }
    

    And some 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)
                    }
                }
            }
        }
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥20 机器学习能否像多层线性模型一样处理嵌套数据
  • ¥20 西门子S7-Graph,S7-300,梯形图
  • ¥50 用易语言http 访问不了网页
  • ¥50 safari浏览器fetch提交数据后数据丢失问题
  • ¥15 matlab不知道怎么改,求解答!!
  • ¥15 永磁直线电机的电流环pi调不出来
  • ¥15 用stata实现聚类的代码
  • ¥15 请问paddlehub能支持移动端开发吗?在Android studio上该如何部署?
  • ¥20 docker里部署springboot项目,访问不到扬声器
  • ¥15 netty整合springboot之后自动重连失效