Bladeren bron

feat(func): support reserved words in json path

ngjaying 4 jaren geleden
bovenliggende
commit
5c6afa2f4c
3 gewijzigde bestanden met toevoegingen van 42 en 5 verwijderingen
  1. 9 1
      docs/en_US/json_expr.md
  2. 13 4
      xsql/lexical.go
  3. 20 0
      xsql/plans/misc_func_test.go

+ 9 - 1
docs/en_US/json_expr.md

@@ -137,7 +137,7 @@ All these functions share the same parameter signatures, among which, the second
 The basic grammar of those expressions is to use the keys part of the JSON objects combined with some elements:
 The basic grammar of those expressions is to use the keys part of the JSON objects combined with some elements:
 
 
 - Dots `.` to move into a tree
 - Dots `.` to move into a tree
-- Brackets `[]` for access to a given array member coupled with a position.
+- Brackets `[]` for access to a given array member coupled with a position. It can also access to a map field.
 - Variables, with `$` representing a JSON text and `@` for result path evaluations.
 - Variables, with `$` representing a JSON text and `@` for result path evaluations.
 
 
 So for example, when applied to the previous JSON data sample we can reach the following parts of the tree with these expressions:
 So for example, when applied to the previous JSON data sample we can reach the following parts of the tree with these expressions:
@@ -146,6 +146,7 @@ So for example, when applied to the previous JSON data sample we can reach the f
 - `$.friends.first` refers to “dale”.
 - `$.friends.first` refers to “dale”.
 - `$.friends` refers to the full array of friends.
 - `$.friends` refers to the full array of friends.
 - `$.friends[0]` refers to the first friend listed in the previous array (contrary to arrays members are zero-based).
 - `$.friends[0]` refers to the first friend listed in the previous array (contrary to arrays members are zero-based).
+- `$.friends[0][lastname]` refers to the lastname of the first friend listed. Use bracket if there are reserved words in the field key.
 - `$.friends[? @.age>60].first` or `$.friends[? (@.age>60)].first` refers to the first name of the friends whose age is bigger than 60. Notice that the space between ? and the condition is required even the condition is with braces.
 - `$.friends[? @.age>60].first` or `$.friends[? (@.age>60)].first` refers to the first name of the friends whose age is bigger than 60. Notice that the space between ? and the condition is required even the condition is with braces.
 
 
 Developers can use the json functions in the SQL statement. Here are some examples.
 Developers can use the json functions in the SQL statement. Here are some examples.
@@ -171,6 +172,13 @@ SELECT json_path_exists(followers, "$.Group1[? @.age>30].last") FROM demo
 ["Miller"]
 ["Miller"]
 ```
 ```
 
 
+- Assume there is a field in follows with reserd word like space `my.follower`, use bracket to access it.
+```tsql
+SELECT json_path_exists(followers, "$[\"my.follower\"]") FROM demo
+
+["Miller"]
+```
+
 # *Projections*
 # *Projections*
 
 
 <!--Do we need to support this?-->
 <!--Do we need to support this?-->

+ 13 - 4
xsql/lexical.go

@@ -4,6 +4,7 @@ import (
 	"bufio"
 	"bufio"
 	"bytes"
 	"bytes"
 	"io"
 	"io"
+	"strconv"
 	"strings"
 	"strings"
 )
 )
 
 
@@ -465,18 +466,26 @@ func (s *Scanner) ScanIdent() (tok Token, lit string) {
 
 
 func (s *Scanner) ScanString() (tok Token, lit string) {
 func (s *Scanner) ScanString() (tok Token, lit string) {
 	var buf bytes.Buffer
 	var buf bytes.Buffer
-	_ = s.read()
+	ch := s.read()
+	buf.WriteRune(ch)
+	escape := false
 	for {
 	for {
-		ch := s.read()
-		if ch == '"' {
+		ch = s.read()
+		if ch == '"' && !escape {
+			buf.WriteRune(ch)
 			break
 			break
 		} else if ch == eof {
 		} else if ch == eof {
 			return BADSTRING, buf.String()
 			return BADSTRING, buf.String()
+		} else if ch == '\\' && !escape {
+			escape = true
+			buf.WriteRune(ch)
 		} else {
 		} else {
+			escape = false
 			buf.WriteRune(ch)
 			buf.WriteRune(ch)
 		}
 		}
 	}
 	}
-	return STRING, buf.String()
+	r, _ := strconv.Unquote(buf.String())
+	return STRING, r
 }
 }
 
 
 func (s *Scanner) ScanDigit() (tok Token, lit string) {
 func (s *Scanner) ScanDigit() (tok Token, lit string) {

+ 20 - 0
xsql/plans/misc_func_test.go

@@ -666,6 +666,26 @@ func TestJsonPathFunc_Apply1(t *testing.T) {
 			result: []map[string]interface{}{{
 			result: []map[string]interface{}{{
 				"a": 2.4,
 				"a": 2.4,
 			}},
 			}},
+		}, {
+			sql: "SELECT json_path_query(equipment, \"$[0][\\\"arm.left\\\"]\") AS a FROM test",
+			data: &xsql.Tuple{
+				Emitter: "test",
+				Message: xsql.Message{
+					"class": "warrior",
+					"equipment": []map[string]interface{}{
+						{
+							"rings": []float64{
+								0.1, 2.4,
+							},
+							"arm.right": "Sword of flame",
+							"arm.left":  "Shield of faith",
+						},
+					},
+				},
+			},
+			result: []map[string]interface{}{{
+				"a": "Shield of faith",
+			}},
 		},
 		},
 	}
 	}