support basic json filtering and selection

This commit is contained in:
Chris Lu
2019-10-06 22:35:05 -07:00
parent e26670c67a
commit f8d4b7d1c0
7 changed files with 418 additions and 186 deletions

View File

@@ -1,5 +1,107 @@
package json
func QueryJson(jsonLine string, query string) (jsonOutput string) {
return jsonLine
import (
"strconv"
"github.com/chrislusf/seaweedfs/weed/query/sqltypes"
"github.com/tidwall/gjson"
"github.com/tidwall/match"
)
type Query struct {
Field string
Op string
Value string
}
func QueryJson(jsonLine string, projections []string, query Query) (passedFilter bool, values []sqltypes.Value) {
if filterJson(jsonLine, query) {
passedFilter = true
fields := gjson.GetMany(jsonLine, projections...)
for _, f := range fields {
values = append(values, sqltypes.MakeTrusted(sqltypes.Type(f.Type), sqltypes.StringToBytes(f.Raw)))
}
return
}
return false, nil
}
func filterJson(jsonLine string, query Query) bool{
value := gjson.Get(jsonLine, query.Field)
// copied from gjson.go queryMatches() function
rpv := query.Value
if !value.Exists() {
return false
}
if query.Op == "" {
// the query is only looking for existence, such as:
// friends.#(name)
// which makes sure that the array "friends" has an element of
// "name" that exists
return true
}
switch value.Type {
case gjson.String:
switch query.Op {
case "=":
return value.Str == rpv
case "!=":
return value.Str != rpv
case "<":
return value.Str < rpv
case "<=":
return value.Str <= rpv
case ">":
return value.Str > rpv
case ">=":
return value.Str >= rpv
case "%":
return match.Match(value.Str, rpv)
case "!%":
return !match.Match(value.Str, rpv)
}
case gjson.Number:
rpvn, _ := strconv.ParseFloat(rpv, 64)
switch query.Op {
case "=":
return value.Num == rpvn
case "!=":
return value.Num != rpvn
case "<":
return value.Num < rpvn
case "<=":
return value.Num <= rpvn
case ">":
return value.Num > rpvn
case ">=":
return value.Num >= rpvn
}
case gjson.True:
switch query.Op {
case "=":
return rpv == "true"
case "!=":
return rpv != "true"
case ">":
return rpv == "false"
case ">=":
return true
}
case gjson.False:
switch query.Op {
case "=":
return rpv == "false"
case "!=":
return rpv != "false"
case "<":
return rpv == "true"
case "<=":
return true
}
}
return false
}

View File

@@ -58,7 +58,7 @@ func TestGjson(t *testing.T) {
projections := []string{"quiz","fruit"}
gjson.ForEachLine(data, func(line gjson.Result) bool{
println(line.String())
println(line.Raw)
println("+++++++++++")
results := gjson.GetMany(line.Raw, projections...)
for _, result := range results {
@@ -71,3 +71,66 @@ func TestGjson(t *testing.T) {
}
func TestJsonQueryRow(t *testing.T) {
data := `
{
"fruit": "Bl\"ue",
"size": 6,
"quiz": "green"
}
`
selections := []string{"fruit", "size"}
isFiltered, values := QueryJson(data, selections, Query{
Field: "quiz",
Op: "=",
Value: "green",
})
if !isFiltered {
t.Errorf("should have been filtered")
}
if values == nil {
t.Errorf("values should have been returned")
}
buf := ToJson(nil, selections, values)
println(string(buf))
}
func TestJsonQueryNumber(t *testing.T) {
data := `
{
"fruit": "Bl\"ue",
"size": 6,
"quiz": "green"
}
`
selections := []string{"fruit", "quiz"}
isFiltered, values := QueryJson(data, selections, Query{
Field: "size",
Op: ">=",
Value: "6",
})
if !isFiltered {
t.Errorf("should have been filtered")
}
if values == nil {
t.Errorf("values should have been returned")
}
buf := ToJson(nil, selections, values)
println(string(buf))
}

View File

@@ -0,0 +1,17 @@
package json
import "github.com/chrislusf/seaweedfs/weed/query/sqltypes"
func ToJson(buf []byte, selections []string, values []sqltypes.Value) []byte {
buf = append(buf, '{')
for i, value := range values {
if i > 0 {
buf = append(buf, ',')
}
buf = append(buf, selections[i]...)
buf = append(buf, ':')
buf = append(buf, value.Raw()...)
}
buf = append(buf, '}')
return buf
}