(Boy, do I ever wish that was the case.)
|Strange AND wonderful! |
So, without further ado, let's begin.
|Yes, quite. |
[Courtesy: samuelrunge.com and Monty Python)
Retrieving Heterogeneous Paths
|The domain model in question.|
Another, more simple, task is to be able to see how two entities are related, i.e. "how do I get from point A to point B?". As an example, one such question might be, "How is game A related to game B, even though the difference in publication dates is large?" And yes, this type of question is extremely similar to the "Six Degrees of Separation" question.
|I bet he's a gamer.|
Some Basic CodeYou might also recall that I had implemented a GameRepository class with the following signature:
public interface GameRepository extends GraphRepository<Game>, RelationshipOperationsRepository<Game>
With the above repository extending the GraphRepository and RelationshipOperationsRepository interfaces, we are provided with a host of cool (and handy) methods out of the box (tip: make sure you're comfortable with the "convention over configuration" paradigm, as there is a bit of magic that goes on with those OOTB methods).
As you'd expect, we can also add additional methods to the interface. One example of a method you might want to add could be a custom Cypher query that returns all Game nodes with a particular property (the implementation of this is outside the scope of this post, but, it's actually pretty simple; if people want to see it, just shoot me a note!).
However, today we're looking to address the "Six Degrees of Separation" question (minus the limit on the degrees of separation), i.e. "how are node A and node B related"?
So let's give this a (very simple) shot:
@Query("START n=node(1), x=node(97) MATCH p = shortestPath( n-[*]-x ) RETURN p") Iterable<EntityPath<Game, Game> > getPath();
Given that the nodes with IDs 1 and 97 are "Game" nodes, the Cypher query above is essentially determining how the two nodes are related.
(For the sake of this post, I'm ignoring the fact that there could be multiple "shortest paths" between the two nodes as it has little bearing on the goal of this post.)
Quickly going over the return type, SDN allows us to return back an EntityPath given a starting node type and an ending node type which, in this case, is the Game type. An EntityPath is capable of storing nodes and their relationships as part of a Cypher path. The Iterable portion of the return type is necessary unless you want to use EndResult instead of Iterable.
We can then access the individual path(s) via the Iterable return type.
(NOTE: There is currently a bug with SDN that throws an exception when calling a single path's .nodes() or .nodeEntities(). This bug has been around since SDN 2.1.RC4.)
Traversing the Returned Path
// n = Node object in question String theType = (String)n.getProperty("__type__", ""); // we could then make the RHS of the assignment something like: (LHS) = template.convert(n, class<theType>;
But what about the LHS? Without an "if/else" block, how can we treat the returned string theType as a first-class citizen that would declare the type for the LHS? As far as I'm aware, there is no way to do this (of course there might be ways I've just not seen, but, I have a feeling they'd be just as expensive as the rest of the assignment). Java is a strongly-typed language, and so I'm sure most of us would expect this outcome.
So we see that this "solution" isn't really much of one.