neo4jr-social 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Neo4jr-Social is a self contained HTTP REST + JSON interface to the graph database Neo4j. Neo4jr-Social supports simple dynamic node creation, building relationships between nodes and also includes a few common social networking queries out of the box (i.e. linkedin degrees of seperation and facebook friend suggestion) with more to come. Think of Neo4jr-Social is to Neo4j like Solr is to Lucene.
4
4
 
5
- Neo4jr-Social was built in JRuby but is language agnostic and is designed to run under Jetty or Tomcat.
5
+ Neo4jr-Social was built in JRuby but is language agnostic and is designed to run under Jetty or Tomcat.
6
6
 
7
7
  = Getting Started
8
8
 
@@ -11,13 +11,13 @@ Neo4jr-Social was built in JRuby but is language agnostic and is designed to run
11
11
  * JRuby: http://jruby.org
12
12
 
13
13
  === Installing
14
- * Install Ruby Gem: json_pure
14
+ Install Ruby Gem: json_pure
15
15
  jgem install json_pure
16
- * Install Ruby Gem: sinatra
16
+ Install Ruby Gem: sinatra
17
17
  jgem install sinatra
18
- * Install Ruby Gem: neo4jr-simple
18
+ Install Ruby Gem: neo4jr-simple
19
19
  jgem install neo4jr-simple
20
- * Install Ruby Gem: neo4jr-social
20
+ Install Ruby Gem: neo4jr-social
21
21
  jgem install neo4jr-simple
22
22
 
23
23
  === Starting Neo4jr-Social
@@ -36,13 +36,13 @@ Run start-neo4jr-social --help to see all options
36
36
  All results returned will be in JSON
37
37
 
38
38
  ==== GET /info
39
- Returns details about the Neo4jr database like the location of the database and the number of nodes.
39
+ Returns details about the Neo4j database like the location of the database and the number of nodes.
40
40
 
41
41
  ==== GET /nodes
42
42
  Returns all nodes in the database. Use this method with caution, this could crash your server if you have a database with more then a few thousand nodes.
43
43
 
44
44
  ==== POST /nodes
45
- Creates a new node in the neo4jr database. Any parameters based in the body of the POST will be treated as properties for the node and will be stored in the database.
45
+ Creates a new node in the neo4j database. Any parameters based in the body of the POST will be treated as properties for the node and will be stored in the database.
46
46
 
47
47
  ==== GET /nodes/:node_id
48
48
  Returns the properties for the specified node, where :node_id is the numeric id for the node
@@ -63,18 +63,29 @@ Creates a relations for the specified node, where :node_id is the numeric id for
63
63
  Returns relationships to other nodes for the specified node, where :node_id is the numeric id for the node.
64
64
  * Optional: type - Specify a type if only certain relationships are of interest
65
65
 
66
- ==== GET /nodes/:node_id/path
66
+ ==== GET /nodes/:node_id/paths
67
67
  This returns all the ways two nodes are connected to each other and is similar to LinkedIn's degrees of separation.
68
68
  * Required: to - the id of the node that your trying to find a path to from the starting node, :node_id
69
69
  * Required: type - the type of relationships between nodes to follow
70
70
  * Optional: depth - maximum degrees of separation to find, the default is 2 degrees. Note: There may be performance impacts if this number is to high.
71
71
  * Optional: direction - What direction of relationships to follow, the default is 'both'
72
+
73
+ ==== GET /nodes/:node_id/shortest_path
74
+ This returns the shortest path of two nodes that are connected to each other
75
+ * Required: to - the id of the node that your trying to find a path to from the starting node, :node_id
76
+ * Required: type - the type of relationships between nodes to follow
72
77
 
73
78
  ==== GET /nodes/:node_id/recommendations
74
79
  This returns node suggestions for the given :node_id. This is similar to facebook friend suggestions where your friend's friends that your not friends with are suggested to you.
75
80
  * Required: type - the type of relationships between nodes to follow
76
81
  * Optional: level - the degree of separation that you want recommendations for, the default is 1 degree away which is similar to facebook's behavior
77
82
 
83
+
84
+ == Troubleshooting
85
+
86
+ /usr/bin/env: jruby: No such file or directory
87
+
88
+ You need to either install jruby or if you used a utility like rvm then your jruby executable may actually be called jruby-1.4, if this is the case create an alias somewhere in your PATH
78
89
  == Contributors
79
90
 
80
91
  ====Matthew Deiters
data/Rakefile CHANGED
@@ -15,7 +15,7 @@ begin
15
15
  gem.authors = ["Matthew Deiters"]
16
16
  gem.add_development_dependency "rspec", ">= 1.2.9"
17
17
  gem.add_development_dependency "rest-client"
18
- gem.add_dependency 'neo4jr-simple'
18
+ gem.add_dependency 'neo4jr-simple', ">= 0.1.6" unless ENV['dev_on_gem']
19
19
  gem.add_dependency 'sinatra'
20
20
  gem.add_dependency 'json_pure'
21
21
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.1.1
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env jruby-1.4.0
1
+ #!/usr/bin/env jruby
2
2
 
3
3
  using_gems = false
4
4
  begin
data/examples/linkedin.rb CHANGED
@@ -15,7 +15,7 @@ def make_mutual_friends(node1, node2)
15
15
  end
16
16
 
17
17
  def degrees_of_seperation(start_node, destination_node)
18
- url = "http://localhost:8988/neo4jr-social/nodes/#{start_node['node_id']}/path?to=#{destination_node['node_id']}&type=friends&depth=3&direction=outgoing"
18
+ url = "http://localhost:8988/neo4jr-social/nodes/#{start_node['node_id']}/paths?to=#{destination_node['node_id']}&type=friends&depth=3&direction=outgoing"
19
19
  response = RestClient.get(url)
20
20
  JSON.parse(response)
21
21
  end
@@ -63,12 +63,11 @@ module Neo4jr
63
63
  relationships.to_json
64
64
  end
65
65
 
66
- #optional direction & depth
67
- get '/nodes/:node_id/path' do
66
+ get '/nodes/:node_id/paths' do
68
67
  paths = Neo4jr::DB.execute do |neo|
69
- relationship = Neo4jr::RelationshipType.instance(params.delete('type'))
70
68
  start_node = neo.getNodeById(params.delete('node_id'))
71
69
  end_node = neo.getNodeById(params.delete('to'))
70
+ relationship = Neo4jr::RelationshipType.instance(params.delete('type'))
72
71
  depth = params.delete('depth') || 2
73
72
  direction = Neo4jr::Direction.from_string(params.delete('direction') || 'both')
74
73
  shortest_path = AllSimplePaths.new(start_node, end_node, depth.to_i, direction, relationship.to_a)
@@ -78,7 +77,25 @@ module Neo4jr
78
77
  paths.to_json
79
78
  end
80
79
 
81
- #optional
80
+ get '/nodes/:node_id/shortest_path' do
81
+ path = Neo4jr::DB.execute do |neo|
82
+ start_node = neo.getNodeById(params.delete('node_id'))
83
+ end_node = neo.getNodeById(params.delete('to'))
84
+ relationship = Neo4jr::RelationshipType.instance(params.delete('type'))
85
+ dijkstra = Neo4jr::Dijkstra.new(
86
+ 0.0,
87
+ start_node,
88
+ end_node,
89
+ Neo4jr::SimpleEvaluator.new,
90
+ DoubleAdder.new,
91
+ DoubleComparator.new,
92
+ Direction::BOTH,
93
+ relationship.to_a)
94
+ dijkstra.getPath.map{|n| n.to_hash }
95
+ end
96
+ path.to_json
97
+ end
98
+
82
99
  get '/nodes/:node_id/recommendations' do
83
100
  suggestions = Neo4jr::DB.execute do |neo|
84
101
  relationship = Neo4jr::RelationshipType.incoming(params.delete('type'))
@@ -0,0 +1,9 @@
1
+ module Neo4jr
2
+ class SimpleEvaluator
3
+ include org.neo4j.graphalgo.shortestpath.CostEvaluator
4
+
5
+ def getCost(relationship, backwards)
6
+ 1.0
7
+ end
8
+ end
9
+ end
data/lib/neo4jr-social.rb CHANGED
@@ -4,11 +4,12 @@ include Java
4
4
 
5
5
  require 'rubygems'
6
6
  gem 'sinatra'
7
- gem 'json_pure'
8
- gem 'neo4jr-simple'
7
+ gem 'json_pure'
8
+ gem 'neo4jr-simple' unless ENV['dev_on_gem']
9
9
 
10
10
  require 'sinatra'
11
11
  require 'json'
12
12
  require 'neo4jr-simple'
13
+ require 'neo4jr-social/simple_cost_evaluator'
13
14
  require 'neo4jr-social/service'
14
15
 
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{neo4jr-social}
8
- s.version = "0.1.0"
8
+ s.version = "0.1.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Matthew Deiters"]
12
- s.date = %q{2009-12-23}
12
+ s.date = %q{2009-12-26}
13
13
  s.default_executable = %q{start-neo4jr-social}
14
14
  s.description = %q{A self-containted lightweight REST interface to Neo4j using JRuby }
15
15
  s.email = %q{matthew_deiters@mckinsey.com}
@@ -44,6 +44,7 @@ Gem::Specification.new do |s|
44
44
  "jetty-runtime/webapps/neo4jr-social.war",
45
45
  "lib/neo4jr-social.rb",
46
46
  "lib/neo4jr-social/service.rb",
47
+ "lib/neo4jr-social/simple_cost_evaluator.rb",
47
48
  "lib/neo4jr-social/version.rb",
48
49
  "neo4jr-social.gemspec",
49
50
  "spec/service_spec.rb",
@@ -76,20 +77,20 @@ Gem::Specification.new do |s|
76
77
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
77
78
  s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
78
79
  s.add_development_dependency(%q<rest-client>, [">= 0"])
79
- s.add_runtime_dependency(%q<neo4jr-simple>, [">= 0"])
80
+ s.add_runtime_dependency(%q<neo4jr-simple>, [">= 0.1.6"])
80
81
  s.add_runtime_dependency(%q<sinatra>, [">= 0"])
81
82
  s.add_runtime_dependency(%q<json_pure>, [">= 0"])
82
83
  else
83
84
  s.add_dependency(%q<rspec>, [">= 1.2.9"])
84
85
  s.add_dependency(%q<rest-client>, [">= 0"])
85
- s.add_dependency(%q<neo4jr-simple>, [">= 0"])
86
+ s.add_dependency(%q<neo4jr-simple>, [">= 0.1.6"])
86
87
  s.add_dependency(%q<sinatra>, [">= 0"])
87
88
  s.add_dependency(%q<json_pure>, [">= 0"])
88
89
  end
89
90
  else
90
91
  s.add_dependency(%q<rspec>, [">= 1.2.9"])
91
92
  s.add_dependency(%q<rest-client>, [">= 0"])
92
- s.add_dependency(%q<neo4jr-simple>, [">= 0"])
93
+ s.add_dependency(%q<neo4jr-simple>, [">= 0.1.6"])
93
94
  s.add_dependency(%q<sinatra>, [">= 0"])
94
95
  s.add_dependency(%q<json_pure>, [">= 0"])
95
96
  end
data/spec/service_spec.rb CHANGED
@@ -52,61 +52,76 @@ describe Neo4jr::Service do
52
52
  relationship['type'].should == 'acted_in'
53
53
  relationship['to'].should == movie['node_id']
54
54
  end
55
-
56
- describe 'degrees of seperation using shortest path' do
57
- let(:actor1) { post('/nodes', {:name => 'Philip Seymour Hoffman'}) && response_to_ruby }
58
- let(:actor2) { post('/nodes', {:name => 'Tina Fey'}) && response_to_ruby}
59
- let(:movie) { post('/nodes', {:title => 'The Invention of Lying'}) && response_to_ruby}
60
55
 
61
- it 'determines degrees of seperation between nodes like LinkedIn' do
62
- post "/nodes/#{actor1['node_id']}/relationships", { :to => movie['node_id'], :type => 'acted_in', :year => 2009 }
63
- post "/nodes/#{actor2['node_id']}/relationships", { :to => movie['node_id'], :type => 'acted_in', :year => 2009 }
64
-
65
- get "/nodes/#{actor1['node_id']}/path", { :to => actor2['node_id'], :type => 'acted_in'}
66
- last_response.status.should == 200
67
- paths = response_to_ruby
68
- first_path = paths.first
69
- first_path[0]['name'].should == 'Philip Seymour Hoffman'
70
- first_path[2]['title'].should == 'The Invention of Lying'
71
- first_path[4]['name'].should == 'Tina Fey'
72
- end
73
-
74
- end
75
-
76
- describe 'suggestions' do
56
+ describe 'querying paths' do
77
57
  let(:hoffman) { post('/nodes', {:name => 'Philip Seymour Hoffman'}) && response_to_ruby }
78
58
  let(:fey) { post( '/nodes', {:name => 'Tina Fey'}) && response_to_ruby}
79
59
  let(:hanks) { post( '/nodes', {:name => 'Tom Hanks'}) && response_to_ruby}
80
60
  let(:murphy) { post( '/nodes', {:name => 'Brittney Murphy'}) && response_to_ruby}
81
61
  let(:bale) { post( '/nodes', {:name => 'Christian Bale'}) && response_to_ruby}
62
+ let(:cruise) { post( '/nodes', {:name => 'Tom Cruise'}) && response_to_ruby}
82
63
 
83
64
  before :each do
84
- create_mutual_friends = Proc.new do |node1, node2|
65
+ @create_mutual_friends = Proc.new do |node1, node2|
85
66
  post "/nodes/#{node1['node_id']}/relationships", { :to => node2['node_id'], :type => 'friends' }
86
67
  post "/nodes/#{node2['node_id']}/relationships", { :to => node1['node_id'], :type => 'friends' }
87
68
  end
88
69
 
89
- create_mutual_friends.call(hoffman, fey)
90
- create_mutual_friends.call(hoffman, murphy)
91
- create_mutual_friends.call(murphy, fey)
92
- create_mutual_friends.call(murphy, hanks)
93
- create_mutual_friends.call(hanks, bale)
70
+ @create_mutual_friends.call(hoffman, fey)
71
+ @create_mutual_friends.call(hoffman, murphy)
72
+ @create_mutual_friends.call(murphy, fey)
73
+ @create_mutual_friends.call(murphy, hanks)
74
+ @create_mutual_friends.call(hanks, bale)
94
75
  end
95
76
 
96
- it "gets my friend's friends that I'm not friends with as suggestions" do
97
- get "/nodes/#{hoffman['node_id']}/recommendations?type=friends"
98
- last_response.status.should == 200
99
- suggestions = response_to_ruby
100
- suggestions.size.should == 1
101
- suggestions.first['name'].should == 'Tom Hanks'
77
+ describe 'suggestions' do
78
+ it "gets my friend's friends that I'm not friends with as suggestions" do
79
+ get "/nodes/#{hoffman['node_id']}/recommendations?type=friends"
80
+ last_response.status.should == 200
81
+ suggestions = response_to_ruby
82
+ suggestions.size.should == 1
83
+ suggestions.first['name'].should == 'Tom Hanks'
84
+ end
85
+
86
+ it "gets my friend's friend's friends that I'm not friends with as suggestions" do
87
+ get "/nodes/#{hoffman['node_id']}/recommendations?type=friends&level=2"
88
+ last_response.status.should == 200
89
+ suggestions = response_to_ruby
90
+ suggestions.size.should == 1
91
+ suggestions.first['name'].should == 'Christian Bale'
92
+ end
102
93
  end
94
+
95
+ it 'retrieves only shortest path between nodes' do
96
+ @create_mutual_friends.call(fey, cruise)
97
+ @create_mutual_friends.call(hanks, cruise)
98
+
99
+ get "/nodes/#{hoffman['node_id']}/shortest_path?type=friends&to=#{cruise['node_id']}"
100
+ path_to_cruise = response_to_ruby
101
+ path_to_cruise[0]['name'].should == 'Philip Seymour Hoffman'
102
+ path_to_cruise[1]['type'].should == 'friends'
103
+ path_to_cruise[2]['name'].should == 'Tina Fey'
104
+ path_to_cruise[3]['type'].should == 'friends'
105
+ path_to_cruise[4]['name'].should == 'Tom Cruise'
106
+ end
107
+ end
108
+
109
+ describe 'getting paths between nodes' do
110
+ let(:actor1) { post('/nodes', {:name => 'Philip Seymour Hoffman'}) && response_to_ruby }
111
+ let(:actor2) { post('/nodes', {:name => 'Tina Fey'}) && response_to_ruby}
112
+ let(:movie) { post('/nodes', {:title => 'The Invention of Lying'}) && response_to_ruby}
113
+
114
+ it 'determines all degrees of seperation between nodes like LinkedIn' do
115
+ post "/nodes/#{actor1['node_id']}/relationships", { :to => movie['node_id'], :type => 'acted_in', :year => 2009 }
116
+ post "/nodes/#{actor2['node_id']}/relationships", { :to => movie['node_id'], :type => 'acted_in', :year => 2009 }
103
117
 
104
- it "gets my friend's friend's friends that I'm not friends with as suggestions" do
105
- get "/nodes/#{hoffman['node_id']}/recommendations?type=friends&level=2"
118
+ get "/nodes/#{actor1['node_id']}/paths", { :to => actor2['node_id'], :type => 'acted_in'}
106
119
  last_response.status.should == 200
107
- suggestions = response_to_ruby
108
- suggestions.size.should == 1
109
- suggestions.first['name'].should == 'Christian Bale'
110
- end
120
+ paths = response_to_ruby
121
+ first_path = paths.first
122
+ first_path[0]['name'].should == 'Philip Seymour Hoffman'
123
+ first_path[2]['title'].should == 'The Invention of Lying'
124
+ first_path[4]['name'].should == 'Tina Fey'
125
+ end
111
126
  end
112
127
  end
data/spec/spec_helper.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  $LOAD_PATH.unshift(File.dirname(__FILE__))
2
2
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
3
 
4
- # $LOAD_PATH.unshift(File.join(File.expand_path(File.dirname(__FILE__)), '..', '..', 'neo4jr-simple', 'lib'))
5
- # require 'neo4jr-simple'
4
+ $LOAD_PATH.unshift(File.join(File.expand_path(File.dirname(__FILE__)), '..', '..', 'neo4jr-simple', 'lib'))
5
+ require 'neo4jr-simple'
6
6
 
7
7
  require 'neo4jr-social'
8
8
  require 'spec'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: neo4jr-social
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew Deiters
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-12-23 00:00:00 -08:00
12
+ date: 2009-12-26 00:00:00 -08:00
13
13
  default_executable: start-neo4jr-social
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -40,7 +40,7 @@ dependencies:
40
40
  requirements:
41
41
  - - ">="
42
42
  - !ruby/object:Gem::Version
43
- version: "0"
43
+ version: 0.1.6
44
44
  version:
45
45
  - !ruby/object:Gem::Dependency
46
46
  name: sinatra
@@ -97,6 +97,7 @@ files:
97
97
  - jetty-runtime/webapps/neo4jr-social.war
98
98
  - lib/neo4jr-social.rb
99
99
  - lib/neo4jr-social/service.rb
100
+ - lib/neo4jr-social/simple_cost_evaluator.rb
100
101
  - lib/neo4jr-social/version.rb
101
102
  - neo4jr-social.gemspec
102
103
  - spec/service_spec.rb