neo4j 1.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. data/CHANGELOG +141 -0
  2. data/CONTRIBUTORS +15 -0
  3. data/Gemfile +3 -0
  4. data/README.rdoc +2015 -0
  5. data/lib/neo4j.old/batch_inserter.rb +144 -0
  6. data/lib/neo4j.old/config.rb +138 -0
  7. data/lib/neo4j.old/event_handler.rb +73 -0
  8. data/lib/neo4j.old/extensions/activemodel.rb +158 -0
  9. data/lib/neo4j.old/extensions/aggregate.rb +12 -0
  10. data/lib/neo4j.old/extensions/aggregate/aggregate_enum.rb +40 -0
  11. data/lib/neo4j.old/extensions/aggregate/ext/node_mixin.rb +69 -0
  12. data/lib/neo4j.old/extensions/aggregate/node_aggregate.rb +8 -0
  13. data/lib/neo4j.old/extensions/aggregate/node_aggregate_mixin.rb +331 -0
  14. data/lib/neo4j.old/extensions/aggregate/node_aggregator.rb +216 -0
  15. data/lib/neo4j.old/extensions/aggregate/node_group.rb +43 -0
  16. data/lib/neo4j.old/extensions/aggregate/prop_group.rb +30 -0
  17. data/lib/neo4j.old/extensions/aggregate/property_enum.rb +24 -0
  18. data/lib/neo4j.old/extensions/aggregate/props_aggregate.rb +8 -0
  19. data/lib/neo4j.old/extensions/aggregate/props_aggregate_mixin.rb +31 -0
  20. data/lib/neo4j.old/extensions/aggregate/props_aggregator.rb +80 -0
  21. data/lib/neo4j.old/extensions/find_path.rb +117 -0
  22. data/lib/neo4j.old/extensions/graph_algo.rb +1 -0
  23. data/lib/neo4j.old/extensions/graph_algo/all_simple_paths.rb +133 -0
  24. data/lib/neo4j.old/extensions/graph_algo/neo4j-graph-algo-0.3.jar +0 -0
  25. data/lib/neo4j.old/extensions/reindexer.rb +104 -0
  26. data/lib/neo4j.old/extensions/rest.rb +21 -0
  27. data/lib/neo4j.old/extensions/rest/rest.rb +336 -0
  28. data/lib/neo4j.old/extensions/rest/rest_mixin.rb +193 -0
  29. data/lib/neo4j.old/extensions/rest/server.rb +50 -0
  30. data/lib/neo4j.old/extensions/rest/stubs.rb +141 -0
  31. data/lib/neo4j.old/extensions/rest_master.rb +34 -0
  32. data/lib/neo4j.old/extensions/rest_slave.rb +31 -0
  33. data/lib/neo4j.old/extensions/tx_tracker.rb +392 -0
  34. data/lib/neo4j.old/indexer.rb +187 -0
  35. data/lib/neo4j.old/jars.rb +6 -0
  36. data/lib/neo4j.old/jars/geronimo-jta_1.1_spec-1.1.1.jar +0 -0
  37. data/lib/neo4j.old/jars/neo4j-kernel-1.0.jar +0 -0
  38. data/lib/neo4j.old/mixins/java_list_mixin.rb +139 -0
  39. data/lib/neo4j.old/mixins/java_node_mixin.rb +205 -0
  40. data/lib/neo4j.old/mixins/java_property_mixin.rb +169 -0
  41. data/lib/neo4j.old/mixins/java_relationship_mixin.rb +60 -0
  42. data/lib/neo4j.old/mixins/migration_mixin.rb +157 -0
  43. data/lib/neo4j.old/mixins/node_mixin.rb +249 -0
  44. data/lib/neo4j.old/mixins/property_class_methods.rb +265 -0
  45. data/lib/neo4j.old/mixins/rel_class_methods.rb +167 -0
  46. data/lib/neo4j.old/mixins/relationship_mixin.rb +103 -0
  47. data/lib/neo4j.old/neo.rb +247 -0
  48. data/lib/neo4j.old/node.rb +49 -0
  49. data/lib/neo4j.old/reference_node.rb +15 -0
  50. data/lib/neo4j.old/relationship.rb +85 -0
  51. data/lib/neo4j.old/relationships/decl_relationship_dsl.rb +164 -0
  52. data/lib/neo4j.old/relationships/has_list.rb +101 -0
  53. data/lib/neo4j.old/relationships/has_n.rb +129 -0
  54. data/lib/neo4j.old/relationships/node_traverser.rb +138 -0
  55. data/lib/neo4j.old/relationships/relationship_dsl.rb +149 -0
  56. data/lib/neo4j.old/relationships/traversal_position.rb +50 -0
  57. data/lib/neo4j.old/relationships/wrappers.rb +51 -0
  58. data/lib/neo4j.old/search_result.rb +72 -0
  59. data/lib/neo4j.old/transaction.rb +254 -0
  60. data/lib/neo4j.old/version.rb +3 -0
  61. data/lib/neo4j.rb +50 -0
  62. data/lib/neo4j/config.rb +137 -0
  63. data/lib/neo4j/database.rb +43 -0
  64. data/lib/neo4j/equal.rb +22 -0
  65. data/lib/neo4j/event_handler.rb +91 -0
  66. data/lib/neo4j/index.rb +157 -0
  67. data/lib/neo4j/jars/geronimo-jta_1.1_spec-1.1.1.jar +0 -0
  68. data/lib/neo4j/jars/lucene-core-2.9.2.jar +0 -0
  69. data/lib/neo4j/jars/lucene-core-3.0.1.jar +0 -0
  70. data/lib/neo4j/jars/neo4j-index-1.1.jar +0 -0
  71. data/lib/neo4j/jars/neo4j-kernel-1.1.1.jar +0 -0
  72. data/lib/neo4j/jars/neo4j-kernel-1.1.jar +0 -0
  73. data/lib/neo4j/jars/neo4j-lucene-index-0.1-20100916.085626-67.jar +0 -0
  74. data/lib/neo4j/mapping/class_methods/index.rb +21 -0
  75. data/lib/neo4j/mapping/class_methods/property.rb +139 -0
  76. data/lib/neo4j/mapping/class_methods/relationship.rb +96 -0
  77. data/lib/neo4j/mapping/class_methods/rule.rb +135 -0
  78. data/lib/neo4j/mapping/decl_relationship_dsl.rb +151 -0
  79. data/lib/neo4j/mapping/has_n.rb +117 -0
  80. data/lib/neo4j/mapping/node_mixin.rb +70 -0
  81. data/lib/neo4j/neo4j.rb +65 -0
  82. data/lib/neo4j/node.rb +82 -0
  83. data/lib/neo4j/node_mixin.rb +4 -0
  84. data/lib/neo4j/node_relationship.rb +60 -0
  85. data/lib/neo4j/node_traverser.rb +141 -0
  86. data/lib/neo4j/property.rb +72 -0
  87. data/lib/neo4j/rails/lucene_connection_closer.rb +19 -0
  88. data/lib/neo4j/rails/model.rb +210 -0
  89. data/lib/neo4j/rails/railtie.rb +16 -0
  90. data/lib/neo4j/rails/transaction.rb +29 -0
  91. data/lib/neo4j/rails/value.rb +43 -0
  92. data/lib/neo4j/relationship.rb +88 -0
  93. data/lib/neo4j/relationship_traverser.rb +57 -0
  94. data/lib/neo4j/to_java.rb +17 -0
  95. data/lib/neo4j/transaction.rb +69 -0
  96. data/lib/neo4j/version.rb +3 -0
  97. data/neo4j.gemspec +30 -0
  98. metadata +243 -0
@@ -0,0 +1 @@
1
+ require 'neo4j/extensions/graph_algo/all_simple_paths'
@@ -0,0 +1,133 @@
1
+ module Neo4j::GraphAlgo
2
+ require 'neo4j/extensions/graph_algo/neo4j-graph-algo-0.3.jar'
3
+
4
+
5
+ class ListOfAlternatingNodesAndRelationships #:nodoc:
6
+ include Enumerable
7
+
8
+ def initialize(list)
9
+ @list = list
10
+ end
11
+
12
+ def each
13
+ iter = @list.iterator
14
+ node = true
15
+ while (iter.hasNext)
16
+ id = iter.next.getId
17
+ if (node)
18
+ yield Neo4j.load_node(id)
19
+ else
20
+ yield Neo4j.load_rel(id)
21
+ end
22
+ node = !node
23
+ end
24
+ end
25
+ end
26
+
27
+ class ListOfNodes #:nodoc:
28
+ include Enumerable
29
+
30
+ def initialize(list)
31
+ @list = list
32
+ end
33
+
34
+ def size
35
+ @list.size
36
+ end
37
+
38
+ def each
39
+ iter = @list.iterator
40
+ while (iter.hasNext)
41
+ n = iter.next
42
+ yield Neo4j.load_node(n.getId)
43
+ end
44
+ end
45
+ end
46
+
47
+ # A Wrapper for some of the neo4j graphdb algorithms
48
+ #
49
+ # Currently only the AllSimplePaths is wrapped in Ruby.
50
+ #
51
+ # === Usage
52
+ #
53
+ # found_nodes = GraphAlgo.all_simple_paths.from(node1).both(:knows).to(node7).depth(4).as_nodes
54
+ #
55
+ # === See also
56
+ # * JavaDoc: http://components.neo4j.org/graph-algo/apidocs/org/neo4j/graphalgo/AllSimplePaths.html
57
+ # * A complete example: http://github.com/andreasronge/neo4j/tree/master/examples/you_might_know/
58
+ #
59
+ class AllSimplePaths
60
+ include Enumerable
61
+
62
+ def initialize
63
+ @types = []
64
+ @direction = org.neo4j.graphdb.Direction::OUTGOING
65
+ end
66
+
67
+ def each
68
+ if @as_nodes
69
+ iter = paths.get_paths_as_nodes.iterator
70
+ while (iter.has_next) do yield ListOfNodes.new(iter.next) end
71
+ else
72
+ iter = paths.get_paths.iterator
73
+ while (iter.has_next) do yield ListOfAlternatingNodesAndRelationships.new(iter.next) end
74
+ end
75
+ end
76
+
77
+ def as_nodes
78
+ @as_nodes = true
79
+ self
80
+ end
81
+
82
+ def size
83
+ paths.get_paths_as_nodes.size
84
+ end
85
+
86
+ def paths
87
+ @paths ||= org.neo4j.graphalgo.AllSimplePaths.new(@from._java_node, @to._java_node, @depth, @direction, @types.to_java(:"org.neo4j.graphdb.RelationshipType"))
88
+ end
89
+
90
+ def from(f)
91
+ @from = f
92
+ self
93
+ end
94
+
95
+ def to(t)
96
+ @to = t
97
+ self
98
+ end
99
+
100
+ def depth(d)
101
+ @depth = d
102
+ self
103
+ end
104
+
105
+ def both(*types)
106
+ types.each { |type| @types << org.neo4j.graphdb.DynamicRelationshipType.withName(type.to_s) }
107
+ @direction = org.neo4j.graphdb.Direction::BOTH
108
+ self
109
+ end
110
+
111
+ def outgoing(*types)
112
+ types.each { |type| @types << org.neo4j.graphdb.DynamicRelationshipType.withName(type.to_s) }
113
+ @direction = org.neo4j.graphdb.Direction::OUTGOING
114
+ self
115
+ end
116
+
117
+ def incoming(*types)
118
+ types.each { |type| @types << org.neo4j.graphdb.DynamicRelationshipType.withName(type.to_s) }
119
+ @direction = org.neo4j.graphdb.Direction::INCOMING
120
+ self
121
+ end
122
+
123
+ end
124
+
125
+ def self.all_simple_paths
126
+ # org.neo4j.graphdb.Node node1, org.neo4j.graphdb.Node node2, int maximumTotalDepth, org.neo4j.graphdb.Direction relationshipDirection, org.neo4j.graphdb.RelationshipType... relationshipTypes)
127
+ AllSimplePaths.new
128
+ end
129
+
130
+
131
+ end
132
+
133
+
@@ -0,0 +1,104 @@
1
+ module Neo4j
2
+
3
+ module PropertyClassMethods
4
+ # Traverse all nodes and update the lucene index.
5
+ # Can be used for example if it's' necessary to change the index on a class
6
+ #
7
+ def update_index
8
+ all.nodes.each do |n|
9
+ n.update_index
10
+ end
11
+ end
12
+
13
+ # Returns node instances of this class.
14
+ #
15
+ def all
16
+ index_node = IndexNode.instance
17
+ index_node.rels.outgoing(self)
18
+ end
19
+ end
20
+
21
+ module NodeMixin
22
+ alias_method :ignore_incoming_cascade_delete_orig?, :ignore_incoming_cascade_delete?
23
+ def ignore_incoming_cascade_delete? (relationship)
24
+ # if it's an index node relationship then it should be allowed to cascade delete the node
25
+ ignore_incoming_cascade_delete_orig?(relationship) || relationship.other_node(self) == IndexNode.instance
26
+ end
27
+
28
+ end
29
+
30
+
31
+ class IndexNode
32
+ include NodeMixin
33
+
34
+ # Connects the given node with the reference node.
35
+ # The type of the relationship will be the same as the class name of the
36
+ # specified node unless the optional parameter type is specified.
37
+ # This method is used internally to keep a reference to all node instances in the node space
38
+ # (useful for example for reindexing all nodes by traversing the node space).
39
+ #
40
+ # ==== Parameters
41
+ # node<Neo4j::NodeMixin>:: Connect the reference node with this node
42
+ # type<String>:: Optional, the type of the relationship we want to create
43
+ #
44
+ # ==== Returns
45
+ # nil
46
+ #
47
+ # :api: private
48
+ def connect(node, type = node.class.root_class)
49
+ rtype = org.neo4j.graphdb.DynamicRelationshipType.withName(type.to_s)
50
+ @_java_node.createRelationshipTo(node._java_node, rtype)
51
+ nil
52
+ end
53
+
54
+ def on_node_created(node)
55
+ # we have to avoid connecting to our self
56
+ unless self == node
57
+ node.class.ancestors.grep(Class).each{|p| connect(node, p) if p.respond_to?(:all)}
58
+ end
59
+ end
60
+
61
+ def self.on_neo_started(neo_instance)
62
+ if neo_instance.ref_node.rel?(:index_node)
63
+ # we already have it, put it in instance variable so we do not have to look again
64
+ @index_node = neo_instance.ref_node.rels.outgoing(:index_node).nodes.first
65
+ else
66
+ @index_node = IndexNode.new # cache this so we do not have to look it up always
67
+ neo_instance.ref_node.rels.outgoing(:index_node) << @index_node
68
+ end
69
+ Neo4j.event_handler.add(@index_node)
70
+ end
71
+
72
+ def self.on_neo_stopped(neo_instance)
73
+ # unregister the instance
74
+ Neo4j.event_handler.remove(@index_node)
75
+ @index_node = nil
76
+ end
77
+
78
+ def self.instance
79
+ Neo4j.start if @index_node.nil?
80
+ @index_node
81
+ end
82
+
83
+ def self.instance?
84
+ !@index_node.nil?
85
+ end
86
+
87
+ end
88
+
89
+
90
+ # Add this so it can add it self as listener
91
+ def self.load_reindexer
92
+ Neo4j.event_handler.add(IndexNode)
93
+ # in case we already have started
94
+ Neo4j::Transaction.run { IndexNode.on_neo_started(Neo4j) } if Neo4j.running?
95
+ end
96
+
97
+ def self.unload_reindexer
98
+ Neo4j.event_handler.remove(IndexNode)
99
+ Neo4j.event_handler.remove(IndexNode.instance) if IndexNode.instance?
100
+ end
101
+
102
+
103
+ load_reindexer
104
+ end
@@ -0,0 +1,21 @@
1
+ require 'net/http'
2
+ require 'thread'
3
+ require 'json'
4
+ require 'sinatra/base'
5
+
6
+ require 'neo4j/extensions/rest/rest'
7
+ require 'neo4j/extensions/rest/rest_mixin'
8
+ require 'neo4j/extensions/rest/stubs'
9
+ require 'neo4j/extensions/rest/server'
10
+
11
+ # Provides Neo4j::NodeMixin::ClassMethods#all
12
+ require 'neo4j/extensions/reindexer'
13
+
14
+
15
+ module Neo4j
16
+ # Make the ReferenceNode available as a REST resource
17
+ # not possible to do much without that node being exposed ...
18
+ class ReferenceNode
19
+ include Neo4j::RestMixin
20
+ end
21
+ end
@@ -0,0 +1,336 @@
1
+ module Neo4j
2
+
3
+ module Rest #:nodoc: all
4
+ # contains a list of rest node class resources
5
+ REST_NODE_CLASSES = {}
6
+
7
+ class RestException < StandardError
8
+ def code;
9
+ 500;
10
+ end
11
+ end
12
+
13
+ class NotAllowed < RestException
14
+ def code;
15
+ 403;
16
+ end
17
+ end
18
+
19
+ class Conflict < RestException
20
+ def code;
21
+ 409;
22
+ end
23
+ end
24
+
25
+ def self.base_uri
26
+ host = Sinatra::Application.host
27
+ port = Sinatra::Application.port
28
+ "http://#{host}:#{port}"
29
+ end
30
+
31
+ def self.load_class(clazz)
32
+ clazz.split("::").inject(Kernel) do |container, name|
33
+ container.const_get(name.to_s)
34
+ end
35
+ rescue NameError
36
+ raise Sinatra::NotFound
37
+ end
38
+
39
+
40
+ # Extracts query parameters from a URL; e.g. if <code>/resource?foo=bar</code> was requested,
41
+ # <code>{'foo' => 'bar'}</code> is returned.
42
+ def self.query_from_params(params)
43
+ query = nil
44
+ unless (params.nil?)
45
+ query = params.clone
46
+ query.delete('neo_id')
47
+ query.delete('class')
48
+ query.delete('prop')
49
+ query.delete('rel')
50
+ end
51
+ query
52
+ end
53
+
54
+ # -------------------------------------------------------------------------
55
+ # /neo
56
+ # -------------------------------------------------------------------------
57
+
58
+ Sinatra::Application.post("/neo") do
59
+ body = request.body.read
60
+ Object.class_eval body
61
+ 200
62
+ end
63
+
64
+
65
+ Sinatra::Application.get("/neo") do
66
+ if request.accept.include?("text/html")
67
+ html = "<html><body><h2>Neo4j.rb v #{Neo4j::VERSION} is alive !</h2><p/><h3>Defined REST classes</h3>"
68
+ REST_NODE_CLASSES.keys.each {|clazz| html << "Class '" + clazz + "' <br/>"}
69
+ html << "</body></html>"
70
+ html
71
+ else
72
+ content_type :json
73
+ # make it look like it was a node - todo maybe it should be a real Neo4j::Node ...
74
+ properties = {:classes => REST_NODE_CLASSES.keys, :ref_node => Neo4j.ref_node._uri}
75
+ {:properties => properties}.to_json
76
+ end
77
+ end
78
+
79
+
80
+ # -------------------------------------------------------------------------
81
+ # /rels/<id>
82
+ # -------------------------------------------------------------------------
83
+
84
+ Sinatra::Application.get("/rels/:id") do
85
+ content_type :json
86
+ begin
87
+ Neo4j::Transaction.run do
88
+ rel = Neo4j.load_rel(params[:id].to_i)
89
+ return 404, "Can't find relationship with id #{params[:id]}" if rel.nil?
90
+ # include hyperlink to end_node if that has an _uri method
91
+ end_node_hash = {:uri => rel.end_node._uri}
92
+
93
+ # include hyperlink to start_node if that has an _uri method
94
+ start_node_hash = {:uri => rel.start_node._uri}
95
+
96
+ {:properties => rel.props, :start_node => start_node_hash, :end_node => end_node_hash}.to_json
97
+ end
98
+ rescue RestException => exception
99
+ return exception.code, {'error' => $!}.to_json
100
+ rescue Exception => e
101
+ return 500, {'error' => $!, 'backtrace' => e.backtrace}.to_json
102
+ end
103
+ end
104
+
105
+
106
+ # -------------------------------------------------------------------------
107
+ # /nodes/<classname>
108
+ # -------------------------------------------------------------------------
109
+
110
+ # Allows searching for nodes (provided that they are indexed). Supports the following:
111
+ # <code>/nodes/classname?search=name:hello~</code>:: Lucene query string
112
+ # <code>/nodes/classname?name=hello</code>:: Exact match on property
113
+ # <code>/nodes/classname?sort=name,desc</code>:: Specify sorting order
114
+ # <code>/nodes/classname?limit=100,20</code>:: Specify offset and number of nodes (for pagination)
115
+ Sinatra::Application.get("/nodes/:class") do
116
+ content_type :json
117
+ clazz = Neo4j::Rest.load_class(params[:class])
118
+ return 404, "Can't find class '#{classname}'" if clazz.nil?
119
+
120
+ begin
121
+ Neo4j::Transaction.run do
122
+ resources = clazz.find(Neo4j::Rest.query_from_params(params)) # uses overridden find method -- see below
123
+ resources.map{|res| res.props}.to_json
124
+ end
125
+ rescue RestException => exception
126
+ return exception.code, {'error' => $!}.to_json
127
+ rescue Exception => e
128
+ return 500, {'error' => $!, 'backtrace' => e.backtrace}.to_json
129
+ end
130
+ end
131
+
132
+ Sinatra::Application.post("/nodes/:class") do
133
+ content_type :json
134
+
135
+ clazz = Neo4j::Rest.load_class(params[:class])
136
+ return 404, "Can't find class '#{classname}'" if clazz.nil?
137
+
138
+ begin
139
+ uri = Neo4j::Transaction.run do
140
+ node = clazz.new
141
+ data = JSON.parse(request.body.read)
142
+ properties = data['properties']
143
+ node.update(properties, Neo4j::Rest.query_from_params(params))
144
+ node._uri
145
+ end
146
+ redirect "#{uri}", 201 # created
147
+ rescue RestException => exception
148
+ return exception.code, {'error' => $!}.to_json
149
+ rescue Exception => e
150
+ return 500, {'error' => $!, 'backtrace' => e.backtrace}.to_json
151
+ end
152
+ end
153
+
154
+
155
+ # -------------------------------------------------------------------------
156
+ # /nodes/<classname>/<id>
157
+ # -------------------------------------------------------------------------
158
+
159
+ Sinatra::Application.get("/nodes/:class/:id") do
160
+ content_type :json
161
+
162
+ begin
163
+ Neo4j::Transaction.run do
164
+ node = Neo4j.load_node(params[:id])
165
+ return 404, "Can't find node with id #{params[:id]}" if node.nil?
166
+ node.read(Neo4j::Rest.query_from_params(params))
167
+ relationships = node.rels.outgoing.inject({}) do |hash, v|
168
+ type = v.relationship_type.to_s
169
+ hash[type] ||= []
170
+ hash[type] << "#{Neo4j::Rest.base_uri}/rels/#{v.neo_id}"
171
+ hash
172
+ end
173
+ {:rels => relationships, :properties => node.props}.to_json
174
+ end
175
+ rescue RestException => exception
176
+ return exception.code, {'error' => $!}.to_json
177
+ rescue Exception => e
178
+ return 500, {'error' => $!, 'backtrace' => e.backtrace}.to_json
179
+ end
180
+ end
181
+
182
+ Sinatra::Application.put("/nodes/:class/:id") do
183
+ content_type :json
184
+ begin
185
+ Neo4j::Transaction.run do
186
+ body = request.body.read
187
+ data = JSON.parse(body)
188
+ properties = data['properties'] || {}
189
+ node = Neo4j.load_node(params[:id])
190
+ node.update(properties, Neo4j::Rest.query_from_params(params).merge({:strict => true}))
191
+ node.props.to_json
192
+ end
193
+ rescue RestException => exception
194
+ return exception.code, {'error' => $!}.to_json
195
+ rescue Exception => e
196
+ return 500, {'error' => $!, 'backtrace' => e.backtrace}.to_json
197
+ end
198
+ end
199
+
200
+ Sinatra::Application.delete("/nodes/:class/:id") do
201
+ content_type :json
202
+ begin
203
+ Neo4j::Transaction.run do
204
+ node = Neo4j.load_node(params[:id])
205
+ return 404, "Can't find node with id #{params[:id]}" if node.nil?
206
+ node.del(Neo4j::Rest.query_from_params(params))
207
+ ""
208
+ end
209
+ rescue RestException => exception
210
+ return exception.code, {'error' => $!}.to_json
211
+ rescue Exception => e
212
+ return 500, {'error' => $!, 'backtrace' => e.backtrace}.to_json
213
+ end
214
+ end
215
+
216
+
217
+ # -------------------------------------------------------------------------
218
+ # /nodes/<classname>/<id>/<property>
219
+ # -------------------------------------------------------------------------
220
+
221
+ Sinatra::Application.get("/nodes/:class/:id/traverse") do
222
+ content_type :json
223
+ begin
224
+ Neo4j::Transaction.run do
225
+ node = Neo4j.load_node(params[:id])
226
+ return 404, {'error' => "Can't find node with id #{params[:id]}"}.to_json if node.nil?
227
+
228
+ relationship = params['relationship']
229
+ depth = case params['depth']
230
+ when nil then
231
+ 1
232
+ when 'all' then
233
+ :all
234
+ else
235
+ params['depth'].to_i
236
+ end
237
+ return 400, {'error' => "invalid depth parameter - must be an integer"}.to_json if depth == 0
238
+
239
+ uris = node.traverse.outgoing(relationship.to_sym).depth(depth).collect{|node| node._uri}
240
+ {'uri_list' => uris}.to_json
241
+ end
242
+ rescue RestException => exception
243
+ return exception.code, {'error' => $!}.to_json
244
+ rescue Exception => e
245
+ return 500, {'error' => $!, 'backtrace' => e.backtrace}.to_json
246
+ end
247
+ end
248
+
249
+
250
+ Sinatra::Application.get("/nodes/:class/:id/:prop") do
251
+ content_type :json
252
+
253
+ begin
254
+ Neo4j::Transaction.run do
255
+ node = Neo4j.load_node(params[:id])
256
+ return 404, "Can't find node with id #{params[:id]}" if node.nil?
257
+ prop = params[:prop].to_sym
258
+ # check if prop is a relationship defined by has_n or has_one
259
+ if node.class.decl_relationships.keys.include?(prop)
260
+ # instead of returning properties return the relationship properties
261
+ rels = node.send(prop) || []
262
+ # is it a has_n or has_one relationship ?
263
+ # if it is a has_n then return an array of properties of that relationship (can be a large json struct)
264
+ (rels.respond_to?(:props) ? rels.props : rels.map{|rel| rel.props}).to_json
265
+ else
266
+ {prop => node[prop]}.to_json
267
+ end
268
+ end
269
+ rescue RestException => exception
270
+ return exception.code, {'error' => $!}.to_json
271
+ rescue Exception => e
272
+ return 500, {'error' => $!, 'backtrace' => e.backtrace}.to_json
273
+ end
274
+ end
275
+
276
+
277
+ Sinatra::Application.put("/nodes/:class/:id/:prop") do
278
+ content_type :json
279
+ begin
280
+ Neo4j::Transaction.run do
281
+ node = Neo4j.load_node(params[:id])
282
+ property = params[:prop]
283
+ body = request.body.read
284
+ data = JSON.parse(body)
285
+ value = data[property]
286
+ return 409, "Can't set property #{property} with JSON data '#{body}'" if value.nil?
287
+ node[property] = value
288
+ 200
289
+ end
290
+ rescue RestException => exception
291
+ return exception.code, {'error' => $!}.to_json
292
+ rescue Exception => e
293
+ return 500, {'error' => $!, 'backtrace' => e.backtrace}.to_json
294
+ end
295
+ end
296
+
297
+ URL_REGEXP = Regexp.new '((http[s]?|ftp):\/)?\/?([^:\/\s]+)((\/\w+)*\/)([\w\-\.]+\/[\w\-\.]+)$' #:nodoc:
298
+
299
+ Sinatra::Application.post("/nodes/:class/:id/:rel") do
300
+ content_type :json
301
+ begin
302
+ new_id = Neo4j::Transaction.run do
303
+ node = Neo4j.load_node(params[:id])
304
+ return 404, "Can't find node with id #{params[:id]}" if node.nil?
305
+ rel = params[:rel]
306
+
307
+ body = request.body.read
308
+ data = JSON.parse(body)
309
+ uri = data['uri']
310
+ match = URL_REGEXP.match(uri)
311
+ return 400, "Bad node uri '#{uri}'" if match.nil?
312
+ to_clazz, to_node_id = match[6].split('/')
313
+
314
+ other_node = Neo4j.load_node(to_node_id.to_i)
315
+ return 400, "Unknown other node with id '#{to_node_id}'" if other_node.nil?
316
+
317
+ if to_clazz != other_node.class.to_s
318
+ return 400, "Wrong type id '#{to_node_id}' expected '#{to_clazz}' got '#{other_node.class.to_s}'"
319
+ end
320
+
321
+ rel_obj = node.add_rel(rel, other_node)
322
+
323
+ return 400, "Can't create relationship to #{to_clazz}" if rel_obj.nil?
324
+
325
+ rel_obj.neo_id
326
+ end
327
+ redirect "/rels/#{new_id}", 201 # created
328
+ rescue RestException => exception
329
+ return exception.code, {'error' => $!}.to_json
330
+ rescue Exception => e
331
+ return 500, {'error' => $!, 'backtrace' => e.backtrace}.to_json
332
+ end
333
+ end
334
+ end
335
+
336
+ end