Pārlūkot izejas kodu

feat(func): support reserved words in json path

ngjaying 4 gadi atpakaļ
vecāks
revīzija
5c6afa2f4c
3 mainītis faili ar 42 papildinājumiem un 5 dzēšanām
  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:
 
 - 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.
 
 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` 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][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.
 
 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"]
 ```
 
+- 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*
 
 <!--Do we need to support this?-->

+ 13 - 4
xsql/lexical.go

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