accept path expressions such as SELECT column lists or WHERE, HAVING, ORDER BY , or GROUP BY clauses.
Now suppose you wanted to access an element by key. You can do that too. In this case, we use the dollar sign followed by a period then the key name. The following shows how to retrieve the last name for a JSON object containing the name and address of an individual.
MySQL localhost:33060+ ssl SQL > SELECT JSON_EXTRACT('{"name":
{"first":"Billy-bob","last":"Throckmutton"},"address": {"street":"4 Main Street","city":"Melborne","state":"California","zip":"90125"}}', '$.name.
first') AS Name;
+---+
| Name | +---+
| "Billy-bob" | +---+
1 row in set (0.00 sec)
88
Note that I had to use two levels of access. That is, I wanted the value for the key named first from the object named name. Hence, I used '$.name.first'. This demonstrates how to use path expressions to drill down into the JSON document. This also is why we call this a path expression because the way we form the expression gives us the “path” to the element.
Now that we’ve seen a few examples, let’s review the entire syntax for path
expressions; both for use in SQL and the NoSQL interfaces. Unless otherwise stated, the syntax aspects apply to both interfaces.
Once again, a path expression starts with the dollar sign and can optionally be followed by several forms of syntax called selectors that allow us to request a part of the document. These selectors include the following:
• A period followed by the name of a key name references the value for that key. The key name must be specified within double quotation marks if the name without quotes is not valid (it requires quotes to be a valid identifier such as a key name with a space).
• Use square brackets with an integer index ([n]) to select an element in an array. Indexes start at 0.
• Paths can contain the wildcards * or ** as follows.
• .[*] evaluates to the values of all members in a JSON object.
• [*] evaluates to the values of all elements in a JSON array.
• A sequence such as prefix**suffix evaluates to all paths that begin with the named prefix and end with the named suffix.
• Paths can be nested using a period as the separator. In this case, the path after the period is evaluated within the context of the parent path context. For example, $.name.first limits the search for a key named first to the name JSON object.
If a path expression is evaluated as false or fails to locate a data item, the server will return null. For example, the following returns null because there are only 6 items in the array. Can you see why? Remember, counting starts at 0. This is a common mistake for those new to using path expressions (or arrays in programming languages).
Chapter 3 JSON DOCumeNtS
MySQL localhost:33060+ ssl SQL > SELECT JSON_EXTRACT('[1,2,3,4,5,6]', '$[6]');
+---+
| JSON_EXTRACT('[1,2,3,4,5,6]', '$[6]') | +---+
| NULL | +---+
1 row in set (0.00 sec)
But wait, there’s one more nifty option for path expressions. We can use a shortcut!
That is, the dash and greater than symbol (->) can be used in place of the JSON_
EXTRACT() function when accessing data in SQL statements by column. How cool is that?
The use of the -> operation is sometimes called an inline path expression. For example, we could have written the example above to find the third item in a JSON array from a table as follows.
MySQL localhost:33060+ ssl SQL > USE test;
Query OK, 0 rows affected (0.00 sec)
MySQL localhost:33060+ ssl SQL > CREATE TABLE ex1 (id int AUTO_INCREMENT PRIMARY KEY, recorded_data JSON);
Query OK, 0 rows affected (0.00 sec)
MySQL localhost:33060+ ssl SQL > INSERT INTO test.ex1 VALUES (NULL, JSON_ARRAY(1,2,3,4,5,6));
Query OK, 1 row affected (0.00 sec)
MySQL localhost:33060+ ssl SQL > INSERT INTO test.ex1 VALUES (NULL, JSON_ARRAY(7,8,9));
Query OK, 1 row affected (0.00 sec)
MySQL localhost:33060+ ssl SQL > SELECT * FROM test.ex1 WHERE recorded_
data->'$[2]' = 3;
+----+---+
| id | recorded_data | +----+---+
| 1 | [1, 2, 3, 4, 5, 6] | +----+---+
1 row in set (0.00 sec)
90
Note that I simply used the column name, recorded_data, and appended the -> to the end then listed the path expression. Brilliant!
There is one other form of this shortcut. If the result of the -> operation (JSON_
EXTRACT) evaluates to a quoted string, we can use the ->> symbol (called the inline path operator) to retrieve the value without quotes. This is helpful when dealing with values that are numbers. The following shows two examples. One example is with the ->
operation and the same with the ->> operation.
MySQL localhost:33060+ ssl SQL > INSERT INTO test.ex1 VALUES (NULL, '{"name":"will","age":"43"}');
Query OK, 1 row affected (0.00 sec)
MySQL localhost:33060+ ssl SQL > INSERT INTO test.ex1 VALUES (NULL, '{"name":"joseph","age":"11"}');
Query OK, 1 row affected (0.00 sec)
MySQL localhost:33060+ ssl SQL > SELECT * FROM test.ex1 WHERE recorded_
data->>'$.age' = 43;
+----+---+
| id | recorded_data | +----+---+
| 3 | {"age": "43", "name": "will"} | +----+---+
1 row in set (0.00 sec)
MySQL localhost:33060+ ssl SQL > SELECT * FROM test.ex1 WHERE recorded_
data->'$.age' = 43;
Empty set (0.00 sec)
Note that the recorded_data values (age and name) were stored as a string. But what if the data were stored as an integer? Observe.
MySQL localhost:33060+ ssl SQL > INSERT INTO test.ex1 VALUES (NULL, '{"name":"amy","age":22}');
Query OK, 1 row affected (0.00 sec)
MySQL localhost:33060+ ssl SQL > SELECT * FROM test.ex1 WHERE recorded_
data->'$.age' = 22;
Chapter 3 JSON DOCumeNtS
+----+---+
| id | recorded_data | +----+---+
| 5 | {"age": 22, "name": "amy"} | +----+---+
1 row in set (0.00 sec)
MySQL localhost:33060+ ssl SQL > SELECT * FROM test.ex1 WHERE recorded_
data->>'$.age' = 22;
+----+---+
| id | recorded_data | +----+---+
| 5 | {"age": 22, "name": "amy"} | +----+---+
1 row in set (0.00 sec)
Aha! So, the ->> operation is most useful when values must be unquoted. If they were already unquoted (such as an integer), the ->> operation returns the same as the ->
operation.
Now, let’s see a few more examples of path expressions. Listing 3-2 shows several examples without explanation. Take a few minutes to look through these and examine the data it is operating on so you can see how each works. With a little imagination, you can drill down to a single data element!
Listing 3-2. Examples of Path Expressions
MySQL localhost:33060+ ssl SQL > INSERT INTO test.ex1 VALUES (NULL, '{"name": {"last": "Throckmutton", "first": "Billy-bob"}, "address":
{"zip": "90125", "city": "Melborne", "state": "California", "street":
"4 Main Street"}}');
Query OK, 1 row affected (0.00 sec)
MySQL localhost:33060+ ssl SQL > SELECT recorded_data FROM test.ex1 WHERE recorded_data->'$.name' IS NOT NULL \G
*************************** 1. row ***************************
recorded_data: {"age": "43", "name": "will"}
*************************** 2. row ***************************
recorded_data: {"age": "11", "name": "joseph"}
92
*************************** 3. row ***************************
recorded_data: {"age": 22, "name": "amy"}
*************************** 4. row ***************************
recorded_data: {"name": {"last": "Throckmutton", "first": "Billy-bob"},
"address": {"zip": "90125", "city": "Melborne", "state": "California",
"street": "4 Main Street"}}
4 rows in set (0.00 sec)
MySQL localhost:33060+ ssl SQL > SELECT recorded_data->'$.name' FROM test.ex1 WHERE recorded_data->'$.name' IS NOT NULL;
+---+
| recorded_data->'$.name' | +---+
| "will" |
| "joseph" |
| "amy" |
| {"last": "Throckmutton", "first": "Billy-bob"} | +---+
4 rows in set (0.00 sec)
MySQL localhost:33060+ ssl SQL > SELECT recorded_data->'$.name.first' as first, recorded_data->'$.name.last' as last FROM test.ex1 WHERE recorded_
data->'$.name.first' IS NOT NULL;
+---+---+
| first | last | +---+---+
| "Billy-bob" | "Throckmutton" | +---+---+
1 row in set (0.00 sec)
MySQL localhost:33060+ ssl SQL > INSERT INTO test.ex1 VALUES (NULL, '{"phones": [{"work": "555-1212"}, {"cell": "555-2121"}]}');
Query OK, 1 row affected (0.00 sec)
MySQL localhost:33060+ ssl SQL > SELECT recorded_data->>'$.phones' FROM test.ex1 WHERE recorded_data->>'$.phones' IS NOT NULL;
+---+
| recorded_data->>'$.phones' | +---+
Chapter 3 JSON DOCumeNtS
| [{"work": "555-1212"}, {"cell": "555-2121"}] | +---+
1 row in set (0.00 sec)
MySQL localhost:33060+ ssl SQL > SELECT recorded_data->'$.phones[1]' FROM test.ex1 WHERE recorded_data->>'$.phones' IS NOT NULL;
+---+
| recorded_data->'$.phones[1]' | +---+
| {"cell": "555-2121"} | +---+
1 row in set (0.00 sec)
MySQL localhost:33060+ ssl SQL > SELECT recorded_data->'$.phones[1].
cell' FROM test.ex1 WHERE recorded_data->>'$.phones' IS NOT NULL;
+---+
| recorded_data->'$.phones[1].cell' | +---+
| "555-2121" | +---+
1 row in set (0.00 sec)
Note that I use the path expression in the WHERE clause checking to see if the result is not NULL. This is a good trick on selecting rows in a table that have the elements you’re looking for in the document. That is, you want only the rows that contain a specific data element (via the path expression).
However, the use of the shortcuts (inline path expressions) is not a direct replacement for the JSON_EXTRACT() function. The following summarizes the limitations.
• Data source: When used in a SQL statement, the inline path
expression uses the field (column) specified only. The function can use any JSON typed value.
• Path expression string: An inline path expression must use a plain string; the function can use any string typed value.
• Number of expressions: An inline path expression can use only one path expression against a single field (column). The function can use multiple path expressions against a JSON document.
94