pangloss/pacer — pull request #6 by pangloss (2011-10-11 21:42:35+00:00)
Title: Experiments with RubyGraph

I've had a few ideas bouncing around in my head about some graph variations that might be cool. This branch is where I'm experimenting with them.

Some of what does go into production here may end up in plugins rather than being merged, but I haven't decided how it should be structured.

The ideas I'm playing with here, in no particular order:

* Pure ruby graph. This is similar to the TinkerGraph but does not restrict vertices to be any particular class, anything that implements the com.tinkerpop.blueprints.pgm.Vertex interface will work, allowing temporary graphs to be put together that associate elements from various graphs.
* ReplayGraph records all commands that mutate the graph, allowing them to be rerun later on another graph. It's not completely worked out yet.
* MultiGraph allows vertices to be grouped together and traversed as if they were one vertex. Vertices can be stored in a MultiVertex's properties. Traversals retrieve the normal edges as well as the edges of the vertices contained in the properties. This seems like it will be useful but I'm not sure. The idea was to replicate the way SQL queries can join tables and then further query based on that combined information.

I've been able to successfully use the MultiGraph to replace the existing group functionality in Pacer. It seems like a huge improvement. The examples produce the same results, but the multigraph one has the advantage that the results are still in a graph and can be further worked with and easily transformed -- the result is just another graph!

Group:

```ruby
person.in(:rated).out(:rated).is_not(person).uniq.group.values_route(:default) do |friend|
friend.in_e(:rated).as(:friend_rating_edge).out_v.out_e.as(:my_rating_edge).in_v.is(person).map do |v|
5 - (v.vars[:friend_rating_edge][:weight] - v.vars[:my_rating_edge][:weight]).abs
end
end.reduce(0) { |total, n| total + n }
```

MultiGraph:

```ruby
graph = person.v.
in(:rated).out(:rated).is_not(person).uniq.collect_as(:friend).
in_e(:rated).as(:friend_rating_edge).out_v.out_e.as(:my_rating_edge).in_v.is(person).map_to(:friend, :ratings) do |v, _|
5 - (v.vars[:friend_rating_edge][:weight] - v.vars[:my_rating_edge][:weight]).abs
end.
collected
graph.v.each { |v| v[:sum] = v[:ratings].inject(:+) }
```