Skip to content


Subversion checkout URL

You can clone with
Download ZIP


Support parent/sibling selection in the query language #1

adamschmideg opened this Issue · 4 comments

2 participants


First of all: great work! I like the light-weight database approach and the comprehensive documentation.

I understand it's possible to get the parents of a node:

  child ='/user/handle');
  child.parent(); // -> gives the user node

My feature request is if it's possible to extend the query language so I can write'..') to get the same result.

I also wonder how I could get the previous or next sibling of a node in an array.


Hey! Very pleasantly surprising to get a feature request before I properly announce the library (I'm working on a decent REPL and API docs before publishing fully).

I actually agree that this would be a neat feature, but it requires some fairly tough changes to the descent operation performed during query operation - while SpahQL currently uses the actual JS call stack for descent, instead we'd have to pass about an array stack which may be popped when encountering a parent operator (".."). I wonder if "+1" would be a valid next-sibling operator as well - what do you think?

// Parent selector"//status/..") //-> select all parent objects of a "status" key
// Sibling selector"/timeline/first/+1") //-> equivalent to "/timeline/1""/timeline/last/-1") //-> select penultimate status

Actually, let's revise the sibling selectors. Applying a path component semantic for elements at the same depth seems wildly inconsistent. Here's some possibilities for you to comment on:"/timeline/first|sibling(+1)") // general pattern of KEY . PIPE . TRAVERSAL_FUNCTION . BRACKET_LEFT . ARGS . BRACKET_RIGHT . FILTER_QUERIES"/timeline/first+1") // potentially dangerous, as "+1" and "-1" are also valid string object keys

Sorry about my late response. I checked the in-browser REPL which is nice to play with, I also had a look at the source.

I think your PIPE syntax would be great, and it wouldn't break the current operation. Here is my slightly different syntax"/timeline/first/|next/name") // the next function takes no argument"/timeline/last/|sibling(-1)") // sibling takes an argument

It could be implemented in a general (an unsafe) way:"/foo/|traversal/bar") would be equivalent with"/foo").traversal().select("/bar"). This would make extension quite easy, the user just has to add their traversal function to the SpahQL prototype. So"/foo/bar").select("|parent") would work out of the box.


I think including the slash for arbitrary traversal functions isn't quite right - let's assume for a moment that the slash doesn't exist in the language. The query:


Could be seen as a macro for "descend" and "descendRecursive" traversals:


However we allow / and // as aliases for baked in descent functions - meaning the slashes have an inherent meaning relating to descent. A pipe is traversal-agnostic and essentially boils down to function-currying:

descend('a')|descend('b')|parent //-> pass output of each function into the next and return the final value
/a/b|parent //-> Truncated version of above

Bear in mind that pipes are not parts of a path component but would actually terminate parsing of the current query and tell the query parser to expect a new path query or traversal function, which will receive the result array from the previous token as input data. Here's an example with filters:

//*[/.type == 'array'] | firstChild //-> Find all arrays and return the first child
//*[/.type == 'array']/0 //-> Equivalent in current syntax

I'm still in two minds about whether or not to actually implement this, but I'm fairly certain the currying pattern is what you're after - which would mean no slashes before pipes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.