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,193 @@
1
+ module Neo4j
2
+
3
+ org.neo4j.kernel.impl.core.NodeProxy.class_eval do
4
+ # See Neo4j::RestMixin#read
5
+ def read
6
+ end
7
+ end
8
+
9
+ # Creates a number of resources for the class using this mixin.
10
+ #
11
+ # The following resources are created:
12
+ #
13
+ # <b>add new class</b>:: <code>POST /neo</code> post ruby code of a neo4j node class
14
+ # <b>node classes</b>:: <code>GET /neo</code> - returns hyperlinks to /nodes/classname
15
+ # <b>search nodes</b>:: <code>GET /nodes/classname?name=p</code>
16
+ # <b>view all nodes</b>:: <code>GET /nodes/classname</code>
17
+ # <b>update property</b>:: <code>PUT nodes/classname/id/property_name</code>
18
+ # <b>view property</b>:: <code>GET nodes/classname/id/property_name</code>
19
+ # <b>delete node</b>:: <code>DELETE nodes/classname/node_id</code>
20
+ # <b>update properties</b>:: <code>PUT nodes/classname/node_id</code>
21
+ # <b>view node</b>:: <code>GET /nodes/classname/id</code>
22
+ # <b>create node</b>:: <code>POST /nodes/classname</code>
23
+ # <b>view relationship</b>:: <code>GET /rels/id</code>
24
+ # <b>list rels</b>:: <code>GET /nodes/classname/id/relationship-type</code>
25
+ # <b>add relationship</b>:: <code>POST /nodes/classname/id/relationship-type</code>
26
+ # <b>traversal</b>:: <code>GET nodes/classname/id/traverse?relationship=relationship-type&depth=depth</code>
27
+ #
28
+ # Also provides lucene queries
29
+ # <b>Lucene query string</b>:: <code>/nodes/classname?search=name:hello~</code>
30
+ # <b>Exact match on property</b>:: <code>/nodes/classname?name=hello</code>
31
+ # <b>Specify sorting order</b>:: <code>/nodes/classname?sort=name,desc</code>
32
+ # <b>Pagination (offset,num)</b>:: <code>/nodes/classname?limit=100,20</code>#
33
+ #
34
+ # When create a new node by posting to <code>/nodes/classname</code> a 201 will be return with the 'Location' header set to the
35
+ # URI of the newly created node.
36
+ #
37
+ # The JSON representation of a node looks like this
38
+ #
39
+ # {"rels" : {"type1":"http://0.0.0.0:4567/rels/0","type2":"http://0.0.0.0:4567/rels/1"},
40
+ # "properties" : {"_neo_id":1,"_classname":"MyNode"}}
41
+ #
42
+ module RestMixin
43
+
44
+ def _uri
45
+ "#{Neo4j::Rest.base_uri}#{_uri_rel}"
46
+ end
47
+
48
+ def _uri_rel
49
+ clazz = self.class.root_class.to_s #.gsub(/::/, '-') TODO urlencoding
50
+ "/nodes/#{clazz}/#{neo_id}"
51
+ end
52
+
53
+ # Called by the REST API if this node is accessed directly by ID. Any query parameters
54
+ # in the request are passed in a hash. For example if <code>GET /nodes/MyClass/1?foo=bar</code>
55
+ # is requested, <code>MyClass#accessed</code> is called with <code>{'foo' => 'bar'}</code>.
56
+ # By default this method does nothing, but model classes may override it to achieve specific
57
+ # behaviour.
58
+ def read(options={})
59
+ end
60
+
61
+ # Called by the REST API if this node is deleted. Any query parameters in the request are passed
62
+ # in a hash.
63
+ def del(options={})
64
+ super()
65
+ end
66
+
67
+
68
+ def self.included(c)
69
+ c.extend ClassMethods
70
+ uri_rel = c._uri_rel
71
+ # just for debugging and logging purpose so we know which classes uses this mixin, TODO - probablly not needed
72
+ Neo4j::Rest::REST_NODE_CLASSES[uri_rel] = c
73
+ end
74
+
75
+
76
+ module ClassMethods
77
+
78
+ # todo remove
79
+ def _uri_rel # :nodoc:
80
+ clazz = root_class.to_s #.gsub(/::/, '-') TODO urlencoding
81
+ "/nodes/#{clazz}"
82
+ end
83
+
84
+
85
+ # Overrides 'find' so that we can simply pass a query parameters object to it, and
86
+ # search resources accordingly.
87
+ def find(query=nil, &block)
88
+ return super(query, &block) if query.nil? || query.kind_of?(String)
89
+
90
+ query = symbolize_keys(query)
91
+
92
+ if query[:search]
93
+ # Use Lucene
94
+ results = super(query[:search])
95
+ results = [*apply_lucene_sort(query[:sort], results)] rescue [*super(query[:search])]
96
+
97
+ else
98
+ # Use traverser
99
+ results = apply_ruby_sort(query[:sort], apply_traverser_conditions(query))
100
+ end
101
+
102
+ apply_limits(query[:limit], results)
103
+ end
104
+
105
+ # :nodoc:
106
+ def symbolize_keys(hash)
107
+ # Borrowed from ActiveSupport
108
+ hash.inject({}) do |options, (key, value)|
109
+ options[(key.to_sym rescue key) || key] = value
110
+ options
111
+ end
112
+ end
113
+
114
+ protected
115
+
116
+ # Searches for nodes matching conditions by using a traverser.
117
+ def apply_traverser_conditions(query)
118
+ query = query.reject{|key, value| [:sort, :limit, :classname].include? key }
119
+
120
+ index_node = Neo4j::IndexNode.instance
121
+ raise 'Index node is nil. Make sure you have called Neo4j.load_reindexer' if index_node.nil?
122
+ traverser = index_node.traverse.outgoing(root_class)
123
+
124
+ traverser.filter do |position|
125
+ node = position.current_node
126
+ position.depth == 1 and
127
+ query.inject(true) do |meets_condition, (key, value)|
128
+ meets_condition && (node.send(key) == value)
129
+ end
130
+ end
131
+ end
132
+
133
+ # Sorts a list of results according to a string of comma-separated fieldnames (optionally
134
+ # with 'asc' or 'desc' thrown in). For use in cases where we don't go via Lucene.
135
+ def apply_ruby_sort(sort_string, results)
136
+ if sort_string
137
+ sort_fields = sort_string.to_s.split(/,/)
138
+ [*results].sort do |x,y|
139
+ catch(:item_order) do
140
+ sort_fields.each_index do |index|
141
+ field = sort_fields[index]
142
+ unless %w(asc desc).include?(field)
143
+ item_order = if sort_fields[index + 1] == 'desc'
144
+ (y.send(field) || '') <=> (x.send(field) || '')
145
+ else
146
+ (x.send(field) || '') <=> (y.send(field) || '')
147
+ end
148
+ throw :item_order, item_order unless item_order == 0
149
+ end
150
+ end
151
+ 0
152
+ end
153
+ end
154
+ else
155
+ [*results]
156
+ end
157
+ end
158
+
159
+ # Applies Lucene sort instructions to a Neo4j::SearchResult object.
160
+ def apply_lucene_sort(sort_string, results)
161
+ return results if sort_string.nil?
162
+ last_field = nil
163
+
164
+ sort_string.to_s.split(/,/).each do |field|
165
+ if %w(asc desc).include? field
166
+ results = results.sort_by(field == 'asc' ? Lucene::Asc[last_field] : Lucene::Desc[last_field])
167
+ last_field = nil
168
+ else
169
+ results = results.sort_by(Lucene::Asc[last_field]) unless last_field.nil?
170
+ last_field = field
171
+ end
172
+ end
173
+ results.sort_by(Lucene::Asc[last_field]) unless last_field.nil?
174
+ results
175
+ end
176
+
177
+ # Return only the requested subset of results for pagination
178
+ # (TODO: can this be done more efficiently within Lucene?)
179
+ def apply_limits(limit_string, results)
180
+ if limit_string
181
+ limit = limit_string.to_s.split(/,/).map{|i| i.to_i}
182
+ limit.unshift(0) if limit.size == 1
183
+
184
+ (limit[0]...(limit[0]+limit[1])).map{|n| results[n] }
185
+ else
186
+ results
187
+ end
188
+ end
189
+
190
+ end
191
+ end
192
+
193
+ end
@@ -0,0 +1,50 @@
1
+ module Neo4j
2
+
3
+ module Rest
4
+ # todo move inside namepace Rest
5
+
6
+ class RestServer #:nodoc:
7
+ class << self
8
+ attr_accessor :thread
9
+
10
+ def on_neo_started(neo_instance)
11
+ start
12
+ end
13
+
14
+ def on_neo_stopped(neo_instance)
15
+ stop
16
+ end
17
+
18
+
19
+ def start
20
+ puts "RESTful already started" if @thread
21
+ return if @thread
22
+
23
+ @thread = Thread.new do
24
+ puts "Start Restful server at port #{Neo4j::Config[:rest_port]}"
25
+ Sinatra::Application.run! :port => Neo4j::Config[:rest_port]
26
+ end
27
+ end
28
+
29
+ def stop
30
+ if @thread
31
+ # TODO must be a nicer way to do this - to shutdown sinatra
32
+ @thread.kill
33
+ @thread = nil
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+
40
+ def self.load_rest #:nodoc:
41
+ Neo4j::Config.defaults[:rest_port] = 9123
42
+ Neo4j.event_handler.add(RestServer)
43
+ end
44
+
45
+ load_rest
46
+
47
+ end
48
+
49
+
50
+ end
@@ -0,0 +1,141 @@
1
+ module Neo4j
2
+ module Rest
3
+ # A stub class that talk to a neo4j rest server.
4
+ # It behaves like a Neo4j::Node, but instead of actually performing operation
5
+ # it talks to the server over HTTP/JSON
6
+ #
7
+ module RestStubMixin
8
+ attr_accessor :json
9
+
10
+ def initialize(uri_or_json_hash)
11
+ if uri_or_json_hash.kind_of?(Hash)
12
+ @json = uri_or_json_hash
13
+ else
14
+ @json = RestHttpMethods.get_request(uri_or_json_hash)
15
+ end
16
+ end
17
+
18
+ def [](key)
19
+ @json['properties'][key.to_s]
20
+ end
21
+
22
+ def relationships
23
+ RelationshipTraverserStub.new(@json['rels'])
24
+ end
25
+
26
+
27
+ def relationship?(type, dir=:outgoing)
28
+ rels.rel?(type, dir)
29
+ end
30
+
31
+ def props
32
+ @json['properties']
33
+ end
34
+ end
35
+
36
+ module RestHttpMethods # :nodoc:
37
+ class << self
38
+ def get_request(resource, args = {})
39
+ body = _get_request(resource, args)
40
+ JSON.parse(body)
41
+ end
42
+
43
+ def _get_request(resource, args)
44
+ _request(resource, :get, args)
45
+ end
46
+
47
+ def _request(resource, method = :get, args = {})
48
+ url = URI.parse(resource)
49
+ host = url.host
50
+ host.sub!(/0\.0\.0\.0/, 'localhost')
51
+
52
+ #if args
53
+ # url.query = args.map { |k, v| "%s=%s" % [URI.encode(k), URI.encode(v)] }.join("&")
54
+ #end
55
+
56
+ req =
57
+ case method
58
+ when :put
59
+ Net::HTTP::Put.new(url.path)
60
+ when :get
61
+ Net::HTTP::Get.new(url.path)
62
+ when :post
63
+ Net::HTTP::Post.new(url.path)
64
+ end
65
+
66
+ http = Net::HTTP.new(host, url.port)
67
+ res = http.start() { |conn| conn.request(req) }
68
+ res.body
69
+ end
70
+ end
71
+ end
72
+
73
+
74
+ class RelationshipStub # :nodoc:
75
+ include RestStubMixin
76
+
77
+ def start_node
78
+ uri = @json['start_node']['uri']
79
+ NodeStub.new(uri)
80
+ end
81
+
82
+ def end_node
83
+ uri = @json['end_node']['uri']
84
+ NodeStub.new(uri)
85
+ end
86
+
87
+ end
88
+
89
+ class RelationshipTraverserStub
90
+ include Enumerable
91
+
92
+ def initialize(json)
93
+ @json = json
94
+ end
95
+
96
+ def outgoing(rel_type)
97
+ @rel_type = rel_type
98
+ self
99
+ end
100
+
101
+ def relationship?(type, dir=:outgoing)
102
+ !@json[type.to_s].nil?
103
+ end
104
+
105
+ def nodes
106
+ @return_nodes = true
107
+ self
108
+ end
109
+
110
+ def first
111
+ each do |x|
112
+ return x if !block_given? || yield(x)
113
+ end
114
+ end
115
+
116
+ def each
117
+ keys =
118
+ if @rel_type.nil?
119
+ @json.keys # take all keys
120
+ else
121
+ [@rel_type.to_s]
122
+ end
123
+
124
+ keys.each do |rel_type|
125
+ next unless rel?(rel_type)
126
+ if @return_nodes
127
+ @json[rel_type.to_s].each {|uri| yield RelationshipStub.new(uri).end_node}
128
+ else
129
+ @json[rel_type.to_s].each {|uri| yield RelationshipStub.new(uri)}
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ # A class that simply includes the RestStubMixin
136
+ class NodeStub
137
+ include RestStubMixin
138
+ end
139
+
140
+ end
141
+ end
@@ -0,0 +1,34 @@
1
+ require 'neo4j'
2
+ require 'neo4j/extensions/rest'
3
+ require 'neo4j/extensions/tx_tracker'
4
+
5
+ module Neo4j
6
+ class TxNode
7
+ include Neo4j::RestMixin
8
+ end
9
+
10
+ class TxRelationshipCreatedNode
11
+ include Neo4j::RestMixin
12
+ end
13
+
14
+ class TxNodeCreated
15
+ include Neo4j::RestMixin
16
+ end
17
+
18
+ class TxNodeList
19
+ include Neo4j::RestMixin
20
+ end
21
+
22
+ class ReferenceNode
23
+ include Neo4j::RestMixin
24
+ end
25
+
26
+
27
+ # FOR TESTING PURPOSE ----
28
+
29
+ class Node
30
+ include Neo4j::RestMixin # for making it easier to test
31
+ end
32
+
33
+ Neo4j::Config[:storage_path] = 'tmp/master'
34
+ end
@@ -0,0 +1,31 @@
1
+ require 'net/http'
2
+ require 'thread'
3
+ require 'json'
4
+ #require 'sinatra/base'
5
+ require 'neo4j/extensions/rest/stubs'
6
+
7
+
8
+ require 'neo4j/extensions/tx_tracker'
9
+
10
+ module Neo4j
11
+
12
+ module Rest #:nodoc: all
13
+ def self.base_uri
14
+ Neo4j::Config[:master_neo4j_uri]
15
+ end
16
+ end
17
+
18
+
19
+ # TODO This is not working yet !
20
+ def self.replicate
21
+ neo_master = Neo4j::Rest::NodeStub.new(Config[:master_neo4j_uri] + "/neo")
22
+ neo_ref_node = Neo4j::Rest::NodeStub.new(neo_master[:ref_node])
23
+ tx_node_list = neo_ref_node.rels.outgoing(:tx_node_list).nodes.first
24
+ tx_node = tx_node_list.rels.outgoing(:tx_nodes).nodes.first
25
+ Neo4j::Transaction.run do
26
+ Neo4j::TxNodeList.instance.redo_tx(tx_node)
27
+ end
28
+ end
29
+
30
+ Config[:master_neo4j_uri] = 'http://localhost:9123'
31
+ end