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 +19 -8
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/bin/start-neo4jr-social +1 -1
- data/examples/linkedin.rb +1 -1
- data/lib/neo4jr-social/service.rb +21 -4
- data/lib/neo4jr-social/simple_cost_evaluator.rb +9 -0
- data/lib/neo4jr-social.rb +3 -2
- data/neo4jr-social.gemspec +6 -5
- data/spec/service_spec.rb +54 -39
- data/spec/spec_helper.rb +2 -2
- metadata +4 -3
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
|
-
|
14
|
+
Install Ruby Gem: json_pure
|
15
15
|
jgem install json_pure
|
16
|
-
|
16
|
+
Install Ruby Gem: sinatra
|
17
17
|
jgem install sinatra
|
18
|
-
|
18
|
+
Install Ruby Gem: neo4jr-simple
|
19
19
|
jgem install neo4jr-simple
|
20
|
-
|
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
|
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
|
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/
|
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.
|
1
|
+
0.1.1
|
data/bin/start-neo4jr-social
CHANGED
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']}/
|
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
|
-
|
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
|
-
|
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'))
|
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
|
|
data/neo4jr-social.gemspec
CHANGED
@@ -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.
|
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-
|
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
|
-
|
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
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
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
|
-
|
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
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
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
|
-
|
5
|
-
|
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.
|
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-
|
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:
|
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
|