Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions docs/path-operators.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,36 @@ The key/value pairs between the curly braces determine the groupings (by evaluat
See [Grouping and Aggregation](sorting-grouping#grouping) for more details.


## `*` (Wildcard)

This wildcard selects the values of all the properties of the context object. It can be used in a path expression in place of a property name, but it cannot be combined with other characters like a glob pattern. The order of these values in the result sequence is implementation dependent.
See [Wildcards](predicate#wildcards) for examples.

## `**` (Descendants)

This wildcard recursively selects the values of all the properties of the context object, and the properties of any objects contained within these values as it descends the hierarchy.
See [Navigate arbitrary depths](predicate#navigate-arbitrary-depths).

## `%` (Parent)

This will select the 'parent' of the current context value. Here, we define 'parent' to be the enclosing object which has the property representing the context value.

This is the only operation which searches 'backwards' in the input data structure. It is implemented by static analysis of the expression at [compile time](https://docs.jsonata.org/embedding-extending#jsonatastr) and can only be used within expressions that navigate through that target parent value in the first place.
If, for any reason, the parent location cannot be determined, then a static error (S0217) is thrown.

__Example__

```
Account.Order.Product.{
'Product': `Product Name`,
'Order': %.OrderID,
'Account': %.%.`Account Name`
}
```
This returns an array of objects for each product in each order in each account. Information from the enclosing Order and Account objects can be accessed using the parent operator.
The repeated combination of `%.%.` is used to access the grandparent and higher ancestors.


## `#` (Positional variable binding)

This can be used to determine at which position in the sequence the current context item is. It can be used following any map, filter or order-by stage in the path.
Expand Down
47 changes: 33 additions & 14 deletions src/jsonata.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ var jsonata = (function() {
case 'descendant':
result = evaluateDescendants(expr, input, environment);
break;
case 'parent':
result = environment.lookup(expr.slot.label);
break;
case 'condition':
result = yield * evaluateCondition(expr, input, environment);
break;
Expand Down Expand Up @@ -121,13 +124,13 @@ var jsonata = (function() {
result = yield result;
}

if (expr.hasOwnProperty('predicate')) {
if (Object.prototype.hasOwnProperty.call(expr, 'predicate')) {
for(var ii = 0; ii < expr.predicate.length; ii++) {
result = yield * evaluateFilter(expr.predicate[ii].expr, result, environment);
}
}

if (expr.type !== 'path' && expr.hasOwnProperty('group')) {
if (expr.type !== 'path' && Object.prototype.hasOwnProperty.call(expr, 'group')) {
result = yield * evaluateGroupExpression(expr.group, result, environment);
}

Expand All @@ -136,7 +139,7 @@ var jsonata = (function() {
exitCallback(expr, input, environment, result);
}

if(result && isSequence(result)) {
if(result && isSequence(result) && !result.tupleStream) {
if(expr.keepArray) {
result.keepSingleton = true;
}
Expand Down Expand Up @@ -204,9 +207,14 @@ var jsonata = (function() {
}

if(isTupleStream) {
resultSequence = createSequence();
for(ii = 0; ii < tupleBindings.length; ii++) {
resultSequence.push(tupleBindings[ii]['@']);
if(expr.tuple) {
// tuple stream is carrying ancestry information - keep this
resultSequence = tupleBindings;
} else {
resultSequence = createSequence();
for (ii = 0; ii < tupleBindings.length; ii++) {
resultSequence.push(tupleBindings[ii]['@']);
}
}
}

Expand Down Expand Up @@ -346,14 +354,21 @@ var jsonata = (function() {
for (var bb = 0; bb < res.length; bb++) {
tuple = {};
Object.assign(tuple, tupleBindings[ee]);
if (expr.focus) {
tuple[expr.focus] = res[bb];
tuple['@'] = input;
if(res.tupleStream) {
Object.assign(tuple, res[bb]);
} else {
tuple['@'] = res[bb];
}
if(expr.index) {
tuple[expr.index] = bb;
if (expr.focus) {
tuple[expr.focus] = res[bb];
tuple['@'] = tupleBindings[ee]['@'];
} else {
tuple['@'] = res[bb];
}
if (expr.index) {
tuple[expr.index] = bb;
}
if (expr.ancestor) {
tuple[expr.ancestor.label] = tupleBindings[ee]['@'];
}
}
result.push(tuple);
}
Expand Down Expand Up @@ -1803,7 +1818,10 @@ var jsonata = (function() {
return value;
},
timestamp: enclosingEnvironment ? enclosingEnvironment.timestamp : null,
async: enclosingEnvironment ? enclosingEnvironment.async : false
async: enclosingEnvironment ? enclosingEnvironment.async : false,
global: enclosingEnvironment ? enclosingEnvironment.global : {
ancestry: [ null ]
}
};
}

Expand Down Expand Up @@ -1909,6 +1927,7 @@ var jsonata = (function() {
"S0214": "The right side of {{token}} must be a variable name (start with $)",
"S0215": "A context variable binding must precede any predicates on a step",
"S0216": "A context variable binding must precede the 'order-by' clause on a step",
"S0217": "The object representing the 'parent' cannot be derived from this expression",
"S0301": "Empty regular expressions are not allowed",
"S0302": "No terminating / in regular expression",
"S0402": "Choice groups containing parameterized types are not supported",
Expand Down
Loading