From 301d7506957dab3ffbacd2c320c9556b0929bef9 Mon Sep 17 00:00:00 2001 From: Russ Michell Date: Wed, 22 Jun 2016 08:57:40 +1200 Subject: [PATCH] Updated docs. --- README.md | 29 ++++++++- code/models/fieldtypes/JSONText.php | 19 +----- docs/en/usage.md | 91 ++++++++++++++++++++++++++++- 3 files changed, 117 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 7f50ebf..54d4324 100755 --- a/README.md +++ b/README.md @@ -14,7 +14,8 @@ JSON storage and querying. ## Introduction -The module exposes a simple query API based on the [JSON operators found in Postgres v9.2+](https://www.postgresql.org/docs/9.5/static/functions-json.html), +The module exposes a fully featured JSON query API that allows developers to use either XPath-like queries using [JSONPath](http://goessner.net/articles/JsonPath/) +or a JSON-aware RDBMS-like syntax such as those found in the [JSON operators of Postgres v9.2+](https://www.postgresql.org/docs/9.5/static/functions-json.html), but with some modifications: In Postgres both the `->` and `->>` operators act as string and integer key matchers on a JSON array or object respectively. The module @@ -25,9 +26,26 @@ In Postgress the `#>` path match operator can act as an object or a text matcher the `#>` operator is *just a simple path matcher*. I see nothing but confusion arising if the same operator were to be treated differently -depending on the specific *type of JSON* stored. +depending on the specific *type of JSON* stored. I'm a reasonable man however, and am prepared for a discussion :-) -I'm a reasonable man however, and am prepared for a discussion on it, if any were to be forthcoming. +Regardless of the type of query in-use you can set what form you'd like the data returned in via the `setReturnType()` method, on a query by query basis. + +Legitimate types are: + +* JSON +* Array +* SilverStripe + +If using `SilverStripe`, the module will automatically cast the result(s) to one of SilverStripe's four native "scalar" `DBObject` subtypes: + +* `Boolean` +* `Int` +* `Float` +* `Varchar` + +If there are multiple results, the output will be an indexed array containing a single-value array for each result found. + +See [the usage docs](docs/en/usage.md) for examples of JSONPath and Postgres queries. Note: This module's query API is based on a relatively simple JSON to array conversion principle. It does *not* use Postgres' or MySQL's JSON operators at the ORM level. The aim however @@ -65,6 +83,11 @@ See: [CONTRIBUTING.md](CONTRIBUTING.md). Please include all details, no matter how small. If it were *your module*, what would you need to know from a bug/feature request? :-) +## Credits + +* [Axel Anceau](https://github.com/Peekmo/) for his packaging-up of the pretty amazing JSONPath implementation by [Stefan Goessner](https://code.google.com/archive/p/jsonpath/) +* [Stefan Goessner](https://code.google.com/archive/p/jsonpath/) for the original work on JSONPath dating back to 2005! + ## TODO * Lose the fugly way that data is queried via `$this->dbObject()` diff --git a/code/models/fieldtypes/JSONText.php b/code/models/fieldtypes/JSONText.php index ff8be6e..8eb57a1 100755 --- a/code/models/fieldtypes/JSONText.php +++ b/code/models/fieldtypes/JSONText.php @@ -3,7 +3,7 @@ /** * Simple text-based database field for storing and querying JSON structured data. * - * JSON sub-structures can be queried in a variety of ways using special operators who's syntax closely mimics those used + * JSON sub-structures can be queried in a variety of ways using special operators who's syntax closely mimics those used * in native JSON queries in PostGreSQL v9.2+. * * Note: The extraction techniques employed here are simple key / value comparisons. They do not use any native JSON @@ -357,24 +357,9 @@ public function query($operator, $operand) return $this->returnAsType([]); } - /** - * Alias of self::query(). - * - * @param string $operator - * @return mixed string|array - * @throws \JSONText\Exceptions\JSONTextException - */ - public function extract($operator) - { - return $this->query($operator); - } - /** * Based on the passed operator, ensure the correct backend matcher method is called. - * - * @param mixed $key - * @param mixed $val - * @param int $idx + * * @param array $args * @return array * @throws \JSONText\Exceptions\JSONTextException diff --git a/docs/en/usage.md b/docs/en/usage.md index c068367..16ba45f 100755 --- a/docs/en/usage.md +++ b/docs/en/usage.md @@ -1,5 +1,9 @@ # Usage +In the examples below, if you pass invalid queries or malformed JSON (where applicable) an instance of `JSONTextException` is thrown. + +## General + You can stipulate what format you want your query results back as via passing one of **json** or **array** to `setReturnType()`, thus: // JSON @@ -11,8 +15,12 @@ You can stipulate what format you want your query results back as via passing on $field = JSONText\Fields\JSONText::create('MyJSON'); $field->setValue('{"a": {"b":{"c": "foo"}}}'); $field->setReturnType('array'); - -In the examples below, if you pass invalid queries or malformed JSON (where applicable) an instnce of `JSONTextException` is thrown. + + // SilverStripe + // Will give you Varchar instances for each scalar value + $field = JSONText\Fields\JSONText::create('MyJSON'); + $field->setValue('{"a": {"b":{"c": "foo"}}}'); + $field->setReturnType('silverstripe'); class MyDataObject extends DataObject { @@ -45,7 +53,16 @@ In the examples below, if you pass invalid queries or malformed JSON (where appl { return $this->dbObject('MyJSON')->nth($n); } + } + +## Postgres Operators + class MyOtherDataObject extends DataObject + { + private static $db = [ + 'MyJSON' => 'JSONText' + ]; + /** * Returns a key=>value pair based on a strict integer -> key match. * If a string is passed, an empty array is returned. @@ -74,3 +91,73 @@ In the examples below, if you pass invalid queries or malformed JSON (where appl } } + +## JSONPath Expressions + +Not implemented yet, but syntax will be very similar to the following example: + + class MyDataObject extends DataObject + { + /* + * @var string + */ + protected $stubJSON = '{ "store": { + "book": [ + { "category": "reference", + "author": "Nigel Rees", + }, + { "category": "fiction", + "author": "Evelyn Waugh", + } + ] + }'; + + private static $db = [ + 'MyJSON' => 'JSONText' + ]; + + public function requireDefaultRecords() + { + parent::requireDefaultRecords(); + + if (!$this->MyJSON) { + $this->setValue($this->stubJSON); + } + } + + public function doStuffWithMyJSON() + { + // Query as Array + $expr = '$.store.book[*].author'; // The authors of all books in the store + $result = $this->dbObject('MyJSON')->query($expr); + $result->setReturnType('array'); + var_dump($result); // Returns ['Nigel Rees', 'Evelyn Waugh'] + + // Query as Array + $expr = '$..book[1]'; // The second book + $result = $this->dbObject('MyJSON')->query($expr); + $result->setReturnType('array'); + var_dump($this->dbObject('MyJSON')->query($expr)); // Returns ['book' => ['category' => 'reference'], ['author' => 'Nigel Rees']] + + // Query as JSON + $expr = '$..book[1]'; // The second book + $result = $this->dbObject('MyJSON')->query($expr); + $result->setReturnType('json'); + var_dump($this->dbObject('MyJSON')->query($expr)); + /* Returns: + {"book": [ + { + "category": "reference", + "author": "Nigel Rees", + }, + { + "category": "fiction", + "author": "Evelyn Waugh" + } ] } + */ + } + + } + + +