neo4jr-social 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|