3

We are working with a hierarchy tree structure where a parent has zero or more children, and a child has either one or zero parents. When we query for a list of direct children for a given parent the query returns the children in random order. We need the children to return in the order we define when we create or update the children.

I have added a relationship between children -[:Sibling]-> so the 'top' sibling has only an incoming :Sibling relationship, and the 'bottom' sibling has only an outgoing relationship.

Given this, is there a Cypher query to return the children in sibling order?

I have a query that returns each child, and its sibling, but now I have to write some code to return the list in the correct order.

An alternative approach might be to add a sort number to each child node. This would need to be updated for all the children if one of them changes order. This approach seems slightly foreign to the graph database concept.

If this problem has been encountered before, is there a standard algorithm for solving it programatically?


Update1

sample data as requested by Bruno

(parent1) (child1)-[:ChildOf]->(parent1) (child2)-[:ChildOf]->(parent1) (child2)-[:Sibling]->(child1) (child3)-[:ChildOf]->(parent1) (child3)-[:Sibling]->(child2)

is there a cypher query to return child1, child2, child3 in that order?

if not, then the ordering can be done programatically

using properties instead of relationships (parent1) (child1)-[:ChildOf]->(parent1) (child1:{order:1}) (child2)-[:ChildOf]->(parent1) (child2:{order:2}) (child3)-[:ChildOf]->(parent1) (child3:{order:3})

`match (c)-[:ChildOf]->(parent1) return c ordered by c:order`

I do not expect that there is a cypher query that can update the order of children.


Update2

I have now arrived at the following query which returns children in the right order

`match (firstChild)-[:FirstChildOf]->(parent) match (sibling)-[:Sibling*]->(firstChild) return firstChild,sibling`

This query depends on adding a -[:FirstChildOf]->(parent) relationship.

If I don't hear otherwise I'll set this to the answer.

Shall I assume there is no cypher query for inserting a node into an ordered list?

Martin Flower
  • 353
  • 4
  • 14
  • Hello Martin! I believe that is a good idea put in your question a sample data set in and the expected results. Thanks! – Bruno Peres Aug 17 '17 at 13:39

3 Answers3

2

When you create your graph model you should concentrate on the questions you want to answered with your data model. If you want to get child of a parent ordered by a "create or update" property then you should store it since the relationship in general does not represent an order. It is not foreign to the graph database concept, since the graph databases use properties. It is not always easy task to decide to store something as a relationship or a property. It is all about the proper modelling. If you have the concept of '[:NEXT_SIBLING]' or similar, then it will be a pain in the back, when you have to remove a node. So I should use it when this state is constant. For example in a timetree, where the days are after each other and it does not change.

In general, if the creation order should be used, I should use timestamps like this:

    create (n:Node {created:timestamp()});

Then you can use the timestamp to order.

    match (p:Parent)-[:HAS_CHILDREN]->(n:Child) where p.name='xy' return n order by n.created;

And you can use timestamps for relationships too. Similar question is here: Modeling an ordered tree with neo4j

szenyo
  • 522
  • 2
  • 9
  • The initial order of the children is defined when they are created - appendSibling() adds the new child to be after the last child, insertSibling() inserts in front of another child. The order can then be changed at a later date - move childXXX to be infront of childYYY. I was guessing that a graph database can behave like a linked list. It may well be that my guess is wrong. – Martin Flower Aug 18 '17 at 06:28
  • It can behave like that definitely, since they are nodes and relationships. The link above contains a time tree solution, which has the similar approach you mentioned. – szenyo Aug 18 '17 at 06:35
  • I have found a query based on the input from the linked question, and I have updated my question. – Martin Flower Aug 18 '17 at 08:46
2

Here are some tips I use for dealing with ordered lists of nodes in Neo4J.

  • Reverse the relation direction to (child1)<-[:HasChild]-(parent1). (mostly just logical reinforcement of the next items, since a "parent has list of children")

  • Add the property index to HasChild. This will let you do WITH child, hasChild.index as sid ORDER BY sid ASC for sibling order. (This is how I maintain arbitrary order information on lists of children) This is on the relationship because it assumes that a node can be part of more than one ordered list.

  • You can use SIZE(shortestpath((root)-[*]->(child1)) as depth to then order them by depth from a root.

  • Since this is for arbitrary order, you must update all the indexes to update the order (You can do something like WITH COLLECT(child) as children, FILTER(c IN COLLECT(child) WHERE c.index >=3) as subset FOREACH (c IN subset| SET c.index+=1) for a basic insert, otherwise you will have to just rewrite them all to arbitrarily change the order.

  • If you don't actually care about the order, just that it is consistent, you can use WITH child, ID(child) as sid ORDER BY sid ASC. This essentially is "Order by node age"

  • One other option is to use a meta relationship. So :HasChild would act as a list of nodes, and then something like :NextMember would tell you what the next item from this one is. This is more flexible, but in my opinion harder to work with (You need to case for doesn't have next, to get the right order you have to do a 'trace' on the nodes, doesn't work if you want to add this node to another ordered list later, ect.)

Of course, if the order isn't arbitrary (based on name or age or something), than it is much better to just sort on the non-arbitrary logic.

Tezra
  • 8,463
  • 3
  • 31
  • 68
  • At the moment I think we will continue with the -[:Sibling]-> relationship as this is easier to change when the order changes. But we may move to storing the order on the node or on the relationship. – Martin Flower Aug 21 '17 at 16:46
  • Whether we should have `(parent)<-[:isChildOf]-(child)` or `(parent)-[:HasChild]->(child)`? At the moment we have the former - a child must have exactly one parent (apart from at the root). The parent can continue to exist even if it has no children. This made me think that the relationship is more strongly attached to the child. In neo4j, does query performance depend on relationship direction? – Martin Flower Aug 21 '17 at 16:50
  • @MartinFlower Cypher only cares about direction as much as you do (just validates it as needed). I'd call the direction thing more of a problem for humans. You have a tree structure, so to expand from the roots, `(parent)-[*]->(child)` is more intuitive for getting all children than `(parent)<-[*]-(child)`. (It sort of implies a one-to-many relationship in that direction) Most of the time, it pays off to make your model more readable for the human than the computer, of course, this all depends on what you are doing with the data. So this is really just a preference consideration. – Tezra Aug 22 '17 at 12:30
  • Just use whatever direction makes the most logical sense for you and your team, and is least likely to cause confusion. Just make sure that your logic for it is consistent. – Tezra Aug 22 '17 at 12:32
0

The query for returning the children in the right order is

match (firstChild)-[:FirstChildOf]->(parent) match (sibling)-[:Sibling*]->(firstChild) return firstChild,sibling
Martin Flower
  • 353
  • 4
  • 14