Peter, I haven't put this code out yet. It has been too much in flux to share the code yet. I use neoclipse for visualization, which helps to check the layout of the test graphs i am using. I would need something more programmable for the visual output, since i use node id's of the types as property names and relationshiptypes. This allows for renaming of types and makes it possible to put types in namespaces and be moved from one namespace to another. Using real names for properties and relationshiptypes is not flexible enough. As a result the visualization in neoclipse looks pretty cryptic, having only numbers as labels. I will look into neo4j/neoviz to see if I can export the graph with proper names for the relationships and properties, otherwise i can always roll my own output program. Dot is not the most complex file format to generate. Niels
> Date: Mon, 29 Aug 2011 07:19:35 +0200 > From: [email protected] > To: [email protected] > Subject: Re: [Neo4j] API adventures in Scalaland > > Niels, > Is that Scala code in the graph collections? If you want, ,you could use the > neo4j/neoviz project to output .dot graphs at any point and thus visualize > what's happening in the graph to illustrate :) > > /Peter > > On Monday, August 29, 2011, Niels Hoogeveen <[email protected]> > wrote: > > > > In the last week I have been working on a Neo4j API in Scala, taking > navigation in the graph as primary. > > > > Just like the Enhanced API written in Java, the Scala API generalizes each > element (Node, Relationship, RelationshipType, property name and property > value) of the Neo4j database as being a Vertex. > > > > Before digging into the details of the Scala API, let's start with some > example code. > > > > val name = Db(String("name")) > > val friend = Db(VertexOut("FRIEND")) > > val john = Db(NewVertex).put(name, "John") > > val pete = Db(NewVertex).put(name, "Pete").put(friend, john) > > > > This piece of code defines the PropertyType "name" and the EdgeTypes > "FRIEND", creates two vertices for the persons "John" and "Pete", and states > that John is a friend of Pete. > > > > In standard Neo4j API this could have been written as: > > > > Node john = db.createNode(); > > Node pete = db.createNode(); > > john.setProperty("name", "John"); > > pete.setProperty("name", "Pete"); > > pete.createRelationshipTo(john, > DynamicRelationshipType.withName("FRIEND")); > > > > Apart from an obvious style difference, there is one immediate difference > noticeable between the two API's. > > > > In the Neo4j API it is possible to write: > > > > john.setProperty("name", "John"); > > pete.setProperty("name", 99); > > > > While the following Scala program won't typecheck: > > > > pete.put(name, 99) //ERROR > > > > The "name" property is defined as a String and the API enforces that type. > This also applies when fetching a property value. In the Neo4j API we write: > > > > john.getProperty("name") // returns java.lang.Object > > > > In the Scala API we write: > > > > john(name) // returns java.lang.String > > > > It is also possible to ask the names of pete's friend as follows: > > > > pete(friend andThen name) > > > > This is equal to the Neo4j call: > > > > > > (String)pete.getSingleRelationship(DynamicRelationshipType.withName("FRIEND"), > Direction.OUTGOING).endNode.getProperty("name") > > > > If pete has more than one friend, we have to define a different key to > fetch them ( the VertexOut key refers to one single relationship, either the > one to be created, or a singular exisiting relationship ): > > > > val friends = Db(Vertices("FRIEND")) > > > > We now have a key to all "FRIEND" relationships so we can ask: > > > > pete(friends andThen name) > > > > This returns an Iterable[String] with the names of all Pete's friends. > > > > We can even do: > > > > pete(Rec(friends) andThen name) > > > > This returns an Iterable[String] with the names of all Pete's friends of > friends (to the n-th degree). The Rec object recursively applies "friend" to > all vertices it traverses, remembering already taken paths, traversed > Relationships or traversed Nodes (settings are optional with sensible > defaults). > > > > We can also write: > > > > pete(Rec(friend, 2) andThen name) > > > > This returns an Iterable[String] with the names of all Pete's friends of > friends (to the 2nd degree) > > > > It is even possible to write: > > > > pete(Rec(friend andThen friend) andThen name) > > > > This returns an Iterable[String] with the names of all Pete's friends of > friends (to the n-th degree where n is even) > > > > Instead of having get methods for properties and relationships and > traversal methods on nodes, the Scala API uses one calling pattern for all > database related objects: > > > > object(traverser) > > > > So a call like Db(String("name")) is not just a call on the database to > return a PropertyType with name "name" and datatype String, it is a > traversal from the database to that PropertyType. What is being returned > with that call is a traverser itself. > > > > Traversers can be composed with "andThen", so the output of one traverser > is used as input for the next traverser. > > > > All traversers are typed, so the "andThen" connective can only be applied > when the type of the output of the left-hand-side traverser is equal to the > type as the input of the right-hand-side traverser. This is checked at > compile time. > > > > Traversals not only work on Vertex objects and it's subtypes (Property, > PropertyType, Edge, EdgeType...), it also works on Iterable[Vertex]. > > > > Instead of fetching just pete's friends, as in: > > > > pete(friends) > > > > we can also fetch the friends of pete and john: > > > > val frnds = List(pete, john) > > frnds(friends) > > > > or if we don't need the "frnds" object later on, we simply state: > > > > List(pete, john)(friends) > > > > and if we want the names of those friends: > > > > List(pete, john)(friends andThen name) > > > > it is even possible to set properties or create relationships on > Iterable[Vertex] > > > > val age = Db(Int("age")) > > val nationality = Db(String("nationality")) > > List(pete, john).put(age, 40).put(nationality, "Irish") > > > > This sets the "age" property to 40 on both "pete" and "john". > > > > It is also possible to write this as a traversal: > > > > List(pete, john)(Put(age, 40) andThen Put(nationality, "Irish")) > > > > All traversers are function objects, so they can both be called as a > function and can be treated as an object. This makes it possible to create > traverers programmatically, allowing for the storage of traversers in the > database, and many more nifty tricks. > > > > Using the Put object, we could for example create a list of such > actions/traversals and perform a validation on the list before actually > calling the put-functions. > > > > There is much more to say about the API, for example the support of n-ary > edges, but I will leave it for now. I think this approach to traversals and > the fusion of Vertex and Iterable[Vertex] is enough for now. > > > > Niels > > > > > > > > > > _______________________________________________ > > Neo4j mailing list > > [email protected] > > https://lists.neo4j.org/mailman/listinfo/user > > > > -- > > Cheers, > > /peter neubauer > > GTalk: neubauer.peter > Skype peter.neubauer > Phone +46 704 106975 > LinkedIn http://www.linkedin.com/in/neubauer > Twitter http://twitter.com/peterneubauer > > http://www.neo4j.org - Your high performance graph database. > http://startupbootcamp.org/ - Ă–resund - Innovation happens HERE. > http://www.thoughtmade.com - Scandinavia's coolest Bring-a-Thing party. > _______________________________________________ > Neo4j mailing list > [email protected] > https://lists.neo4j.org/mailman/listinfo/user _______________________________________________ Neo4j mailing list [email protected] https://lists.neo4j.org/mailman/listinfo/user

