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"
+]
```
+