Today I pushed a major rewrite of the Enhanced API. See: https://github.com/peterneubauer/graph-collections/tree/master/src/main/java/org/neo4j/collections/graphdb
Originally the Enhanced API was a drop-in replacement of the standard Neo4j API. This resulted in lots of wrapper classes that needed to be maintained. The rewrite of Enhanced API is no longer a drop-in replacement and contains no interface/class names that can be found in the standard API. Enhanced API no longer speaks of Nodes but of Vertices and doesn't speak of Relationships but of Edges. This helps to prevent name clashes at the expense of somewhat less recognizable names (Relationship is after all a more common word than Edge). This rewrite is not merely a renaming of classes and interfaces, but is in most part a complete rewrite and also a rethinking of the API on my part. Enhanced API consists of two basic elements: Vertex and EdgeRole. Most elements are a subclass of Vertex, though there are some specialized versions of EdgeRole. Let me start with an example: Suppose we have two vertices denoting the persons Tom and Paula, and we want to state that Tom is the father of Paula. For standard Neo4j we tend to write such a fact as: Tom --Father--> Paula For Enhanced API we can conceptually write this fact as follows: --StartRole--Tom Father --EndRole--Paula This should be read as follows: We have two Vertices: Tom and Paula and we have a BinaryEdge (similar to a Relationship in the standard API) of type "Father", where Tom has the StartRole for that edge and Paula has the EndRole for that edge. So instead of a directed graph, we conceptually have an undirected bipartite graph. For binary edges (edges between two vertices), this is mostly conceptually the case, because the API will simply allow you to write: tom.createEdgeTo(paula, FATHER) (similar to tom.createRelationshipTo(paula, FATHER) as we would have in the standard API). It is also possible to fetch the start vertex of the binary relationship with the method: edge.getStartVertex() (similar to relationship.getStartNode()), although it is also possible to treat the binary edge as a generic edge and fetch that Vertex as: edge.getElement(db.getStartRole()). BinaryEdges, are a special case and have special methods which cover the same functionality as can be found in the standard Neo4j API. In general, we can say that Vertices are connected to Edges by means of EdgeRoles. In the binary case there are two predefined EdgeRoles: StartRole and EndRole. Before we get deeper into the general case of n-ary edges, let's first look at another special case: Properties. Properties can be thought of as unary edges, an edge that connects to only one Vertex (as opposed to two in the binary case). Suppose we want to state that Tom is 49 years old, we can write that as: age(49)--PropertyRole--Tom We have an edge of type "age" that is connected to the vertex Tom in the role of a property. Again this is mostly conceptually true, because there are lots of methods in Enhanced API that are very similar to the ones found in the standard API; getProperty, hasProperty, setProperty. Instead, we can also call methods on the property itself, after all the age property connected to the Vertex "Tom", is an object all of itself. More precisely it is a Property and with that it is a UnaryEdge, which is an Edge, which is a Vertex. >From the age property we can fetch the ProperyType, but we can also ask for >the Vertex it is connected to: getVertex(). Since a Property is an Edge we can >also fetch the connected vertex (Tom) as follows: >age.getElement(db.getPropertyRole). So we have seen the two special cases: unary edges and binary edges, which work very much the same as properties and Relationships in the standard Neo4j API, though we have given it a conceptually different perspective that unifies the two and fits it neatly into the general case of N-ary edges. As said before, an Edge is a Vertex that connects other Vertices by means of EdgeRoles. Since Edges are Vertices, they can have other Edges connected to them. Or in standard API talk: relationships can be connected to other relationships and they can have properties. The concept of EdgeRoles separates Edges from Vertices, so we will effectively have a bipartite graph where Vertices can only connect to Edges and Edges can only connect to Vertices. Given the fact that Edges are also Vertices, Edges can be connected to Edges, but in such a case it is unambiguous which plays the role of Edge and which plays the role of Vertex in that connection. Let's look at an example of an N-ary edge: Suppose we want to state the fact that Tom gives Paula a Bicycle (no golden helicopters in stock today). We can write that as follows: --Giver--Tom GIVES --Recipient -- Paula --Gift -- Bicycle There is an EdgeType GIVES which defines three EdgeRoles: Giver, Recipient and Gift, which connect Tom, Paula and Bicycle to the Edge. The edge is created by first creating three EdgeElement objects that each contain a Role and the connected Vertex. We can then make the call db.createEdge(GIVES, edgeElements). An EdgeElement is that what is connected to Edge for a particular EdgeRole (including that EdgeRole itself). An EdgeElement can contain more than one connected Vertex. We can for example state: Tom and Dick give Paula a Bicycle. In Enhanced API notation: --Giver--Tom, Dick GIVES --Recipient -- Paula --Gift -- Bicycle Or we may want to state: Tom, Dick and Harry give Paula and Josephine a Bicycle and an Icecream. In Enhanced API notation: --Giver--Tom, Dick, Harry GIVES --Recipient -- Paula, Josephine --Gift -- Bicycle, Icecream The API allow the user to fetch an EdgeElement by means of an EdgeRole and iterate over the connected Vertices: for(EdgeElement givers: gives.getElements(Giver)){ for(Vertex giver: givers.getVertices){ //do something with the giver Vertex } } For those cases where an EdgeElement can contain only one Vertex, there is a FunctionalEdgeElement, which can only be used in conjunction with FunctionalEdgeRoles. StartRole, EndRole and PropertyRole are all FunctionalEdgeRoles, since we can have only one start Vertex and one end Vertex per BinaryEdge (just like there can only be one StartNode and one EndNode for a Relationship in the standard API) and we can only have one Vertex associated with a Property (just like a property can not belong to two different Nodes in the standard Neo4j API) . The Enhanced API can be used in conjunction with standard Neo4j API. The only replacement needed is that of the database instance. The Enhanced API defines a DatabaseService interface, which extends the standard GraphDatabaseService interface and adds several enhanced methods for the creation and lookup of Vertices, Edges and several kinds of VertexTypes. Now the big question is of course, what do we gain with this entire apparatus? First of all, we have unification of the storage elements of Neo4j. Everything that can be stored in Neo4j is a Vertex: Node is very much like a Vertex (with a slightly different interface that has similar features to the standard Neo4j API, and more...) Relationship is very much like BinaryEdge, which is an Edge, which is a Vertex RelationshipType is covered by BinaryEdgeType which is an EdgeType, which is a VertexType, which is a Vertex property name is wrapped as a PropertyType which is an an EdgeType, which is a VertexType, which is a Vertex. propery value is wrapped as a Property which is a UnaryEdge, which is an Edge, which is a Vertex Having this unification, it is possible to write traversals to every part of the Neo4j database. And that is the big boon of this unification. Every part of the database can be accessed with a traveral description. The standard Neo4j API only allows traversals to return Nodes given a start Node. The Enhanced API allows traversals from any part of the graph, whether it is a regular Vertex, an Edge or a Property (or a type thereof), to any other part of the graph, no matter if it is a regular Vertex, an Edge or a Property (or a type thereof). All that needs to be supplied are the EdgeTypes that need to be followed in a traversal (and the regular evaluators that go with it). Now the big downer to this all: I still have to write the traversal framework, which will actually follow the Standard Neo4j framework, but will certainly make traversals composable. Every Vertex is not just a Vertex, but it is also a bunch of paths. Well not really a bunch, it is a bunch of size one, and not much of a path either, since it only contains one path element, the Vertex itself. A traversal returns a bunch of paths (Iterable<Path>) and starts from a bunch of paths (still Iterable<Path>). Since the output of a traversal is the same as the input of a traversal we can now compose them. This makes it possible to write a traversal description which states that we want to retrieve the parents of our friends, or the neighbours of the parents of our friends, and even: the names of the dogs of the neighbours of the parents of our friends (after all, we can now traverse to a property). This can be achieved when we make traversal descriptions composable. Most users probably don't want to manually compose traversals, they would much rather compose traversal descriptions and let those descriptions do the composition of the traversals. These are some things to work on over the weekend + plus + plus + documentation (especially Javadoc) and more test cases (especially the integration of IndexedRelationships as SortableBinaryEdges needs thorough testing). For the rest, I'd like to hear opinions and suggestions for improvement. Niels _______________________________________________ Neo4j mailing list User@lists.neo4j.org https://lists.neo4j.org/mailman/listinfo/user