neo4j 1.0.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +141 -0
- data/CONTRIBUTORS +15 -0
- data/Gemfile +3 -0
- data/README.rdoc +2015 -0
- data/lib/neo4j.old/batch_inserter.rb +144 -0
- data/lib/neo4j.old/config.rb +138 -0
- data/lib/neo4j.old/event_handler.rb +73 -0
- data/lib/neo4j.old/extensions/activemodel.rb +158 -0
- data/lib/neo4j.old/extensions/aggregate.rb +12 -0
- data/lib/neo4j.old/extensions/aggregate/aggregate_enum.rb +40 -0
- data/lib/neo4j.old/extensions/aggregate/ext/node_mixin.rb +69 -0
- data/lib/neo4j.old/extensions/aggregate/node_aggregate.rb +8 -0
- data/lib/neo4j.old/extensions/aggregate/node_aggregate_mixin.rb +331 -0
- data/lib/neo4j.old/extensions/aggregate/node_aggregator.rb +216 -0
- data/lib/neo4j.old/extensions/aggregate/node_group.rb +43 -0
- data/lib/neo4j.old/extensions/aggregate/prop_group.rb +30 -0
- data/lib/neo4j.old/extensions/aggregate/property_enum.rb +24 -0
- data/lib/neo4j.old/extensions/aggregate/props_aggregate.rb +8 -0
- data/lib/neo4j.old/extensions/aggregate/props_aggregate_mixin.rb +31 -0
- data/lib/neo4j.old/extensions/aggregate/props_aggregator.rb +80 -0
- data/lib/neo4j.old/extensions/find_path.rb +117 -0
- data/lib/neo4j.old/extensions/graph_algo.rb +1 -0
- data/lib/neo4j.old/extensions/graph_algo/all_simple_paths.rb +133 -0
- data/lib/neo4j.old/extensions/graph_algo/neo4j-graph-algo-0.3.jar +0 -0
- data/lib/neo4j.old/extensions/reindexer.rb +104 -0
- data/lib/neo4j.old/extensions/rest.rb +21 -0
- data/lib/neo4j.old/extensions/rest/rest.rb +336 -0
- data/lib/neo4j.old/extensions/rest/rest_mixin.rb +193 -0
- data/lib/neo4j.old/extensions/rest/server.rb +50 -0
- data/lib/neo4j.old/extensions/rest/stubs.rb +141 -0
- data/lib/neo4j.old/extensions/rest_master.rb +34 -0
- data/lib/neo4j.old/extensions/rest_slave.rb +31 -0
- data/lib/neo4j.old/extensions/tx_tracker.rb +392 -0
- data/lib/neo4j.old/indexer.rb +187 -0
- data/lib/neo4j.old/jars.rb +6 -0
- data/lib/neo4j.old/jars/geronimo-jta_1.1_spec-1.1.1.jar +0 -0
- data/lib/neo4j.old/jars/neo4j-kernel-1.0.jar +0 -0
- data/lib/neo4j.old/mixins/java_list_mixin.rb +139 -0
- data/lib/neo4j.old/mixins/java_node_mixin.rb +205 -0
- data/lib/neo4j.old/mixins/java_property_mixin.rb +169 -0
- data/lib/neo4j.old/mixins/java_relationship_mixin.rb +60 -0
- data/lib/neo4j.old/mixins/migration_mixin.rb +157 -0
- data/lib/neo4j.old/mixins/node_mixin.rb +249 -0
- data/lib/neo4j.old/mixins/property_class_methods.rb +265 -0
- data/lib/neo4j.old/mixins/rel_class_methods.rb +167 -0
- data/lib/neo4j.old/mixins/relationship_mixin.rb +103 -0
- data/lib/neo4j.old/neo.rb +247 -0
- data/lib/neo4j.old/node.rb +49 -0
- data/lib/neo4j.old/reference_node.rb +15 -0
- data/lib/neo4j.old/relationship.rb +85 -0
- data/lib/neo4j.old/relationships/decl_relationship_dsl.rb +164 -0
- data/lib/neo4j.old/relationships/has_list.rb +101 -0
- data/lib/neo4j.old/relationships/has_n.rb +129 -0
- data/lib/neo4j.old/relationships/node_traverser.rb +138 -0
- data/lib/neo4j.old/relationships/relationship_dsl.rb +149 -0
- data/lib/neo4j.old/relationships/traversal_position.rb +50 -0
- data/lib/neo4j.old/relationships/wrappers.rb +51 -0
- data/lib/neo4j.old/search_result.rb +72 -0
- data/lib/neo4j.old/transaction.rb +254 -0
- data/lib/neo4j.old/version.rb +3 -0
- data/lib/neo4j.rb +50 -0
- data/lib/neo4j/config.rb +137 -0
- data/lib/neo4j/database.rb +43 -0
- data/lib/neo4j/equal.rb +22 -0
- data/lib/neo4j/event_handler.rb +91 -0
- data/lib/neo4j/index.rb +157 -0
- data/lib/neo4j/jars/geronimo-jta_1.1_spec-1.1.1.jar +0 -0
- data/lib/neo4j/jars/lucene-core-2.9.2.jar +0 -0
- data/lib/neo4j/jars/lucene-core-3.0.1.jar +0 -0
- data/lib/neo4j/jars/neo4j-index-1.1.jar +0 -0
- data/lib/neo4j/jars/neo4j-kernel-1.1.1.jar +0 -0
- data/lib/neo4j/jars/neo4j-kernel-1.1.jar +0 -0
- data/lib/neo4j/jars/neo4j-lucene-index-0.1-20100916.085626-67.jar +0 -0
- data/lib/neo4j/mapping/class_methods/index.rb +21 -0
- data/lib/neo4j/mapping/class_methods/property.rb +139 -0
- data/lib/neo4j/mapping/class_methods/relationship.rb +96 -0
- data/lib/neo4j/mapping/class_methods/rule.rb +135 -0
- data/lib/neo4j/mapping/decl_relationship_dsl.rb +151 -0
- data/lib/neo4j/mapping/has_n.rb +117 -0
- data/lib/neo4j/mapping/node_mixin.rb +70 -0
- data/lib/neo4j/neo4j.rb +65 -0
- data/lib/neo4j/node.rb +82 -0
- data/lib/neo4j/node_mixin.rb +4 -0
- data/lib/neo4j/node_relationship.rb +60 -0
- data/lib/neo4j/node_traverser.rb +141 -0
- data/lib/neo4j/property.rb +72 -0
- data/lib/neo4j/rails/lucene_connection_closer.rb +19 -0
- data/lib/neo4j/rails/model.rb +210 -0
- data/lib/neo4j/rails/railtie.rb +16 -0
- data/lib/neo4j/rails/transaction.rb +29 -0
- data/lib/neo4j/rails/value.rb +43 -0
- data/lib/neo4j/relationship.rb +88 -0
- data/lib/neo4j/relationship_traverser.rb +57 -0
- data/lib/neo4j/to_java.rb +17 -0
- data/lib/neo4j/transaction.rb +69 -0
- data/lib/neo4j/version.rb +3 -0
- data/neo4j.gemspec +30 -0
- 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
|