diff --git a/README.md b/README.md index c98d0d2..13b4763 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ Jetro can be used in command line using [Jetrocli](https://github.com/mitghi/jet Jetro combines access path with functions which operate on values matched within the pipeline. Access path uses `/` as separator similar to structure of URI, the start of access path should denote whether the access starts from root by using `>`, it is possible to traverse from root in nested paths by using `<`. +Jetro expressions support line breaks and whitespace, the statements can be broken up into smaller parts. + By convention, functions are denoted using `#` operator. Functions can be composed. | Function | Action | @@ -22,6 +24,8 @@ By convention, functions are denoted using `#` operator. Functions can be compos | #pick('string' \| expression, ...) [ as \| as* 'binding_value' ] | Select a key from an object, bind it to a name, select multiple sub queries to create new object | | #head | Head of the list| | #tail | Tail of the list | +| #keys | Keys associated with an object | +| #values | Values associated with an object | | #reverse | Reverse the list | | #all | Whether all boolean values are true | | #sum | Sum of numbers | @@ -62,87 +66,132 @@ Jetro consists of a parser, context wrapper which manages traversal and evaluati ```json { - "bar": { - "meows": [ - 10, - 20, - 30, - 40, - 50, - 60 - ], - "person": { - "firstname": "Mio", - "lastname": "Snuggle", - "hasFurr": true + "customer": { + "id": "xyz", + "ident": { + "user": { + "isExternal": false, + "profile": { + "firstname": "John", + "alias": "Japp", + "lastname": "Appleseed" + } + } }, - "rolls": [ + "preferences": [] + }, + "line_items": { + "items": [ { - "roll": "on side" + "ident": "abc", + "is_gratis": false, + "name": "pizza", + "price": 4.8, + "total": 1, + "type": "base_composable" }, { - "roll": "in space" + "ident": "def", + "is_gratis": false, + "name": "salami", + "price": 2.8, + "total": 10, + "type": "ingredient" }, { - "roll": "in multiverse" + "ident": "ghi", + "is_gratis": false, + "name": "cheese", + "price": 2, + "total": 1, + "type": "ingredient" }, { - "roll": "everywhere" + "ident": "uip", + "is_gratis": true, + "name": "chilli", + "price": 0, + "total": 1, + "type": "ingredient" + }, + { + "ident": "ewq", + "is_gratis": true, + "name": "bread sticks", + "price": 0, + "total": 8, + "type": "box" } ] - }, - "foo": [ - 1, - 2, - 3, - 4, - 5, - { - "contract": { - "kind": "Furry Purr", - "hasFurr": false - } - } - ], - "friend": "Thunder Pur" + } } ``` ### Queries -Get value associated with `bar`. +Get value associated with `line_items`. ``` ->/bar +>/line_items ```
See output ### result ```json - "bar": { - "meows": [ - 10, - 20, - 30, - 40, - 50, - 60 - ], - "person": { - "firstname": "Mio", - "lastname": "Snuggle" +{ + "items": [ + { + "ident": "abc", + "is_gratis": false, + "name": "pizza", + "price": 4.8, + "total": 1, + "type": "base_composable" + }, + { + "ident": "def", + "is_gratis": false, + "name": "salami", + "price": 2.8, + "total": 10, + "type": "ingredient" + }, + { + "ident": "ghi", + "is_gratis": false, + "name": "cheese", + "price": 2, + "total": 1, + "type": "ingredient" + }, + { + "ident": "uip", + "is_gratis": true, + "name": "chilli", + "price": 0, + "total": 1, + "type": "ingridient" + }, + { + "ident": "ewq", + "is_gratis": true, + "name": "bread sticks", + "price": 0, + "total": 8, + "type": "box" } - } + ] +} ```
--- -Get value associated with `bar`, return first value associated with any existing key. +Get value associated with first matching key which has a value and return its `id` field. ``` ->/bar/('person' | 'whatever') +>/('non-existing-member' | 'customer')/id ```
@@ -151,18 +200,13 @@ Get value associated with `bar`, return first value associated with any existing ### result ```json -{ - "firstname": "Mio", - "hasFurr": true, - "lastname": "Snuggle" -} +"xyz" ```
--- - ``` ->/('foo' | 'bar' | 'non-exinsting-key') +>/..items/#tail ```
@@ -172,15 +216,37 @@ Get value associated with `bar`, return first value associated with any existing ```json [ - 1, - 2, - 3, - 4, - 5, { - "contract": { - "kind": "Furry Purr" - } + "ident": "def", + "is_gratis": false, + "name": "salami", + "price": 2.8, + "total": 10, + "type": "ingredient" + }, + { + "ident": "ghi", + "is_gratis": false, + "name": "cheese", + "price": 2, + "total": 1, + "type": "ingredient" + }, + { + "ident": "uip", + "is_gratis": true, + "name": "chilli", + "price": 0, + "total": 1, + "type": "ingridient" + }, + { + "ident": "ewq", + "is_gratis": true, + "name": "bread sticks", + "price": 0, + "total": 8, + "type": "box" } ] ``` @@ -189,23 +255,7 @@ Get value associated with `bar`, return first value associated with any existing --- ``` ->/('foo' | 'bar' | 'non-exinsting-key')/[:5]/#sum -``` - -
- See output - - ### result - -```json -15 -``` -
- ---- - -``` ->/..rolls/#filter('roll' == 'everywhere') +>/..items/#filter('is_gratis' == true and 'name' ~= 'ChILLi') ```
@@ -216,7 +266,12 @@ Get value associated with `bar`, return first value associated with any existing ```json [ { - "roll": "everywhere" + "ident": "uip", + "is_gratis": true, + "name": "chilli", + "price": 0, + "total": 1, + "type": "ingridient" } ] ``` @@ -225,7 +280,7 @@ Get value associated with `bar`, return first value associated with any existing --- ``` ->/..rolls/#filter('priority' < 11 and 'roll' ~= 'ON Side') +>/..items/#filter('is_gratis' == true and 'name' ~= 'ChILLi')/#map(x: x.type) ```
@@ -235,18 +290,27 @@ Get value associated with `bar`, return first value associated with any existing ```json [ - { - "priority": 1, - "roll": "on side" - } + "ingridient" ] ```
--- +Create a new object with scheme `{'total': ..., 'fullname': ...}` as follow: +- recursively search for `line_items`, dive into any matched object, filter matches with `is_gratis == false` statement, recursively look for their prices and return the sum of prices +- recursively search for object `user`, select its `profile` and create a new object with schema `{'fullname': ...}` formated by concatenating values of keys ('firstname', 'lastname') + ``` ->/..rolls/#head/#formats('Rolling {}', 'roll') -> 'msg' +>/#pick( + >/..line_items + /* + /#filter('is_gratis' == false)/..price/#sum as 'total', + + >/..user + /profile + /#formats('{} {}', 'firstname', 'lastname') ->* 'fullname' +) ```
@@ -256,17 +320,18 @@ Get value associated with `bar`, return first value associated with any existing ```json { - "msg": "Rolling on side", - "priority": 1, - "roll": "on side" + "fullname": "John Appleseed", + "total": 9.6 } ```
--- +Select up to 4 items from index zero of array `items` + ``` ->/..foo/[:4] +>/..items/[:4] ```
@@ -276,18 +341,48 @@ Get value associated with `bar`, return first value associated with any existing ```json [ - 1, - 2, - 3, - 4 + { + "ident": "abc", + "is_gratis": false, + "name": "pizza", + "price": 4.8, + "total": 1, + "type": "base_composable" + }, + { + "ident": "def", + "is_gratis": false, + "name": "salami", + "price": 2.8, + "total": 10, + "type": "ingredient" + }, + { + "ident": "ghi", + "is_gratis": false, + "name": "cheese", + "price": 2, + "total": 1, + "type": "ingredient" + }, + { + "ident": "uip", + "is_gratis": true, + "name": "chilli", + "price": 0, + "total": 1, + "type": "ingridient" + } ] ```
--- +Select from 4th index and consume until end of array `items` + ``` ->/..meows/[4:] +>/..items/[4:] ```
@@ -297,16 +392,25 @@ Get value associated with `bar`, return first value associated with any existing ```json [ - 50, - 60 + { + "ident": "ewq", + "is_gratis": true, + "name": "bread sticks", + "price": 0, + "total": 8, + "type": "box" + } ] ```
--- +Create a new object with schema `{'total_gratis': ...}` as follow: +- Recursively look for any object containing `items`, and then recursively search within the matched object for `is_gratis` and length of matched values + ``` ->/#pick(>/..hasFurr/#all as 'totallyFurry') +>/#pick(>/..items/..is_gratis/#len as 'total_gratis') ```
@@ -316,17 +420,17 @@ Get value associated with `bar`, return first value associated with any existing ```json { - "totallyFurry": true + "total_gratis": 2 } ```
--- ---- +Recursively search for object `items`, select its first item and return its keys ``` ->/#pick('foo', >/..person/#formats('Herrn {} {}', 'firstname', 'lastname') ->* 'fullname') +>/..items/[0]/#keys ```
@@ -335,48 +439,23 @@ Get value associated with `bar`, return first value associated with any existing ### result ```json -{ - "foo": [ - 1, - 2, - 3, - 4, - 5, - { - "contract": { - "kind": "Furry Purr" - } - } - ], - "fullname": "Herrn Mio Snuggle" -} +[ + "ident", + "is_gratis", + "name", + "price", + "total", + "type" +] ```
--- -``` ->/..foo/..contract/#pick('kind' as 'contract', * 'welcome_message') -``` - -
- See output - - ### result - -```json -{ - "contract": "Furry Purr", - "welcome_message": "Welcome Mio" -} +Recursively search for object `items`, select its first item and return its values ``` -
- ---- - -``` ->/..values/#map(x: x.name)/#head +>/..items/[0]/#values ```
@@ -385,6 +464,14 @@ Get value associated with `bar`, return first value associated with any existing ### result ```json -"gearbox" +[ + "abc", + false, + "pizza", + 4.8, + 1, + "base_composable" +] ```
+