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,392 @@
1
+ module Neo4j
2
+
3
+
4
+ # The relationship class between TxNodes
5
+ class TxNodeRelationship
6
+ include Neo4j::RelationshipMixin
7
+ end
8
+
9
+ # This nodes listen for all events like node nodes created/deleted or
10
+ # if a property/relationship has changed. When a event is triggered it
11
+ #
12
+ class TxNodeList
13
+ include Neo4j::NodeMixin
14
+
15
+ has_list(:tx_nodes).relationship(TxNodeRelationship)
16
+
17
+ def on_node_created(node)
18
+ tx = TxNodeCreated.new
19
+ uuid = Neo4j.create_uuid
20
+ node[:uuid] = uuid # TODO should use a better UUID
21
+ tx[:tracked_neo_id] = node.neo_id
22
+ tx[:tracked_classname] = node.class.to_s unless node.kind_of?(org.neo4j.graphdb.Node)
23
+ tx[:created] = true
24
+ tx[:uuid] = uuid
25
+ tx_nodes << tx
26
+ end
27
+
28
+ def on_node_deleted(node)
29
+ tx = TxNode.new
30
+ tx[:uuid] = node[:uuid]
31
+ tx[:deleted] = true
32
+ tx[:tracked_classname] = node.class.to_s unless node.kind_of?(org.neo4j.graphdb.Node)
33
+ tx_nodes << tx
34
+ end
35
+
36
+
37
+ def on_tx_finished(tx)
38
+ return if self.tx_nodes.empty? # nothing yet commited
39
+ last_commited_node = self.tx_nodes.first
40
+ last_commited_node[:tx_finished] = true
41
+ end
42
+
43
+ def on_property_changed(node, key, old_value, new_value)
44
+ return if "uuid" == key.to_s # do not track uuid
45
+ tx = TxNode.new
46
+ tx[:uuid] = node[:uuid]
47
+ tx[:property_changed] = true
48
+ tx[:tracked_neo_id] = node.neo_id
49
+ tx[:key] = key
50
+ tx[:old_value] = old_value
51
+ tx[:new_value] = new_value
52
+ tx_nodes << tx
53
+ end
54
+
55
+ def on_relationship_created(relationship)
56
+ # check so that it was no this method that caused this event
57
+ return if tx_nodes.relationship_type.to_sym == relationship.relationship_type.to_sym
58
+ tx = TxRelationshipCreatedNode.new
59
+ uuid = Neo4j.create_uuid
60
+ tx[:uuid] = uuid
61
+ tx[:relationship_created] = true
62
+ tx[:tracked_neo_id] = relationship.neo_id
63
+ tx[:start_node_uuid] = relationship.start_node[:uuid]
64
+ tx[:end_node_uuid] = relationship.end_node[:uuid]
65
+ tx[:relationship_type] = relationship.relationship_type.to_s
66
+ relationship[:uuid] = uuid
67
+ tx_nodes << tx
68
+ end
69
+
70
+
71
+ def on_relationship_deleted(relationship)
72
+ # check so that it was no this method that caused this event
73
+ return if tx_nodes.relationship_type.to_sym == relationship.relationship_type.to_sym
74
+ tx = TxNode.new
75
+ uuid = Neo4j.create_uuid
76
+ tx[:uuid] = uuid
77
+ tx[:relationship_deleted] = true
78
+ tx[:tracked_neo_id] = relationship.neo_id
79
+ tx[:relationship_type] = relationship.relationship_type.to_s
80
+ tx[:start_node_uuid] = relationship.start_node[:uuid]
81
+ tx[:end_node_uuid] = relationship.end_node[:uuid]
82
+ tx_nodes << tx
83
+ end
84
+
85
+
86
+ # Return a list of TxNodes that belongs to the same transaction
87
+ # Will always include the specified from_tx_node
88
+ # It will follow the linked list of tx nodes until it founds a new transaction.
89
+ def tx_nodes_belonging_to_same_tx(from_tx_node)
90
+ nodes_in_same_tx = []
91
+ nodes_in_same_tx << from_tx_node # always include the first one
92
+
93
+ # include all other nodes until we find a new transaction marker
94
+ curr_node = from_tx_node
95
+
96
+ while (true) do
97
+ curr_node = curr_node.list(:tx_nodes).next
98
+ break if curr_node.nil?
99
+ break if curr_node[:tx_finished]
100
+ nodes_in_same_tx << curr_node
101
+ end
102
+ nodes_in_same_tx
103
+ end
104
+
105
+ def create_node(tx_node)
106
+ classname = tx_node[:tracked_classname]
107
+ node = if classname.nil?
108
+ Neo4j::Node.new
109
+ else
110
+ clazz = classname.split("::").inject(Kernel) do |container, name|
111
+ container.const_get(name.to_s)
112
+ end
113
+ clazz.new
114
+ end
115
+
116
+ uuid = tx_node[:uuid]
117
+ tx_node = find_tx(node.neo_id, :tracked_neo_id)
118
+ #tx_node = find_tx_node(uuid)
119
+ tx_node[:uuid] = uuid
120
+ tx_node[:tracked_neo_id] = node.neo_id
121
+ node[:uuid] = uuid
122
+ end
123
+
124
+ def delete_node(tx_node)
125
+ uuid = tx_node[:uuid]
126
+ node = load_node_with_uuid(uuid)
127
+ node.del
128
+ end
129
+
130
+ def undo_property_changed(tx_node)
131
+ uuid = tx_node[:uuid]
132
+ node = load_node_with_uuid(uuid)
133
+ key = tx_node[:key]
134
+ old_value = tx_node[:old_value]
135
+ node[key] = old_value
136
+ end
137
+
138
+ def redo_property_changed(tx_node)
139
+ uuid = tx_node[:uuid]
140
+ node = load_node_with_uuid(uuid)
141
+ key = tx_node[:key]
142
+ new_value = tx_node[:new_value]
143
+ node[key] = new_value
144
+ end
145
+
146
+ def create_relationship(tx_node)
147
+ # recreate deleted relationship
148
+ type = tx_node[:relationship_type]
149
+ start_node_uuid = tx_node[:start_node_uuid]
150
+ end_node_uuid = tx_node[:end_node_uuid]
151
+ start_node = load_node_with_uuid(start_node_uuid)
152
+ end_node = load_node_with_uuid(end_node_uuid)
153
+ start_node.rels.outgoing(type) << end_node
154
+ end
155
+
156
+ def delete_relationship(tx_node)
157
+ relationship = load_relationship_with_uuid(tx_node[:uuid])
158
+ relationship.del
159
+ end
160
+
161
+
162
+ def redo_tx(from_tx_node)
163
+ nodes_to_redo = tx_nodes_belonging_to_same_tx(from_tx_node)
164
+ nodes_to_redo.reverse_each do |curr_node|
165
+ if (curr_node[:created])
166
+ create_node(curr_node)
167
+ elsif (curr_node[:deleted])
168
+ delete_node(curr_node)
169
+ elsif (curr_node[:property_changed])
170
+ redo_property_changed(curr_node)
171
+ elsif (curr_node[:relationship_created])
172
+ create_relationship(curr_node)
173
+ elsif (curr_node[:relationship_deleted])
174
+ delete_relationship(curr_node)
175
+ else
176
+ raise "unknow tx #{curr_node.props.inspect}"
177
+ end
178
+ end
179
+ end
180
+
181
+ def undo_tx(from_tx_node = tx_nodes.first)
182
+ return if from_tx_node.nil?
183
+
184
+ nodes_to_undo = tx_nodes_belonging_to_same_tx(from_tx_node)
185
+
186
+ nodes_to_undo.each do |curr_node|
187
+ if (curr_node[:created])
188
+ delete_node(curr_node)
189
+ elsif (curr_node[:deleted])
190
+ create_node(curr_node)
191
+ elsif (curr_node[:property_changed])
192
+ undo_property_changed(curr_node)
193
+ elsif (curr_node[:relationship_created])
194
+ delete_relationship(curr_node)
195
+ elsif (curr_node[:relationship_deleted])
196
+ create_relationship(curr_node)
197
+ else
198
+ raise "unknow tx #{curr_node.props.inspect}"
199
+ end
200
+ end
201
+ end
202
+
203
+
204
+ # Load a neo4j node given a cluster wide UUID (instead of id)
205
+ # :api: public
206
+ def load_node_with_uuid(uuid)
207
+ txnode = find_tx_node(uuid)
208
+ return if txnode.nil?
209
+ # does this node exist ?
210
+ id = txnode[:tracked_neo_id]
211
+ Neo4j.load_node(id)
212
+ end
213
+
214
+
215
+ # Load a neo4j relatinship given a cluster wide UUID (instead of id)
216
+ # :api: public
217
+ def load_relationship_with_uuid(uuid)
218
+ txnode = find_tx_relationship(uuid)
219
+ return if txnode.nil?
220
+ # does this node exist ?
221
+ id = txnode[:tracked_neo_id]
222
+ Neo4j.load_rel(id)
223
+ end
224
+
225
+
226
+ # :api: private
227
+ def find_tx_node(uuid) # :nodoc:
228
+ # since lucene only updates the index after the transaction commits we
229
+ # first look in the current transaction
230
+ found = find_tx(uuid)
231
+ # if not found that find it with lucene
232
+ found ||= TxNodeCreated.find(:uuid => uuid).first
233
+ found
234
+ end
235
+
236
+
237
+ # :api: private
238
+ def find_tx_relationship(uuid) # :nodoc:
239
+ TxRelationshipCreatedNode.find(:uuid => uuid).first
240
+ end
241
+
242
+
243
+ # Find a TxNodeCreate node in the latest transaction with the given uuid
244
+ def find_tx(value, key = :uuid) # :nodoc:
245
+ tx_nodes.find {|node| node[:_classname] == TxNodeCreated.to_s && node[key] == value}
246
+ end
247
+
248
+ # Create a new a neo4j node given a cluster wide UUID (instead of id)
249
+ # :nodoc:
250
+ # :api: private
251
+ def create_node_with_uuid(uuid)
252
+ txnode = find_tx_node(uuid)
253
+ return if txnode.nil?
254
+ # does this node exist ?
255
+ id = txnode[:tracked_neo_id]
256
+ Neo4j.load_node(id)
257
+ end
258
+
259
+ #
260
+ # Class methods ------------------------------------------------------
261
+ #
262
+
263
+ def self.on_neo_started(neo_instance)
264
+ Neo4j::Transaction.run do
265
+ # has the tx_node_list already been created ?
266
+ unless neo_instance.ref_node.rel?(:tx_node_list)
267
+ # it does not exist - create it
268
+ neo_instance.ref_node.rels.outgoing(:tx_node_list) << TxNodeList.new
269
+ end
270
+ # cache this so we do not have to look it up always
271
+ @tx_node_list = neo_instance.ref_node.rels.outgoing(:tx_node_list).nodes.first
272
+ Neo4j.event_handler.add(@tx_node_list)
273
+ end
274
+ end
275
+
276
+ def self.on_neo_stopped(neo_instance)
277
+ # unregister the instance
278
+ Neo4j.event_handler.remove(@tx_node_list)
279
+ @tx_node_list = nil
280
+ end
281
+
282
+
283
+ def self.instance
284
+ Neo4j.start unless @tx_node_list
285
+ @tx_node_list
286
+ end
287
+ end
288
+
289
+
290
+ # Keeps the uuid of created relationship in a lucene index
291
+ class TxRelationshipCreatedNode
292
+ include Neo4j::NodeMixin
293
+
294
+ belongs_to_list(:tx_nodes).relationship(TxNodeRelationship)
295
+
296
+ property :uuid
297
+ index :uuid
298
+
299
+ def to_s
300
+ "TxRelationshipCreatedNode: " + props.inspect
301
+ end
302
+ end
303
+
304
+
305
+ # Keeps the uuid of created nodes in a lucene index
306
+ class TxNodeCreated
307
+ include Neo4j::NodeMixin
308
+
309
+ belongs_to_list(:tx_nodes).relationship(TxNodeRelationship)
310
+
311
+ property :uuid
312
+ index :uuid
313
+
314
+ def to_s
315
+ "TxNodeCreated: " + props.inspect
316
+ end
317
+ end
318
+
319
+
320
+ # Represent an events like property change
321
+ # Does not represend events for relationship and node creations
322
+ class TxNode
323
+ include Neo4j::NodeMixin
324
+
325
+ belongs_to_list(:tx_nodes).relationship(TxNodeRelationship)
326
+
327
+ def to_s
328
+ "TxNode: " + props.inspect
329
+ end
330
+ end
331
+
332
+
333
+ #-------------------------------------------------
334
+ # Neo4j Module Methods
335
+ #
336
+ #-------------------------------------------------
337
+
338
+
339
+ # Loads the tx tracker extension
340
+ def self.load_tx_tracker
341
+ Neo4j.event_handler.add_filter(TxNode)
342
+ Neo4j.event_handler.add_filter(TxNodeCreated)
343
+ Neo4j.event_handler.add_filter(TxRelationshipCreatedNode)
344
+
345
+ Neo4j.event_handler.add(TxNodeList)
346
+ Neo4j.event_handler.add(TxNodeRelationship)
347
+ # if neo is already run we have to let txnodelist have a chance to add it self
348
+ # TxNodeList.on_neo_started(Neo4j.instance) if Neo4j.running?
349
+ Neo4j::Transaction.run { TxNodeList.on_neo_started(Neo4j) } if Neo4j.running?
350
+ end
351
+
352
+ def self.unload_tx_tracker
353
+ Neo4j.event_handler.remove_filter(TxNode)
354
+ Neo4j.event_handler.remove_filter(TxNodeCreated)
355
+ Neo4j.event_handler.remove_filter(TxRelationshipCreatedNode)
356
+
357
+ Neo4j.event_handler.remove(TxNodeList)
358
+ Neo4j.event_handler.remove(TxNodeRelationship)
359
+ end
360
+
361
+ # Undo the last transaction
362
+ #
363
+ # :api: public
364
+ def self.undo_tx
365
+ TxNodeList.instance.undo_tx
366
+ end
367
+
368
+
369
+ # Loads a node with the given uuid
370
+ # Returns nil if not found other wise the Node.
371
+ # :api: public
372
+ def self.load_node_with_uuid(uuid)
373
+ TxNodeList.instance.load_node_with_uuid(uuid)
374
+ end
375
+
376
+
377
+ # Loads a relationship with the given uuid
378
+ # Returns nil if not found other wise the Node.
379
+ # :api: public
380
+ def self.load_relationship_with_uuid(uuid)
381
+ TxNodeList.instance.load_relationship_with_uuid(uuid)
382
+ end
383
+
384
+ # Generates a new unique uuid
385
+ def self.create_uuid
386
+ rand(100000000) # TODO a very bad UUID generator ...
387
+ end
388
+
389
+ Neo4j.load_tx_tracker
390
+
391
+
392
+ end
@@ -0,0 +1,187 @@
1
+ module Neo4j
2
+
3
+ # This class is responsible for both knowing which nodes that needs to be reindexed
4
+ # and how to perform the reindex operation (the document_updaters attribute).
5
+ #
6
+ # There is one Indexer per Node root class.
7
+ #
8
+ class Indexer #:nodoc:
9
+ attr_reader :document_updaters, :index_id
10
+ attr_reader :property_indexer # for testing purpose
11
+
12
+ def initialize(indexed_class, query_for_nodes)
13
+ @relationship_indexers = {}
14
+ @query_for_nodes = query_for_nodes
15
+ @property_indexer = PropertyIndexer.new
16
+ @document_updaters = [@property_indexer]
17
+ # the file name of the lucene index if kept on disk
18
+ @index_id = "/" + indexed_class.to_s.gsub('::', '/')
19
+ end
20
+
21
+ # Returns the Indexer for the given Neo4j::NodeMixin class
22
+ def self.instance(clazz, query_for_nodes = true)
23
+ @instances ||= {}
24
+ @instances[clazz.root_class] ||= Indexer.new(clazz.root_class, query_for_nodes)
25
+ @instances[clazz.root_class]
26
+ end
27
+
28
+ # only for testing purpose, e.g we need to redefine an existing class
29
+ def self.remove_instance(clazz)
30
+ @instances.delete(clazz.root_class) if !@instances.nil? && clazz.respond_to?(:root_class)
31
+ end
32
+
33
+
34
+ # (Re)index the given node
35
+ def self.index(node)
36
+ indexer = instance(node.class)
37
+ indexer.index(node)
38
+ end
39
+
40
+ def find(query,block)
41
+ SearchResult.new lucene_index, query, @query_for_nodes, &block
42
+ end
43
+
44
+ def add_index_on_property(prop)
45
+ @property_indexer.properties << prop.to_sym
46
+ end
47
+
48
+ def remove_index_on_property(prop)
49
+ @property_indexer.properties.delete prop.to_sym
50
+ end
51
+
52
+ def add_index_in_relationship_on_property(updater_clazz, rel_name, rel_type, prop, namespace_type)
53
+ unless relationship_indexer_for?(namespace_type)
54
+ indexer = new_relationship_indexer_for(namespace_type, rel_name.to_sym)
55
+ self.class.instance(updater_clazz).document_updaters << indexer
56
+ end
57
+
58
+ # TODO make sure the same index is not added twice
59
+ relationship_indexer_for(namespace_type).properties << prop.to_sym
60
+ end
61
+
62
+ def index(node)
63
+ document = {:id => node.neo_id }
64
+
65
+ @document_updaters.each do |updater|
66
+ updater.update_document(document, node)
67
+ end
68
+
69
+ lucene_index << document
70
+ end
71
+
72
+ def delete_index(node)
73
+ lucene_index.delete(node.neo_id)
74
+ end
75
+
76
+
77
+ def lucene_index
78
+ Lucene::Index.new(@index_id)
79
+ end
80
+
81
+ def field_infos
82
+ lucene_index.field_infos
83
+ end
84
+
85
+ def on_property_changed(node, prop)
86
+ @relationship_indexers.values.each {|indexer| indexer.on_property_changed(node, prop.to_sym)}
87
+ @property_indexer.on_property_changed(node,prop.to_sym)
88
+ end
89
+
90
+ def on_relationship_created(node, rel_type)
91
+ @relationship_indexers.values.each {|indexer| indexer.on_relationship_created(node, rel_type.to_sym)}
92
+ end
93
+
94
+ def on_relationship_deleted(node, rel_type)
95
+ @relationship_indexers.values.each {|indexer| indexer.on_relationship_deleted(node, rel_type.to_sym)}
96
+ end
97
+
98
+ def relationship_indexer_for(rel_type)
99
+ @relationship_indexers[rel_type.to_sym]
100
+ end
101
+
102
+ def relationship_indexer_for?(rel_type)
103
+ !relationship_indexer_for(rel_type.to_sym).nil?
104
+ end
105
+
106
+ def new_relationship_indexer_for(rel_type, rel_name)
107
+ @relationship_indexers[rel_type.to_sym] = RelationshipIndexer.new(rel_name.to_sym, rel_type.to_sym)
108
+ end
109
+
110
+ end
111
+
112
+
113
+ class PropertyIndexer #:nodoc:
114
+ attr_reader :properties
115
+
116
+ def initialize
117
+ @properties = []
118
+ end
119
+
120
+ def on_property_changed(node, prop)
121
+ Indexer.index(node) if @properties.include?(prop)
122
+ end
123
+
124
+ def update_document(document, node)
125
+ # we have to check that the property exists since the index can be defined in a subclass
126
+ @properties.each {|prop| document[prop.to_sym] = node.send(prop) if node.respond_to?(prop)}
127
+ end
128
+ end
129
+
130
+
131
+ # If node class A has a relationship with type 'd' to node class B
132
+ # A.x -d-> B.y A.index d.y
133
+ # If property y on a B node changes then all its nodes in the relationship 'd' will
134
+ # be reindexed. Those nodes (which will be of type node class A) will use the same RelationshipIndexer to update the
135
+ # index document with key field 'd.y' and values of property y of all nodes in the
136
+ # relationship 'd'
137
+ #
138
+ class RelationshipIndexer #:nodoc:
139
+ attr_reader :rel_type, :properties
140
+
141
+ def initialize(rel_name, rel_type)
142
+ @properties = []
143
+ @rel_type = rel_type
144
+ @rel_name = rel_name
145
+ end
146
+
147
+ def on_property_changed(node, prop)
148
+ # make sure we're interested in indexing this property
149
+ reindex_related_nodes(node) if @properties.include?(prop)
150
+ end
151
+
152
+ def on_relationship_deleted(node, rel_type)
153
+ Indexer.index(node) if @rel_type == rel_type
154
+ end
155
+
156
+ def on_relationship_created(node, rel_type)
157
+ # make sure we're interested in indexing this relationship
158
+ if @rel_type == rel_type
159
+ reindex_related_nodes(node)
160
+ Indexer.index(node)
161
+ end
162
+ end
163
+
164
+ def reindex_related_nodes(node)
165
+ related_nodes = node.rels.both(@rel_type).nodes
166
+ related_nodes.each do |related_node|
167
+ Indexer.index(related_node)
168
+ end
169
+ end
170
+
171
+ def index_key(property)
172
+ "#@rel_name.#{property}".to_sym
173
+ end
174
+
175
+ def update_document(document, node)
176
+ relationships = node.rels.both(@rel_type).nodes
177
+ relationships.each do |other_node|
178
+ @properties.each do |p|
179
+ index_key = index_key(p)
180
+ document[index_key] ||= []
181
+ document[index_key] << other_node.send(p)
182
+ end
183
+ end
184
+ end
185
+ end
186
+
187
+ end