neo4jr-social 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/README.rdoc +10 -69
- data/Rakefile +1 -3
- data/TODO +4 -4
- data/VERSION +1 -1
- data/config/warble.rb +18 -0
- data/examples/facebook.rb +3 -1
- data/examples/linkedin.rb +1 -0
- data/jetty-runtime/webapps/neo4jr-social.war +0 -0
- data/lib/neo4jr-social/format_handler.rb +50 -0
- data/lib/neo4jr-social/json_printer.rb +154 -0
- data/lib/neo4jr-social/param_helper.rb +32 -0
- data/lib/neo4jr-social/self_documentor.rb +43 -0
- data/lib/neo4jr-social/service.rb +76 -46
- data/lib/neo4jr-social.rb +37 -4
- data/neo4jr-social.gemspec +13 -16
- data/spec/format_handler_spec.rb +22 -0
- data/spec/json_printer_spec.rb +94 -0
- data/spec/service_spec.rb +1 -1
- data/spec/spec_helper.rb +4 -4
- metadata +11 -21
- data/lib/neo4jr-social/simple_cost_evaluator.rb +0 -9
- data/lib/neo4jr-social/version.rb +0 -3
- data/tmp/war/WEB-INF/lib/jruby-core-1.4.0.jar +0 -0
- data/tmp/war/WEB-INF/lib/jruby-rack-0.9.5.jar +0 -0
- data/tmp/war/WEB-INF/lib/jruby-stdlib-1.4.0.jar +0 -0
- data/tmp/war/WEB-INF/lib/neo4jr-social/service.rb +0 -97
- data/tmp/war/WEB-INF/lib/neo4jr-social/version.rb +0 -3
- data/tmp/war/WEB-INF/lib/neo4jr-social.rb +0 -14
- data/tmp/war/WEB-INF/web.xml +0 -38
data/.gitignore
CHANGED
data/README.rdoc
CHANGED
@@ -2,90 +2,31 @@
|
|
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 as a self-contained service or can be deployed under Jetty or Tomcat.
|
6
6
|
|
7
7
|
= Getting Started
|
8
8
|
|
9
|
-
|
9
|
+
== Prerequisites
|
10
|
+
|
10
11
|
* Java
|
11
12
|
* JRuby: http://jruby.org
|
12
13
|
|
13
|
-
|
14
|
-
Install Ruby Gem: json_pure
|
15
|
-
jgem install json_pure
|
16
|
-
Install Ruby Gem: sinatra
|
17
|
-
jgem install sinatra
|
18
|
-
Install Ruby Gem: neo4jr-simple
|
19
|
-
jgem install neo4jr-simple
|
20
|
-
Install Ruby Gem: neo4jr-social
|
21
|
-
jgem install neo4jr-simple
|
22
|
-
|
23
|
-
=== Starting Neo4jr-Social
|
14
|
+
== Running Neo4jr-Social
|
24
15
|
|
25
|
-
After installing the above gems, an executable 'start-neo4jr-social' should be in your
|
16
|
+
After installing the above gems, an executable 'start-neo4jr-social' should be in your $PATH. This will start the service and the database and listen for HTTP requests on localhost at port 8988 by default:
|
26
17
|
|
27
18
|
start-neo4jr-social -p8988
|
28
19
|
|
29
20
|
You can also start the service and specify the directory of where the neo4j database is stored or where to create a new database. If you omit this setting a database will be created in your tmp directory and destroyed when you shutdown the server.
|
30
21
|
|
31
22
|
start-neo4jr-social -p8988 -dsome_relative_or_absolute_directory
|
32
|
-
|
23
|
+
|
33
24
|
Run start-neo4jr-social --help to see all options
|
34
25
|
|
35
|
-
=== API
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
Returns details about the Neo4j database like the location of the database and the number of nodes.
|
40
|
-
|
41
|
-
==== GET /nodes
|
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
|
-
|
44
|
-
==== POST /nodes
|
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
|
-
|
47
|
-
==== GET /nodes/:node_id
|
48
|
-
Returns the properties for the specified node, where :node_id is the numeric id for the node
|
49
|
-
|
50
|
-
==== PUT /nodes/:node_id
|
51
|
-
Updates the properties of the specified node, where :node_id is the numeric id for the node. Any parameters pased in the body of the PUT will be treated as properties for the node. If you add a new parameters (i.e. age=4) which previously were not on the node, neo4jr-social will still add that property to the node.
|
52
|
-
|
53
|
-
==== DELETE /nodes/:node_id
|
54
|
-
Deletes the specified node, where :node_id is the numeric id for the node.
|
55
|
-
|
56
|
-
==== POST /nodes/:node_id/relationships
|
57
|
-
Creates a relations for the specified node, where :node_id is the numeric id for the node. This is how you designate how two nodes are related to each other.
|
58
|
-
* Required: to - this is the node id of the node you want to make a relationship to. This is a one-way relationship. If you want both nodes to be related to each other you will need to make individual POSTs for each direction of the relationship.
|
59
|
-
* Required: type - this is the type of the relationship, i.e. 'friends'. This can be any string that is sensible in your domain.
|
60
|
-
* Optional: Any other parameters you supply in the body of the POST will be added as properties to the relationship. For example if you were making 'friend' relationships and wanted to add a date since the friendship started you could pass a 'since' parameter in the POST.
|
61
|
-
|
62
|
-
==== GET /nodes/:node_id/relationships
|
63
|
-
Returns relationships to other nodes for the specified node, where :node_id is the numeric id for the node.
|
64
|
-
* Optional: type - Specify a type if only certain relationships are of interest
|
65
|
-
|
66
|
-
==== GET /nodes/:node_id/paths
|
67
|
-
This returns all the ways two nodes are connected to each other and is similar to LinkedIn's degrees of separation.
|
68
|
-
* Required: to - the id of the node that your trying to find a path to from the starting node, :node_id
|
69
|
-
* Required: type - the type of relationships between nodes to follow
|
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
|
-
* 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
|
77
|
-
|
78
|
-
==== GET /nodes/:node_id/recommendations
|
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.
|
80
|
-
* Required: type - the type of relationships between nodes to follow
|
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
|
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
|
26
|
+
=== API
|
27
|
+
|
28
|
+
The service is documented at http://wiki.github.com/mdeiters/neo4jr-social/
|
29
|
+
|
89
30
|
== Contributors
|
90
31
|
|
91
32
|
====Matthew Deiters
|
data/Rakefile
CHANGED
@@ -15,11 +15,9 @@ 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', ">= 0.1.6" unless ENV['
|
18
|
+
gem.add_dependency 'neo4jr-simple', ">= 0.1.6" unless ENV['neo4jr_simple']
|
19
19
|
gem.add_dependency 'sinatra'
|
20
20
|
gem.add_dependency 'json_pure'
|
21
|
-
|
22
|
-
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
23
21
|
end
|
24
22
|
Jeweler::GemcutterTasks.new
|
25
23
|
rescue LoadError
|
data/TODO
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
* Figure out a better way to shebang for start-neo4jr-social
|
2
1
|
* Validate requests, check parameters, etc
|
3
|
-
*
|
2
|
+
* Retrieve Node by identifier other then node id
|
3
|
+
* Figure out how to handle the Neo4j nodes classname
|
4
|
+
* More querying potential: Enable solr type parameters with lt gt eq etc or enable gremlin
|
5
|
+
Examples
|
4
6
|
* Query Type: Amazon Recommendations
|
5
7
|
* Query Type: Most Popular
|
6
8
|
* Query Type: Primitive dynamic querying on all nodes
|
7
|
-
* Enable solr type parameters with lt gt eq etc
|
8
|
-
* Explore vendor-ize all ruby libs into WEB-INF
|
9
9
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.2
|
data/config/warble.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# Warbler web application assembly configuration file
|
2
|
+
Warbler::Config.new do |config|
|
3
|
+
config.dirs = %w(lib)
|
4
|
+
config.includes = FileList["config.ru"]
|
5
|
+
|
6
|
+
config.gems['sinatra'] = '0.9.4'
|
7
|
+
config.gems['json_pure'] = '1.2.0'
|
8
|
+
config.gems['neo4jr-simple'] = '0.1.7'
|
9
|
+
|
10
|
+
config.gem_dependencies = true
|
11
|
+
config.webxml.booter = :rack
|
12
|
+
|
13
|
+
# Control the pool of runtimes. Leaving unspecified means
|
14
|
+
# the pool will grow as needed to service requests. It is recommended
|
15
|
+
# that you fix these values when running a production server!
|
16
|
+
# config.webxml.jruby.min.runtimes = 2
|
17
|
+
# config.webxml.jruby.max.runtimes = 4
|
18
|
+
end
|
data/examples/facebook.rb
CHANGED
@@ -30,4 +30,6 @@ make_mutual_friends(phill, mary)
|
|
30
30
|
make_mutual_friends(phill, luke)
|
31
31
|
|
32
32
|
puts "Johnathan should become friends with #{suggestions_for(johnathan).map{|n| n['name']}.join(', ')}"
|
33
|
-
|
33
|
+
|
34
|
+
# RESULT
|
35
|
+
# Johnathan should become friends with Mary, Phill
|
data/examples/linkedin.rb
CHANGED
@@ -34,5 +34,6 @@ degrees_of_seperation(johnathan, mary).each do |path|
|
|
34
34
|
puts path.map{|node| node['name'] || node['type']}.join(' => ')
|
35
35
|
end
|
36
36
|
|
37
|
+
# RESULT
|
37
38
|
# Johnathan => friends => Mark => friends => Phill => friends => Mary
|
38
39
|
# Johnathan => friends => Mark => friends => Mary
|
Binary file
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Neo4jr
|
2
|
+
module FormatHandler
|
3
|
+
def self.registered(app)
|
4
|
+
app.send :mime, :json, 'application/json'
|
5
|
+
app.set :assume_xhr_is_js, true
|
6
|
+
app.helpers self
|
7
|
+
app.before do
|
8
|
+
if request.env['HTTP_ACCEPT'] && request.env['HTTP_ACCEPT'].include?('text/html')
|
9
|
+
format :html
|
10
|
+
else
|
11
|
+
format :json
|
12
|
+
end
|
13
|
+
charset 'utf-8'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def render_for_format(data)
|
18
|
+
case format
|
19
|
+
when :json : return JsonPrinter.render(data)
|
20
|
+
when :html : return JsonPrinter.render_html(data)
|
21
|
+
else
|
22
|
+
fail("#{format} is not a supported MIME type")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def format(val=nil)
|
27
|
+
unless val.nil?
|
28
|
+
mime_type = media_type(val)
|
29
|
+
fail "Unknown media type #{val}\nTry registering the extension with a mime type" if mime_type.nil?
|
30
|
+
|
31
|
+
@format = val.to_sym
|
32
|
+
response['Content-Type'].sub!(/^[^;]+/, mime_type)
|
33
|
+
end
|
34
|
+
|
35
|
+
@format
|
36
|
+
end
|
37
|
+
|
38
|
+
def charset(val=nil)
|
39
|
+
fail "Content-Type must be set in order to specify a charset" if response['Content-Type'].nil?
|
40
|
+
|
41
|
+
if response['Content-Type'] =~ /charset=[^;]+/
|
42
|
+
response['Content-Type'].sub!(/charset=[^;]+/, (val == '' && '') || "charset=#{val}")
|
43
|
+
else
|
44
|
+
response['Content-Type'] += ";charset=#{val}"
|
45
|
+
end unless val.nil?
|
46
|
+
|
47
|
+
response['Content-Type'][/charset=([^;]+)/, 1]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
# Please see http://github.com/techcrunch/json_printer
|
2
|
+
#
|
3
|
+
class JsonPrinter
|
4
|
+
attr_reader :buf, :indent
|
5
|
+
|
6
|
+
# ==== Arguments
|
7
|
+
# obj<Object>::
|
8
|
+
# The object to be rendered into JSON. This object and all of its
|
9
|
+
# associated objects must be either nil, true, false, a String, a Symbol,
|
10
|
+
# a Numeric, an Array, or a Hash.
|
11
|
+
#
|
12
|
+
# ==== Returns
|
13
|
+
# <String>::
|
14
|
+
# The pretty-printed JSON ecoding of the given <i>obj</i>. This string
|
15
|
+
# can be parsed by any compliant JSON parser without modification.
|
16
|
+
#
|
17
|
+
# ==== Examples
|
18
|
+
# See <tt>JsonPrinter</tt> docs.
|
19
|
+
#
|
20
|
+
def self.render(obj)
|
21
|
+
new(obj).buf
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.render_html(obj)
|
25
|
+
render(obj).gsub(' ', ' ').gsub("\n", "<br>")
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# Execute the JSON rendering of <i>obj</i>, storing the result in the
|
31
|
+
# <tt>buf</tt>.
|
32
|
+
#
|
33
|
+
def initialize(obj)
|
34
|
+
@buf = ""
|
35
|
+
@indent = ""
|
36
|
+
render(obj)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Increase the indentation level.
|
40
|
+
#
|
41
|
+
def indent_out
|
42
|
+
@indent << " "
|
43
|
+
end
|
44
|
+
|
45
|
+
# Decrease the indendation level.
|
46
|
+
#
|
47
|
+
def indent_in
|
48
|
+
@indent.slice!(-1, 1)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Append the given <i>str</i> to the <tt>buf</tt>.
|
52
|
+
#
|
53
|
+
def print(str)
|
54
|
+
@buf << str
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
# Recursive rendering method. Primitive values, like nil, true, false,
|
59
|
+
# numbers, symbols, and strings are converted to JSON and appended to the
|
60
|
+
# buffer. Enumerables are treated specially to generate pretty whitespace.
|
61
|
+
#
|
62
|
+
def render(obj)
|
63
|
+
# We can't use a case statement here becuase "when Hash" doesn't work for
|
64
|
+
# ActiveSupport::OrderedHash - respond_to?(:values) is a more reliable
|
65
|
+
# indicator of hash-like behavior.
|
66
|
+
if NilClass === obj
|
67
|
+
print("null")
|
68
|
+
|
69
|
+
elsif TrueClass === obj
|
70
|
+
print("true")
|
71
|
+
|
72
|
+
elsif FalseClass === obj
|
73
|
+
print("false")
|
74
|
+
|
75
|
+
elsif String === obj
|
76
|
+
print(escape_json_string(obj))
|
77
|
+
|
78
|
+
elsif Symbol === obj
|
79
|
+
print("\"#{obj}\"")
|
80
|
+
|
81
|
+
elsif Numeric === obj
|
82
|
+
print(obj.to_s)
|
83
|
+
|
84
|
+
elsif Time === obj
|
85
|
+
print(obj.to_s)
|
86
|
+
|
87
|
+
elsif obj.respond_to?(:keys)
|
88
|
+
print("{")
|
89
|
+
indent_out
|
90
|
+
last_key = obj.keys.last
|
91
|
+
obj.each do |(key, val)|
|
92
|
+
render(key)
|
93
|
+
case val
|
94
|
+
when Hash, Array
|
95
|
+
indent_out
|
96
|
+
print(":\n#{indent}")
|
97
|
+
render(val)
|
98
|
+
indent_in
|
99
|
+
else
|
100
|
+
print(": ")
|
101
|
+
render(val)
|
102
|
+
end
|
103
|
+
print(",\n#{indent}") unless key == last_key
|
104
|
+
end
|
105
|
+
indent_in
|
106
|
+
print("}")
|
107
|
+
|
108
|
+
elsif Array === obj
|
109
|
+
print("[")
|
110
|
+
indent_out
|
111
|
+
last_index = obj.size - 1
|
112
|
+
obj.each_with_index do |elem, index|
|
113
|
+
render(elem)
|
114
|
+
print(",\n#{indent}") unless index == last_index
|
115
|
+
end
|
116
|
+
indent_in
|
117
|
+
print("]")
|
118
|
+
|
119
|
+
else
|
120
|
+
raise "unrenderable object: #{obj.inspect}"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Special JSON character escape cases.
|
125
|
+
ESCAPED_CHARS = {
|
126
|
+
"\010" => '\b',
|
127
|
+
"\f" => '\f',
|
128
|
+
"\n" => '\n',
|
129
|
+
"\r" => '\r',
|
130
|
+
"\t" => '\t',
|
131
|
+
'"' => '\"',
|
132
|
+
'\\' => '\\\\',
|
133
|
+
'>' => '\u003E',
|
134
|
+
'<' => '\u003C',
|
135
|
+
'&' => '\u0026'}
|
136
|
+
|
137
|
+
# String#to_json extracted from ActiveSupport, using interpolation for speed.
|
138
|
+
#
|
139
|
+
def escape_json_string(str)
|
140
|
+
begin
|
141
|
+
"\"#{
|
142
|
+
str.gsub(/[\010\f\n\r\t"\\><&]/) { |s| ESCAPED_CHARS[s] }.
|
143
|
+
gsub(/([\xC0-\xDF][\x80-\xBF]|
|
144
|
+
[\xE0-\xEF][\x80-\xBF]{2}|
|
145
|
+
[\xF0-\xF7][\x80-\xBF]{3})+/nx) do |s|
|
146
|
+
s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/, '\\\\u\&')
|
147
|
+
end
|
148
|
+
}\""
|
149
|
+
rescue Encoding::CompatibilityError
|
150
|
+
rawbytes = str.dup.force_encoding 'ASCII-8BIT'
|
151
|
+
escape_json_string(rawbytes)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Neo4jr
|
2
|
+
module ParamHelper
|
3
|
+
|
4
|
+
def param_node_id
|
5
|
+
params.delete('node_id')
|
6
|
+
end
|
7
|
+
|
8
|
+
def param_relationship_type
|
9
|
+
@param_relationship_type ||= params.delete('type')
|
10
|
+
end
|
11
|
+
|
12
|
+
def param_to_node_id
|
13
|
+
params.delete('to')
|
14
|
+
end
|
15
|
+
|
16
|
+
def param_depth
|
17
|
+
(params.delete('depth') || 2).to_i
|
18
|
+
end
|
19
|
+
|
20
|
+
def param_level
|
21
|
+
@param_level ||= (params.delete('level') || 1).to_i
|
22
|
+
end
|
23
|
+
|
24
|
+
def param_direction
|
25
|
+
Neo4jr::Direction.from_string(params.delete('direction') || 'both')
|
26
|
+
end
|
27
|
+
|
28
|
+
def relationship_types
|
29
|
+
(param_relationship_type.nil? ? [] : [param_relationship_type].flatten.map {|name| DynamicRelationshipType.with_name(name)}).to_java(DynamicRelationshipType)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Neo4jr
|
2
|
+
module SelfDocumentor
|
3
|
+
def describe(info)
|
4
|
+
SelfDocumentor.capture info
|
5
|
+
end
|
6
|
+
|
7
|
+
def required_param(*args)
|
8
|
+
SelfDocumentor.required_param(*args)
|
9
|
+
end
|
10
|
+
|
11
|
+
def optional_param(*args)
|
12
|
+
SelfDocumentor.optional_param(*args)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.required_param(*args)
|
16
|
+
(@capture_required_param ||= []) << {args.first => args.last}
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.optional_param(*args)
|
20
|
+
args.unshift(:note) if args.size == 1
|
21
|
+
(@capture_optional_param ||= []) << {args.first => args.last}
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.capture(text)
|
25
|
+
@capture = text
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.route_added(verb, path, proc)
|
29
|
+
if [:get, :post, :put, :delete].include?(verb.downcase.to_sym)
|
30
|
+
document = {:path => path, :description => (@capture || '').to_s}
|
31
|
+
document[:required] = @capture_required_param if @capture_required_param
|
32
|
+
document[:optional] = @capture_optional_param if @capture_optional_param
|
33
|
+
(@@document ||= []) << document
|
34
|
+
@capture_optional_param = nil
|
35
|
+
@capture_required_param = nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.output
|
40
|
+
@@document
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -1,114 +1,144 @@
|
|
1
1
|
module Neo4jr
|
2
2
|
class Service < Sinatra::Base
|
3
|
-
|
3
|
+
helpers ParamHelper
|
4
|
+
register SelfDocumentor, FormatHandler
|
5
|
+
|
6
|
+
describe "Lists all possible request types with descriptions"
|
7
|
+
get '/' do
|
8
|
+
render_for_format(SelfDocumentor.output)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "Returns details about the Neo4j database like the location of the database and the number of nodes."
|
4
12
|
get '/info' do
|
5
13
|
Neo4jr::DB.stats.to_json
|
6
14
|
end
|
7
|
-
|
15
|
+
|
16
|
+
describe "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."
|
8
17
|
get '/nodes' do
|
9
18
|
nodes = Neo4jr::DB.execute do |neo|
|
10
|
-
nodes = neo.all_nodes.map{|m| m.to_hash }
|
19
|
+
nodes = neo.all_nodes.map{|m| m.to_hash }
|
11
20
|
end
|
12
21
|
nodes.to_json
|
13
22
|
end
|
14
|
-
|
23
|
+
|
24
|
+
describe "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."
|
15
25
|
post '/nodes' do
|
16
26
|
node = Neo4jr::DB.execute do |neo|
|
17
27
|
node = neo.create_node(params)
|
18
28
|
end
|
19
29
|
node.to_hash.to_json
|
20
30
|
end
|
21
|
-
|
31
|
+
|
32
|
+
describe "Returns the properties for the specified node, where :node_id is the numeric id for the node."
|
22
33
|
get '/nodes/:node_id' do
|
23
|
-
Neo4jr::DB.
|
34
|
+
node = Neo4jr::DB.execute do |neo|
|
35
|
+
neo.getNodeById(param_node_id).to_hash.to_json
|
36
|
+
end
|
24
37
|
end
|
25
|
-
|
38
|
+
|
39
|
+
describe "Updates the properties of the specified node, where :node_id is the numeric id for the node. Any parameters pased in the body of the PUT will be treated as properties for the node. If you add a new parameters (i.e. age=4) which previously were not on the node, neo4jr-social will still add that property to the node."
|
26
40
|
put '/nodes/:node_id' do
|
27
41
|
node = Neo4jr::DB.execute do |neo|
|
28
|
-
node = neo.getNodeById(
|
42
|
+
node = neo.getNodeById(param_node_id)
|
29
43
|
node.update_properties(params)
|
30
44
|
end
|
31
45
|
node.to_hash.to_json
|
32
46
|
end
|
33
|
-
|
47
|
+
|
48
|
+
describe "Deletes the specified node, where :node_id is the numeric id for the node."
|
34
49
|
delete '/nodes/:node_id' do
|
35
50
|
Neo4jr::DB.execute do |neo|
|
36
|
-
node = neo.getNodeById(
|
51
|
+
node = neo.getNodeById(param_node_id)
|
52
|
+
node.get_relationships.each { |r| r.delete }
|
37
53
|
node.delete
|
38
54
|
end
|
39
55
|
end
|
40
|
-
|
56
|
+
|
57
|
+
describe "Creates a relations for the specified node, where :node_id is the numeric id for the node. This is how you designate how two nodes are related to each other."
|
58
|
+
required_param :to, 'This is the node id of the node you want to make a relationship to. This is a one-way relationship. If you want both nodes to be.'
|
59
|
+
required_param :type, "this is the type of the relationship, i.e. 'friends'. This can be any string that is sensible in your domain."
|
60
|
+
optional_param "Any other parameters you supply in the body of the POST will be added as properties to the relationship. For example if you were making 'friend' relationships and wanted to add a date since the friendship started you could pass a 'since' parameter in the POST."
|
41
61
|
get '/nodes/:node_id/relationships' do
|
42
62
|
relationships = Neo4jr::DB.execute do |neo|
|
43
|
-
node
|
44
|
-
if
|
45
|
-
relationship_type = RelationshipType.instance(
|
46
|
-
node.getRelationships(
|
63
|
+
node = neo.getNodeById(param_node_id)
|
64
|
+
if param_relationship_type
|
65
|
+
relationship_type = RelationshipType.instance(param_relationship_type)
|
66
|
+
node.getRelationships(param_relationship_type.to_a).hashify_objects
|
47
67
|
else
|
48
68
|
node.getRelationships.hashify_objects
|
49
69
|
end
|
50
70
|
end
|
51
71
|
relationships.to_json
|
52
72
|
end
|
53
|
-
|
73
|
+
|
74
|
+
describe "Returns relationships to other nodes for the specified node, where :node_id is the numeric id for the node."
|
75
|
+
optional_param :type, "Specify a type if only certain relationships are of interest"
|
54
76
|
post '/nodes/:node_id/relationships' do
|
55
77
|
relationships = Neo4jr::DB.execute do |neo|
|
56
|
-
node
|
57
|
-
to_node
|
58
|
-
relationship_type = RelationshipType.instance(
|
59
|
-
relationship
|
78
|
+
node = neo.getNodeById(param_node_id)
|
79
|
+
to_node = neo.getNodeById(param_to_node_id)
|
80
|
+
relationship_type = RelationshipType.instance(param_relationship_type)
|
81
|
+
relationship = node.create_relationship_to to_node, relationship_type
|
60
82
|
relationship.update_properties(params)
|
61
83
|
node.getRelationships(relationship_type.to_a).hashify_objects
|
62
84
|
end
|
63
85
|
relationships.to_json
|
64
86
|
end
|
65
87
|
|
88
|
+
describe "This returns all the ways two nodes are connected to each other and is similar to LinkedIn's degrees of separation. Warning: This is only good for sparse graphs, shortest_paths is better at handling larger connected graphs."
|
89
|
+
required_param :to, "the id of the node that your trying to find a path to from the starting node, :node_id"
|
90
|
+
required_param :type, "the type of relationships between nodes to follow"
|
91
|
+
optional_param :depth, "the maximum degrees of separation to find, the default is 2 degrees. Note: There may be performance impacts if this number is to high."
|
92
|
+
optional_param :direction, "hat direction of relationships to follow, the default is 'both'"
|
66
93
|
get '/nodes/:node_id/paths' do
|
67
94
|
paths = Neo4jr::DB.execute do |neo|
|
68
|
-
start_node
|
69
|
-
end_node
|
70
|
-
|
71
|
-
|
72
|
-
direction = Neo4jr::Direction.from_string(params.delete('direction') || 'both')
|
73
|
-
shortest_path = AllSimplePaths.new(start_node, end_node, depth.to_i, direction, relationship.to_a)
|
74
|
-
paths = shortest_path.getPaths
|
75
|
-
paths.map{|p| p.map{|n| n.to_hash }}
|
95
|
+
start_node = neo.getNodeById(param_node_id)
|
96
|
+
end_node = neo.getNodeById(param_to_node_id)
|
97
|
+
shortest_path = AllSimplePaths.new(start_node, end_node, param_depth, param_direction, relationship_types)
|
98
|
+
to_hash shortest_path.getPaths
|
76
99
|
end
|
77
100
|
paths.to_json
|
78
101
|
end
|
79
|
-
|
102
|
+
|
103
|
+
describe "This returns the first of the shortest path of two nodes that are connected to each other"
|
104
|
+
required_param :to, "the id of the node that your trying to find a path to from the starting node, :node_id"
|
105
|
+
required_param :type, "the type of relationships between nodes to follow"
|
80
106
|
get '/nodes/:node_id/shortest_path' do
|
81
107
|
path = Neo4jr::DB.execute do |neo|
|
82
|
-
|
83
|
-
end_node = neo.getNodeById(params.delete('to'))
|
84
|
-
relationship = Neo4jr::RelationshipType.instance(params.delete('type'))
|
85
|
-
dijkstra = Neo4jr::Dijkstra.new(
|
108
|
+
dijkstra = Dijkstra.new(
|
86
109
|
0.0,
|
87
|
-
|
88
|
-
|
110
|
+
neo.getNodeById(param_node_id),
|
111
|
+
neo.getNodeById(param_to_node_id),
|
89
112
|
Neo4jr::SimpleEvaluator.new,
|
90
113
|
DoubleAdder.new,
|
91
114
|
DoubleComparator.new,
|
92
|
-
|
93
|
-
|
94
|
-
dijkstra.getPath.map{|n| n.to_hash }
|
115
|
+
direction,
|
116
|
+
relationship_types)
|
117
|
+
(p=dijkstra.getPath) and p.map{|n| n.to_hash }
|
95
118
|
end
|
96
119
|
path.to_json
|
97
120
|
end
|
98
|
-
|
121
|
+
|
122
|
+
describe "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."
|
123
|
+
required_param :type, "the type of relationships between nodes to follow"
|
124
|
+
optional_param :leve, "the degree of separation that you want recommendations for, the default is 1 degree away which is similar to facebook's behavior"
|
99
125
|
get '/nodes/:node_id/recommendations' do
|
100
126
|
suggestions = Neo4jr::DB.execute do |neo|
|
101
|
-
relationship = Neo4jr::RelationshipType.incoming(
|
102
|
-
start_node
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
current_position.depth > level
|
127
|
+
relationship = Neo4jr::RelationshipType.incoming(param_relationship_type)
|
128
|
+
start_node = neo.getNodeById(param_node_id)
|
129
|
+
order = Order::BREADTH_FIRST
|
130
|
+
return_when = Return.when do |current_position|
|
131
|
+
current_position.depth > param_level
|
107
132
|
end
|
108
|
-
traverser = start_node.traverse(order, Stop.at(
|
133
|
+
traverser = start_node.traverse(order, Stop.at(param_level + 1), return_when, relationship)
|
109
134
|
traverser.map{|node| node.to_hash }
|
110
135
|
end
|
111
136
|
suggestions.to_json
|
112
137
|
end
|
138
|
+
|
139
|
+
private
|
140
|
+
def to_hash paths
|
141
|
+
paths and paths.map{|p| p.map{|n| n.to_hash }}
|
142
|
+
end
|
113
143
|
end
|
114
144
|
end
|
data/lib/neo4jr-social.rb
CHANGED
@@ -1,15 +1,48 @@
|
|
1
1
|
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__)))
|
2
2
|
|
3
|
+
def find_and_require_neo4jr_simple
|
4
|
+
neo4jr_simple_root = java.lang.System.getProperty('neo4jr.simple') || ENV['neo4jr_simple']
|
5
|
+
if neo4jr_simple_root
|
6
|
+
$LOAD_PATH.unshift(File.join(neo4jr_simple_root, 'lib'))
|
7
|
+
else
|
8
|
+
gem 'neo4jr-simple'
|
9
|
+
end
|
10
|
+
require 'neo4jr-simple'
|
11
|
+
end
|
12
|
+
|
13
|
+
def find_and_require_user_defined_code
|
14
|
+
extensions_path = java.lang.System.getProperty('neo4jr.extensions') || ENV['neo4jr_extensions'] || "~/.neo4jr-social"
|
15
|
+
extensions_path = File.expand_path(extensions_path)
|
16
|
+
if File.exists?(extensions_path)
|
17
|
+
Dir.open extensions_path do |dir|
|
18
|
+
dir.entries.each do |file|
|
19
|
+
if file.split('.').size > 1 && file.split('.').last == 'rb'
|
20
|
+
extension = File.join(File.expand_path(extensions_path), file)
|
21
|
+
require(extension) && puts("Loaded Extension: #{extension}")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
else
|
26
|
+
puts "No Extensions Found: #{extensions_path}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
3
30
|
include Java
|
4
31
|
|
5
32
|
require 'rubygems'
|
33
|
+
|
6
34
|
gem 'sinatra'
|
7
|
-
gem 'json_pure'
|
8
|
-
gem 'neo4jr-simple' unless ENV['dev_on_gem']
|
35
|
+
gem 'json_pure'
|
9
36
|
|
10
37
|
require 'sinatra'
|
11
38
|
require 'json'
|
12
|
-
|
13
|
-
|
39
|
+
|
40
|
+
find_and_require_neo4jr_simple
|
41
|
+
|
42
|
+
require 'neo4jr-social/self_documentor'
|
43
|
+
require 'neo4jr-social/param_helper'
|
44
|
+
require 'neo4jr-social/format_handler'
|
45
|
+
require 'neo4jr-social/json_printer'
|
14
46
|
require 'neo4jr-social/service'
|
15
47
|
|
48
|
+
find_and_require_user_defined_code
|
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.2"
|
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{
|
12
|
+
s.date = %q{2010-01-19}
|
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}
|
@@ -29,6 +29,7 @@ Gem::Specification.new do |s|
|
|
29
29
|
"VERSION",
|
30
30
|
"bin/start-neo4jr-social",
|
31
31
|
"config.ru",
|
32
|
+
"config/warble.rb",
|
32
33
|
"examples/facebook.rb",
|
33
34
|
"examples/linkedin.rb",
|
34
35
|
"jetty-runtime/etc/jetty.xml",
|
@@ -43,20 +44,17 @@ Gem::Specification.new do |s|
|
|
43
44
|
"jetty-runtime/start.jar",
|
44
45
|
"jetty-runtime/webapps/neo4jr-social.war",
|
45
46
|
"lib/neo4jr-social.rb",
|
47
|
+
"lib/neo4jr-social/format_handler.rb",
|
48
|
+
"lib/neo4jr-social/json_printer.rb",
|
49
|
+
"lib/neo4jr-social/param_helper.rb",
|
50
|
+
"lib/neo4jr-social/self_documentor.rb",
|
46
51
|
"lib/neo4jr-social/service.rb",
|
47
|
-
"lib/neo4jr-social/simple_cost_evaluator.rb",
|
48
|
-
"lib/neo4jr-social/version.rb",
|
49
52
|
"neo4jr-social.gemspec",
|
53
|
+
"spec/format_handler_spec.rb",
|
54
|
+
"spec/json_printer_spec.rb",
|
50
55
|
"spec/service_spec.rb",
|
51
56
|
"spec/spec.opts",
|
52
|
-
"spec/spec_helper.rb"
|
53
|
-
"tmp/war/WEB-INF/lib/jruby-core-1.4.0.jar",
|
54
|
-
"tmp/war/WEB-INF/lib/jruby-rack-0.9.5.jar",
|
55
|
-
"tmp/war/WEB-INF/lib/jruby-stdlib-1.4.0.jar",
|
56
|
-
"tmp/war/WEB-INF/lib/neo4jr-social.rb",
|
57
|
-
"tmp/war/WEB-INF/lib/neo4jr-social/service.rb",
|
58
|
-
"tmp/war/WEB-INF/lib/neo4jr-social/version.rb",
|
59
|
-
"tmp/war/WEB-INF/web.xml"
|
57
|
+
"spec/spec_helper.rb"
|
60
58
|
]
|
61
59
|
s.homepage = %q{http://github.com/mdeiters/neo4jr-social}
|
62
60
|
s.rdoc_options = ["--charset=UTF-8"]
|
@@ -64,7 +62,9 @@ Gem::Specification.new do |s|
|
|
64
62
|
s.rubygems_version = %q{1.3.5}
|
65
63
|
s.summary = %q{A self-containted and lightweight REST interface to Neo4j using JRuby.}
|
66
64
|
s.test_files = [
|
67
|
-
"spec/
|
65
|
+
"spec/format_handler_spec.rb",
|
66
|
+
"spec/json_printer_spec.rb",
|
67
|
+
"spec/service_spec.rb",
|
68
68
|
"spec/spec_helper.rb",
|
69
69
|
"examples/facebook.rb",
|
70
70
|
"examples/linkedin.rb"
|
@@ -77,20 +77,17 @@ Gem::Specification.new do |s|
|
|
77
77
|
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
78
78
|
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
79
79
|
s.add_development_dependency(%q<rest-client>, [">= 0"])
|
80
|
-
s.add_runtime_dependency(%q<neo4jr-simple>, [">= 0.1.6"])
|
81
80
|
s.add_runtime_dependency(%q<sinatra>, [">= 0"])
|
82
81
|
s.add_runtime_dependency(%q<json_pure>, [">= 0"])
|
83
82
|
else
|
84
83
|
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
85
84
|
s.add_dependency(%q<rest-client>, [">= 0"])
|
86
|
-
s.add_dependency(%q<neo4jr-simple>, [">= 0.1.6"])
|
87
85
|
s.add_dependency(%q<sinatra>, [">= 0"])
|
88
86
|
s.add_dependency(%q<json_pure>, [">= 0"])
|
89
87
|
end
|
90
88
|
else
|
91
89
|
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
92
90
|
s.add_dependency(%q<rest-client>, [">= 0"])
|
93
|
-
s.add_dependency(%q<neo4jr-simple>, [">= 0.1.6"])
|
94
91
|
s.add_dependency(%q<sinatra>, [">= 0"])
|
95
92
|
s.add_dependency(%q<json_pure>, [">= 0"])
|
96
93
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe Neo4jr::FormatHandler do
|
4
|
+
|
5
|
+
it 'returns pure json by default' do
|
6
|
+
get '/'
|
7
|
+
last_response.body.should_not include(html_space = ' ')
|
8
|
+
last_response.headers['Content-Type'].should include('application/json')
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'returns htmlized json if a browser request' do
|
12
|
+
get '/', {}, {'HTTP_ACCEPT' => 'text/html'}
|
13
|
+
last_response.body.should include(html_space = ' ')
|
14
|
+
last_response.headers['Content-Type'].should include('text/html')
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'always returns utf compliant text' do
|
18
|
+
get '/'
|
19
|
+
last_response.headers['Content-Type'].should include('charset=utf-8')
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "JsonPrinter" do
|
4
|
+
describe ".render" do
|
5
|
+
it "should raise an error for invalid objects" do
|
6
|
+
lambda {
|
7
|
+
JsonPrinter.render(Object.new)
|
8
|
+
}.should raise_error
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should render nil with null" do
|
12
|
+
JsonPrinter.render(nil).should == %{null}
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should render true with true" do
|
16
|
+
JsonPrinter.render(true).should == %{true}
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should render false with false" do
|
20
|
+
JsonPrinter.render(false).should == %{false}
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should render JSON-escaped strings" do
|
24
|
+
JsonPrinter.render(%{foo&}).should == %{"foo\\u0026"}
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should render symbols as strings" do
|
28
|
+
JsonPrinter.render(:foo).should == %{"foo"}
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should render numbers" do
|
32
|
+
JsonPrinter.render(7).should == %{7}
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should render times" do
|
36
|
+
t = Time.at(946702800).utc
|
37
|
+
JsonPrinter.render(t).should == "Sat Jan 01 05:00:00 UTC 2000"
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should render arrays" do
|
41
|
+
JsonPrinter.render([:foo, :bar, :bat]).should ==
|
42
|
+
%{["foo",\n} <<
|
43
|
+
%{ "bar",\n} <<
|
44
|
+
%{ "bat"]}
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should render arrays with repeated values" do
|
48
|
+
JsonPrinter.render([:foo, :bar, :foo]).should ==
|
49
|
+
%{["foo",\n} <<
|
50
|
+
%{ "bar",\n} <<
|
51
|
+
%{ "foo"]}
|
52
|
+
end
|
53
|
+
|
54
|
+
# TODO: would be nice to spec the actual output formatting
|
55
|
+
|
56
|
+
it "should render hashes" do
|
57
|
+
d = {"foo" => "bar", "biz" => "bat", "bing" => "bong"}
|
58
|
+
JSON.parse(JsonPrinter.render(d)).should == d
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should render hashes with repeated values" do
|
62
|
+
d = {"foo" => "bar", "biz" => "bar", "bing" => "bar"}
|
63
|
+
JSON.parse(JsonPrinter.render(d)).should == d
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "on compound data structures" do
|
67
|
+
before :each do
|
68
|
+
@d =
|
69
|
+
["foo",
|
70
|
+
"bar",
|
71
|
+
["biz",
|
72
|
+
"bang",
|
73
|
+
{"biz" => "bat",
|
74
|
+
"bang" => "yang"},
|
75
|
+
"zoo"],
|
76
|
+
"bat",
|
77
|
+
{"bang" => "str",
|
78
|
+
"comp" =>
|
79
|
+
["foo",
|
80
|
+
"bar",
|
81
|
+
"bat"],
|
82
|
+
"hash" =>
|
83
|
+
{"from" => "to",
|
84
|
+
"next" => "best"}},
|
85
|
+
"fin",
|
86
|
+
"end"]
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should render" do
|
90
|
+
JSON.parse(JsonPrinter.render(@d)).should == @d
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/spec/service_spec.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
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'
|
6
|
-
|
7
4
|
require 'neo4jr-social'
|
8
5
|
require 'spec'
|
9
6
|
require 'spec/autorun'
|
10
7
|
require 'rack/test'
|
11
8
|
|
9
|
+
# Neo4jr::Configuration.database_path = File.join(File.expand_path(File.dirname(__FILE__)), 'test-imdb-database')
|
10
|
+
# puts Neo4jr::Configuration.database_path
|
11
|
+
|
12
12
|
Spec::Runner.configure do |config|
|
13
13
|
include Rack::Test::Methods
|
14
14
|
|
@@ -18,5 +18,5 @@ Spec::Runner.configure do |config|
|
|
18
18
|
|
19
19
|
def response_to_ruby
|
20
20
|
JSON.parse(last_response.body)
|
21
|
-
end
|
21
|
+
end
|
22
22
|
end
|
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.2
|
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:
|
12
|
+
date: 2010-01-19 00:00:00 -08:00
|
13
13
|
default_executable: start-neo4jr-social
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -32,16 +32,6 @@ dependencies:
|
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: "0"
|
34
34
|
version:
|
35
|
-
- !ruby/object:Gem::Dependency
|
36
|
-
name: neo4jr-simple
|
37
|
-
type: :runtime
|
38
|
-
version_requirement:
|
39
|
-
version_requirements: !ruby/object:Gem::Requirement
|
40
|
-
requirements:
|
41
|
-
- - ">="
|
42
|
-
- !ruby/object:Gem::Version
|
43
|
-
version: 0.1.6
|
44
|
-
version:
|
45
35
|
- !ruby/object:Gem::Dependency
|
46
36
|
name: sinatra
|
47
37
|
type: :runtime
|
@@ -82,6 +72,7 @@ files:
|
|
82
72
|
- VERSION
|
83
73
|
- bin/start-neo4jr-social
|
84
74
|
- config.ru
|
75
|
+
- config/warble.rb
|
85
76
|
- examples/facebook.rb
|
86
77
|
- examples/linkedin.rb
|
87
78
|
- jetty-runtime/etc/jetty.xml
|
@@ -96,20 +87,17 @@ files:
|
|
96
87
|
- jetty-runtime/start.jar
|
97
88
|
- jetty-runtime/webapps/neo4jr-social.war
|
98
89
|
- lib/neo4jr-social.rb
|
90
|
+
- lib/neo4jr-social/format_handler.rb
|
91
|
+
- lib/neo4jr-social/json_printer.rb
|
92
|
+
- lib/neo4jr-social/param_helper.rb
|
93
|
+
- lib/neo4jr-social/self_documentor.rb
|
99
94
|
- lib/neo4jr-social/service.rb
|
100
|
-
- lib/neo4jr-social/simple_cost_evaluator.rb
|
101
|
-
- lib/neo4jr-social/version.rb
|
102
95
|
- neo4jr-social.gemspec
|
96
|
+
- spec/format_handler_spec.rb
|
97
|
+
- spec/json_printer_spec.rb
|
103
98
|
- spec/service_spec.rb
|
104
99
|
- spec/spec.opts
|
105
100
|
- spec/spec_helper.rb
|
106
|
-
- tmp/war/WEB-INF/lib/jruby-core-1.4.0.jar
|
107
|
-
- tmp/war/WEB-INF/lib/jruby-rack-0.9.5.jar
|
108
|
-
- tmp/war/WEB-INF/lib/jruby-stdlib-1.4.0.jar
|
109
|
-
- tmp/war/WEB-INF/lib/neo4jr-social.rb
|
110
|
-
- tmp/war/WEB-INF/lib/neo4jr-social/service.rb
|
111
|
-
- tmp/war/WEB-INF/lib/neo4jr-social/version.rb
|
112
|
-
- tmp/war/WEB-INF/web.xml
|
113
101
|
has_rdoc: true
|
114
102
|
homepage: http://github.com/mdeiters/neo4jr-social
|
115
103
|
licenses: []
|
@@ -139,6 +127,8 @@ signing_key:
|
|
139
127
|
specification_version: 3
|
140
128
|
summary: A self-containted and lightweight REST interface to Neo4j using JRuby.
|
141
129
|
test_files:
|
130
|
+
- spec/format_handler_spec.rb
|
131
|
+
- spec/json_printer_spec.rb
|
142
132
|
- spec/service_spec.rb
|
143
133
|
- spec/spec_helper.rb
|
144
134
|
- examples/facebook.rb
|
Binary file
|
Binary file
|
Binary file
|
@@ -1,97 +0,0 @@
|
|
1
|
-
module Neo4jr
|
2
|
-
class Service < Sinatra::Base
|
3
|
-
|
4
|
-
get '/info' do
|
5
|
-
Neo4jr::DB.stats.to_json
|
6
|
-
end
|
7
|
-
|
8
|
-
get '/nodes' do
|
9
|
-
nodes = Neo4jr::DB.execute do |neo|
|
10
|
-
nodes = neo.all_nodes.map{|m| m.to_hash }
|
11
|
-
end
|
12
|
-
nodes.to_json
|
13
|
-
end
|
14
|
-
|
15
|
-
post '/nodes' do
|
16
|
-
node = Neo4jr::DB.execute do |neo|
|
17
|
-
node = neo.create_node(params)
|
18
|
-
end
|
19
|
-
node.to_hash.to_json
|
20
|
-
end
|
21
|
-
|
22
|
-
get '/nodes/:node_id' do
|
23
|
-
Neo4jr::DB.getNodeById(params.delete('node_id')).to_json
|
24
|
-
end
|
25
|
-
|
26
|
-
put '/nodes/:node_id' do
|
27
|
-
node = Neo4jr::DB.execute do |neo|
|
28
|
-
node = neo.getNodeById(params.delete('node_id'))
|
29
|
-
node.update_properties(params)
|
30
|
-
end
|
31
|
-
node.to_hash.to_json
|
32
|
-
end
|
33
|
-
|
34
|
-
delete '/nodes/:node_id' do
|
35
|
-
Neo4jr::DB.execute do |neo|
|
36
|
-
node = neo.getNodeById(params['node_id'])
|
37
|
-
node.delete
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
get '/nodes/:node_id/relationships' do
|
42
|
-
relationships = Neo4jr::DB.execute do |neo|
|
43
|
-
node = neo.getNodeById(params.delete('node_id'))
|
44
|
-
if relationship_type = params.delete('type')
|
45
|
-
relationship_type = RelationshipType.instance(relationship_type)
|
46
|
-
node.getRelationships(relationship_type.to_a).hashify_objects
|
47
|
-
else
|
48
|
-
node.getRelationships.hashify_objects
|
49
|
-
end
|
50
|
-
end
|
51
|
-
relationships.to_json
|
52
|
-
end
|
53
|
-
|
54
|
-
post '/nodes/:node_id/relationships' do
|
55
|
-
relationships = Neo4jr::DB.execute do |neo|
|
56
|
-
node = neo.getNodeById(params.delete('node_id'))
|
57
|
-
to_node = neo.getNodeById(params.delete('to'))
|
58
|
-
relationship_type = RelationshipType.instance(params.delete('type'))
|
59
|
-
relationship = node.create_relationship_to to_node, relationship_type
|
60
|
-
relationship.update_properties(params)
|
61
|
-
node.getRelationships(relationship_type.to_a).hashify_objects
|
62
|
-
end
|
63
|
-
relationships.to_json
|
64
|
-
end
|
65
|
-
|
66
|
-
#optional direction & depth
|
67
|
-
get '/nodes/:node_id/path' do
|
68
|
-
paths = Neo4jr::DB.execute do |neo|
|
69
|
-
relationship = Neo4jr::RelationshipType.instance(params.delete('type'))
|
70
|
-
start_node = neo.getNodeById(params.delete('node_id'))
|
71
|
-
end_node = neo.getNodeById(params.delete('to'))
|
72
|
-
depth = params.delete('depth') || 2
|
73
|
-
direction = Neo4jr::Direction.from_string(params.delete('direction') || 'both')
|
74
|
-
shortest_path = AllSimplePaths.new(start_node, end_node, depth.to_i, direction, relationship.to_a)
|
75
|
-
paths = shortest_path.getPaths
|
76
|
-
paths.map{|p| p.map{|n| n.to_hash }}
|
77
|
-
end
|
78
|
-
paths.to_json
|
79
|
-
end
|
80
|
-
|
81
|
-
#optional
|
82
|
-
get '/nodes/:node_id/recommendations' do
|
83
|
-
suggestions = Neo4jr::DB.execute do |neo|
|
84
|
-
relationship = Neo4jr::RelationshipType.incoming(params.delete('type'))
|
85
|
-
start_node = neo.getNodeById(params.delete('node_id'))
|
86
|
-
level = (params.delete('level') || 1).to_i
|
87
|
-
order = Order::BREADTH_FIRST
|
88
|
-
return_when = Return.when do |current_position|
|
89
|
-
current_position.depth > level
|
90
|
-
end
|
91
|
-
traverser = start_node.traverse(order, Stop.at(level + 1), return_when, relationship)
|
92
|
-
traverser.map{|node| node.to_hash }
|
93
|
-
end
|
94
|
-
suggestions.to_json
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
@@ -1,14 +0,0 @@
|
|
1
|
-
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__)))
|
2
|
-
|
3
|
-
include Java
|
4
|
-
|
5
|
-
require 'rubygems'
|
6
|
-
gem 'sinatra'
|
7
|
-
gem 'json_pure'
|
8
|
-
gem 'neo4jr-simple'
|
9
|
-
|
10
|
-
require 'sinatra'
|
11
|
-
require 'json'
|
12
|
-
require 'neo4jr-simple'
|
13
|
-
require 'neo4jr-social/service'
|
14
|
-
|
data/tmp/war/WEB-INF/web.xml
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
<!DOCTYPE web-app PUBLIC
|
2
|
-
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
|
3
|
-
"http://java.sun.com/dtd/web-app_2_3.dtd">
|
4
|
-
<web-app>
|
5
|
-
|
6
|
-
<context-param>
|
7
|
-
<param-name>rails.env</param-name>
|
8
|
-
<param-value>production</param-value>
|
9
|
-
</context-param>
|
10
|
-
|
11
|
-
<context-param>
|
12
|
-
<param-name>public.root</param-name>
|
13
|
-
<param-value>/</param-value>
|
14
|
-
</context-param>
|
15
|
-
|
16
|
-
<context-param>
|
17
|
-
<param-name>rackup</param-name>
|
18
|
-
<param-value>require 'lib/neo4jr-social'
|
19
|
-
|
20
|
-
run Neo4jr::Service</param-value>
|
21
|
-
</context-param>
|
22
|
-
|
23
|
-
|
24
|
-
<filter>
|
25
|
-
<filter-name>RackFilter</filter-name>
|
26
|
-
<filter-class>org.jruby.rack.RackFilter</filter-class>
|
27
|
-
</filter>
|
28
|
-
<filter-mapping>
|
29
|
-
<filter-name>RackFilter</filter-name>
|
30
|
-
<url-pattern>/*</url-pattern>
|
31
|
-
</filter-mapping>
|
32
|
-
|
33
|
-
<listener>
|
34
|
-
<listener-class>org.jruby.rack.RackServletContextListener</listener-class>
|
35
|
-
</listener>
|
36
|
-
|
37
|
-
|
38
|
-
</web-app>
|