Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Axis steps in lookup expressions #1094

Closed
michaelhkay opened this issue Mar 18, 2024 · 2 comments · Fixed by #1125
Closed

Axis steps in lookup expressions #1094

michaelhkay opened this issue Mar 18, 2024 · 2 comments · Fixed by #1125
Labels
Enhancement A change or improvement to an existing feature XPath An issue related to XPath

Comments

@michaelhkay
Copy link
Contributor

michaelhkay commented Mar 18, 2024

This issue picks up where issue #341, issue #350, issue #596, issue #960 etc left off - an attempt to find better syntax and semantics for navigation within JTrees (by which I mean trees of maps and arrays). The problems we are addressing are well aired in those previous issues. There are new opportunities for improving navigation within pinned trees, where upwards navigation becomes possible.

Firstly I propose that the existing constructs ?*, ?key, and ?1 be treated as abbreviations for ?content::*, ?content::key, and ?content::1 respectively. The content axis delivers a flattened sequence of items.

Then I propose we introduce an entry axis. ?entry::*, ?entry::key, and ?entry::1 deliver their results as a sequence of key value pairs, in the style of map:pairs(). Arrays for this purpose are treated as maps with integer keys. For example if $A is [(1,2), (3,4)] then $A?entry::* delivers (map{'key':1, 'value':(1,2)}, map{'key':2 'value':(3,4)}.

This applies equally to the deep lookup operator. $A??entry::* returns all the key-value pairs within the JTree rooted at $A, recursively.

We could also consider a value axis which delivers a sequence of arrays containing the values, losing the associated keys.

If values are labelled, as a result of being found by navigating a pinned JTree. then upwards navigation is also possible. For an item in a pinned tree,

  • containing-entry::* delivers the containing entry as a key-value pair. Duplicates are eliminated.

  • owner::* delivers the immediately containing map or array as identified by the label

  • ownership::* delivers the transitive closure of the owner::* axis.

  • peer::* delivers owner::*/entry::*

  • following-member::* delivers the subarray of the containing array that follows the current entry

  • preceding-member::* delivers the subarray of the containing array thay precedes the current entry

Of course, improved names for these concepts are welcomed!

In these examples I have used * to select everything on the relevant axis. This can always be replaced by a key specifier K that selects the item only if it is labelled with a key K. So for example ownership::address selects the containing maps and arrays that are themselves in a map entry with key "address".

I think we also need a convenient way to filter the selection by type (see issue #859 for a problem with the current syntax). I propose

??content::[record(longitude, latitude)]

to select all items in the recursive content that match type record(longitude, latitude)

Similarly

??entry::[array(xs:integer)+]

to select all entries where the value is an array of integers.

Finally, responding to issue #341, I propose that lookup operators should be error free: rather than reporting errors, they should return nothing.

@ChristianGruen
Copy link
Contributor

ChristianGruen commented Mar 18, 2024

Interesting indeed…

Then I propose we introduce an entry axis. ?entry::*, ?entry::key, and ?entry::1 deliver their results as a sequence of key value pairs, in the style of map:pairs(). Arrays for this purpose are treated as maps with integer keys. For example if $A is [(1,2), (3,4)] then $A?entry::* delivers (map{'key':1, 'value':(1,2)}, map{'key':2 'value':(3,4)}.

What concerns me most is terminology. We should figure out what we believe an “entry” is, and use it consistently for the same concept.

Can’t we simply return { 1: (1, 2) } and { 2: (3, 4) } for $A?entry::*? Since XQFO 3.1, we have map:entry, which returns a singleton map, and we’ve recently added map:entries, which returns a sequence of singleton maps. In addition, what about an array:entries function that returns the same result for $A := [ (1, 2), (3, 4) ] (see #826 (comment))?

Next, I think ?* should be an abbreviation for ?value::*, and we should call flattened array members “values”. This would be in line with array:values and map:values.

  • owner: I would prefer “root”
  • containing-entry: Maybe “parents” (inspired by the handling of attributes, which have a parent axis, too)
  • following-member: Maybe simply “following”, and return an empty sequence if the input is a map?

I believe we’ll make everyone’s life easier if we manage to re-use existing terms (from both node traversals and maps/arrays).

??entry::[array(xs:integer)+]

I see there’s a need for filtering the results, but I’m not so thrilled that the proposed syntax is easily misinterpreted as a predicate:

  • We should take in mind that the following syntax is legal, too: ?*[.], and we already have so many shortcuts in the map/array 3.1 syntax that hardly anyone can differentiate (A(B), A?B, A?(B), …).
  • Next, people may be tempted to attach other predicates – ??entry::[array(xs:integer)+][1] – which would probably need to be wrapped into parentheses… (??entry::[array(xs:integer)+])[1]`?

Maybe we get the more obvious choice ??entry::array() working? That’s related to a question of mine that I couldn’t answer by myself: #573 (comment)

If not, the existing syntax is not sooo bad: ??entry::*[. instance of array(xs:integer)+].

Finally, responding to issue #341, I propose that lookup operators should be error free: rather than reporting errors, they should return nothing.

If we do so, it would be consistent to be more liberal for path expressions as well. Otherwise (if I understand you correctly), 1?1 would return an empty sequence, whereas 1/1 continues to return an error.

@ChristianGruen ChristianGruen added XPath An issue related to XPath Enhancement A change or improvement to an existing feature labels Mar 19, 2024
@michaelhkay
Copy link
Contributor Author

As a first step, I propose that we expand the lookup expression syntax from

expr ('?'|'??') key-specifier

to

expr('?'|'??') (lookup-axis '::')? key-specifier

We'll initially define three axes: content, value, and pair, with the default (corresponding to current usage) being content.

We'll define pair first because it returns the most information. The pair axis returns a sequence of key-value pairs as instances of record(key, value). If the LHS expression is a map, these are key/value pairs from the map; if the LHS is an array, the keys will be integers representing positions in the array.

We then define the value axis as taking the result of the pair axis, discarding the keys, and wrapping each value as an array. So the value axis returns a sequence of arrays.

The content axis (which is the default, for compatibility) then flattens the result of the value axis to deliver a sequence of items.

I've separated the question of filtering into a separate proposal for a concise syntax for "instance of". Standard predicates can be used with any of the lookup axes to filter the results, for example

$data??pair::*[?value instance of xs:integer*]?key

@michaelhkay michaelhkay added the PR Pending A PR has been raised to resolve this issue label Mar 27, 2024
@ChristianGruen ChristianGruen removed the PR Pending A PR has been raised to resolve this issue label Oct 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Enhancement A change or improvement to an existing feature XPath An issue related to XPath
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants