neo4j-core 0.0.1-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/Gemfile +27 -0
  2. data/README.rdoc +27 -0
  3. data/config/neo4j/config.yml +102 -0
  4. data/lib/db/active_tx_log +1 -0
  5. data/lib/db/index/lucene-store.db +0 -0
  6. data/lib/db/index/lucene.log.1 +0 -0
  7. data/lib/db/index/lucene.log.active +0 -0
  8. data/lib/db/lock +0 -0
  9. data/lib/db/messages.log +530 -0
  10. data/lib/db/neostore +0 -0
  11. data/lib/db/neostore.id +0 -0
  12. data/lib/db/neostore.nodestore.db +0 -0
  13. data/lib/db/neostore.nodestore.db.id +0 -0
  14. data/lib/db/neostore.propertystore.db +0 -0
  15. data/lib/db/neostore.propertystore.db.arrays +0 -0
  16. data/lib/db/neostore.propertystore.db.arrays.id +0 -0
  17. data/lib/db/neostore.propertystore.db.id +0 -0
  18. data/lib/db/neostore.propertystore.db.index +0 -0
  19. data/lib/db/neostore.propertystore.db.index.id +0 -0
  20. data/lib/db/neostore.propertystore.db.index.keys +0 -0
  21. data/lib/db/neostore.propertystore.db.index.keys.id +0 -0
  22. data/lib/db/neostore.propertystore.db.strings +0 -0
  23. data/lib/db/neostore.propertystore.db.strings.id +0 -0
  24. data/lib/db/neostore.relationshipstore.db +0 -0
  25. data/lib/db/neostore.relationshipstore.db.id +0 -0
  26. data/lib/db/neostore.relationshiptypestore.db +0 -0
  27. data/lib/db/neostore.relationshiptypestore.db.id +0 -0
  28. data/lib/db/neostore.relationshiptypestore.db.names +0 -0
  29. data/lib/db/neostore.relationshiptypestore.db.names.id +0 -0
  30. data/lib/db/nioneo_logical.log.2 +0 -0
  31. data/lib/db/nioneo_logical.log.active +0 -0
  32. data/lib/db/tm_tx_log.1 +0 -0
  33. data/lib/neo4j/config.rb +139 -0
  34. data/lib/neo4j/cypher.rb +156 -0
  35. data/lib/neo4j/neo4j.rb +244 -0
  36. data/lib/neo4j/neo4j.rb~ +214 -0
  37. data/lib/neo4j/node.rb +39 -0
  38. data/lib/neo4j/relationship.rb +61 -0
  39. data/lib/neo4j/transaction.rb +86 -0
  40. data/lib/neo4j/type_converters/type_converters.rb +287 -0
  41. data/lib/neo4j-core/cypher/cypher.rb +867 -0
  42. data/lib/neo4j-core/cypher/result_wrapper.rb +39 -0
  43. data/lib/neo4j-core/database.rb +191 -0
  44. data/lib/neo4j-core/equal/equal.rb +23 -0
  45. data/lib/neo4j-core/event_handler.rb +265 -0
  46. data/lib/neo4j-core/index/class_methods.rb +117 -0
  47. data/lib/neo4j-core/index/index.rb +36 -0
  48. data/lib/neo4j-core/index/index_config.rb +112 -0
  49. data/lib/neo4j-core/index/indexer.rb +243 -0
  50. data/lib/neo4j-core/index/indexer_registry.rb +55 -0
  51. data/lib/neo4j-core/index/lucene_query.rb +264 -0
  52. data/lib/neo4j-core/lazy_map.rb +21 -0
  53. data/lib/neo4j-core/node/class_methods.rb +77 -0
  54. data/lib/neo4j-core/node/node.rb +47 -0
  55. data/lib/neo4j-core/property/property.rb +94 -0
  56. data/lib/neo4j-core/relationship/class_methods.rb +80 -0
  57. data/lib/neo4j-core/relationship/relationship.rb +97 -0
  58. data/lib/neo4j-core/relationship_set.rb +61 -0
  59. data/lib/neo4j-core/rels/rels.rb +147 -0
  60. data/lib/neo4j-core/rels/traverser.rb +99 -0
  61. data/lib/neo4j-core/to_java.rb +51 -0
  62. data/lib/neo4j-core/traversal/evaluator.rb +36 -0
  63. data/lib/neo4j-core/traversal/filter_predicate.rb +30 -0
  64. data/lib/neo4j-core/traversal/prune_evaluator.rb +20 -0
  65. data/lib/neo4j-core/traversal/rel_expander.rb +35 -0
  66. data/lib/neo4j-core/traversal/traversal.rb +130 -0
  67. data/lib/neo4j-core/traversal/traverser.rb +295 -0
  68. data/lib/neo4j-core/version.rb +5 -0
  69. data/lib/neo4j-core.rb +64 -0
  70. data/neo4j-core.gemspec +31 -0
  71. metadata +145 -0
@@ -0,0 +1,39 @@
1
+ module Neo4j
2
+ module Core
3
+ module Cypher
4
+ # Wraps the Cypher query result
5
+ # Loads the wrapper if possible and use symbol as keys.
6
+ class ResultWrapper
7
+ include Enumerable
8
+
9
+ # @return the original result from the Neo4j Cypher Engine
10
+ attr_reader :source
11
+
12
+ def initialize(source)
13
+ @source = source
14
+ end
15
+
16
+ # @return [Array<Symbol>] the columns in the query result
17
+ def columns
18
+ @source.columns.map{|x| x.to_sym}
19
+ end
20
+
21
+ # for the Enumerable contract
22
+ def each
23
+ @source.each { |row| yield map(row) }
24
+ end
25
+
26
+ # Maps each row
27
+ # @private
28
+ def map(row)
29
+ out = {} # move to a real hash!
30
+ row.each do |key, value|
31
+ out[key.to_sym] = value.respond_to?(:wrapper) ? value.wrapper : value
32
+ end
33
+ out
34
+ end
35
+
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,191 @@
1
+ module Neo4j
2
+ module Core
3
+ # Wraps both Java Neo4j GraphDatabaseService and Lucene.
4
+ # You can access the raw java neo4j and lucene db's with the <tt>graph</tt> and <tt>lucene</tt>
5
+ # properties.
6
+ #
7
+ # This class is also responsible for checking if there is already a running neo4j database.
8
+ # If one tries to start an already started database then a read only instance to neo4j will be used.
9
+ # Many of the methods here are delegated from the Neo4j module
10
+ #
11
+ # @private
12
+ class Database
13
+
14
+ # The Java graph database
15
+ # @see http://components.neo4j.org/neo4j/1.6.1/apidocs/org/neo4j/graphdb/GraphDatabaseService.html
16
+ # @return [Java::OrgNeo4jGraphdb::GraphDatabaseService]
17
+ attr_reader :graph
18
+
19
+ # The lucene index manager
20
+ # @see http://components.neo4j.org/neo4j/1.6.1/apidocs/org/neo4j/graphdb/index/IndexManager.html
21
+ # @return [Java::OrgNeo4jGraphdbIndex::IndexManager]
22
+ attr_reader :lucene
23
+
24
+ # @return [Neo4j::EventHandler] the event handler listining to commit events
25
+ attr_reader :event_handler
26
+
27
+ # @return [String] The location of the database
28
+ attr_reader :storage_path
29
+
30
+ alias_method :index, :lucene # needed by cypher
31
+
32
+ def initialize()
33
+ @event_handler = EventHandler.new
34
+ end
35
+
36
+ def self.default_embedded_db
37
+ @default_embedded_db || Java::OrgNeo4jKernel::EmbeddedGraphDatabase
38
+ end
39
+
40
+ def self.default_embedded_db=(db)
41
+ @default_embedded_db = db
42
+ end
43
+
44
+ # Private start method, use Neo4j.start instead
45
+ # @see Neo4j#start
46
+ def start
47
+ return if running?
48
+ @running = true
49
+ @storage_path = Config.storage_path
50
+
51
+ begin
52
+ if self.class.locked?
53
+ start_readonly_graph_db
54
+ elsif Neo4j::Config['ha.db']
55
+ start_ha_graph_db
56
+ Neo4j.migrate!
57
+ else
58
+ start_local_graph_db
59
+ Neo4j.migrate!
60
+ end
61
+ rescue
62
+ @running = false
63
+ raise
64
+ end
65
+
66
+ at_exit { shutdown }
67
+ end
68
+
69
+
70
+ def running? #:nodoc:
71
+ @running
72
+ end
73
+
74
+ # Returns true if the neo4j db was started in read only mode.
75
+ # This can occur if the database was locked (it was already one instance running).
76
+ # @see Neo4j#read_only?
77
+ def read_only?
78
+ @graph.java_class == Java::OrgNeo4jKernel::EmbeddedReadOnlyGraphDatabase
79
+ end
80
+
81
+ # check if the database is locked. A neo4j database is locked when the database is running.
82
+ def self.locked?
83
+ lock_file = File.join(Neo4j.config.storage_path, 'neostore')
84
+ return false unless File.exist?(lock_file)
85
+ rfile = java.io.RandomAccessFile.new(lock_file, 'rw')
86
+ begin
87
+ lock = rfile.getChannel.tryLock
88
+ lock.release if lock
89
+ return lock == nil # we got the lock, so that means it is not locked.
90
+ rescue Exception => e
91
+ return false
92
+ end
93
+ end
94
+
95
+ # Internal method, see Neo4j#shutdown
96
+ def shutdown
97
+ if @running
98
+ @graph.unregister_transaction_event_handler(@event_handler) unless read_only?
99
+ @event_handler.neo4j_shutdown(self)
100
+ @graph.shutdown
101
+ @graph = nil
102
+ @lucene = nil
103
+ @running = false
104
+ @neo4j_manager = nil
105
+ end
106
+ end
107
+
108
+
109
+ # @see Neo4j.management
110
+ def management(jmx_clazz)
111
+ @neo4j_manager ||= Java::OrgNeo4jManagement::Neo4jManager.new(@graph.get_management_bean(org.neo4j.jmx.Kernel.java_class))
112
+ @neo4j_manager.getBean(jmx_clazz.java_class)
113
+ end
114
+
115
+ # private method, used from Neo4j::Transaction.new
116
+ def begin_tx
117
+ @graph.begin_tx
118
+ end
119
+
120
+ # @see Neo4j.all_nodes
121
+ def each_node
122
+ iter = @graph.all_nodes.iterator
123
+ while (iter.hasNext)
124
+ yield iter.next.wrapper
125
+ end
126
+ end
127
+
128
+ # @see Neo4j._all_nodes
129
+ def _each_node
130
+ iter = @graph.all_nodes.iterator
131
+ while (iter.hasNext)
132
+ yield iter.next
133
+ end
134
+ end
135
+
136
+ private
137
+
138
+ def start_readonly_graph_db
139
+ Neo4j.logger.info "Starting Neo4j in readonly mode since the #{@storage_path} is locked"
140
+ @graph = Java::OrgNeo4jKernel::EmbeddedReadOnlyGraphDatabase.new(@storage_path, Config.to_java_map)
141
+ @lucene = @graph.index
142
+ end
143
+
144
+ def start_local_graph_db
145
+ Neo4j.logger.info "Starting local Neo4j using db #{@storage_path} using #{self.class.default_embedded_db}"
146
+ @graph = self.class.default_embedded_db.new(@storage_path, Config.to_java_map)
147
+ @graph.register_transaction_event_handler(@event_handler)
148
+ @lucene = @graph.index
149
+ @event_handler.neo4j_started(self)
150
+ end
151
+
152
+ # needed by cypher
153
+ def getNodeById(id) #:nodoc:
154
+ Neo4j::Node.load(id)
155
+ end
156
+
157
+ # needed by cypher
158
+ def getRelationshipById(id) #:nodoc:
159
+ Neo4j::Relationship.load(id)
160
+ end
161
+
162
+ def start_ha_graph_db
163
+ Neo4j.logger.info "starting Neo4j in HA mode, machine id: #{Neo4j.config['ha.server_id']} at #{Neo4j.config['ha.server']} db #{@storage_path}"
164
+ # Modify the public base classes for the HA Node and Relationships
165
+ # (instead of private Java::OrgNeo4jKernel::HighlyAvailableGraphDatabase::LookupNode)
166
+ Neo4j::Node.extend_java_class(Java::OrgNeo4jToolingWrap::WrappedNode)
167
+ Neo4j::Relationship.extend_java_class(Java::OrgNeo4jToolingWrap::WrappedRelationship)
168
+ @graph = Java::OrgNeo4jKernel::HighlyAvailableGraphDatabase.new(@storage_path, Neo4j.config.to_java_map)
169
+ @graph.register_transaction_event_handler(@event_handler)
170
+ @lucene = @graph.index
171
+ @event_handler.neo4j_started(self)
172
+ end
173
+
174
+ def start_external_db(external_graph_db)
175
+ begin
176
+ @running = true
177
+ @graph = external_graph_db
178
+ Neo4j.migrate!
179
+ @graph.register_transaction_event_handler(@event_handler)
180
+ @lucene = @graph.index #org.neo4j.index.impl.lucene.LuceneIndexProvider.new
181
+ @event_handler.neo4j_started(self)
182
+ Neo4j.logger.info("Started with external db")
183
+ rescue
184
+ @running = false
185
+ raise
186
+ end
187
+ end
188
+
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,23 @@
1
+ module Neo4j
2
+ module Core
3
+
4
+ # == This mixin is used for both nodes and relationships to decide if two entities are equal or not.
5
+ #
6
+ module Equal
7
+ def equal?(o)
8
+ eql?(o)
9
+ end
10
+
11
+ def eql?(o)
12
+ return false unless o.respond_to?(:getId)
13
+ o.getId == getId
14
+ end
15
+
16
+ def ==(o)
17
+ eql?(o)
18
+ end
19
+
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,265 @@
1
+ module Neo4j
2
+ module Core
3
+
4
+ # == Handles Transactional Events
5
+ #
6
+ # You can use this to receive event before the transaction commits.
7
+ # The following events are supported:
8
+ # * <tt>on_neo4j_started</tt>
9
+ # * <tt>on_neo4j_shutdown</tt>
10
+ # * <tt>on_node_created</tt>
11
+ # * <tt>on_node_deleted</tt>
12
+ # * <tt>on_relationship_created</tt>
13
+ # * <tt>on_relationship_deleted</tt>
14
+ # * <tt>on_property_changed</tt>
15
+ # * <tt>on_rel_property_changed</tt>
16
+ # * <tt>on_after_commit</tt>
17
+ #
18
+ # ==== on_neo4j_started(db)
19
+ #
20
+ # Called when the neo4j engine starts.
21
+ # Notice that the neo4j will be started automatically when the first neo4j operation is performed.
22
+ # You can also start Neo4j: <tt>Neo4j.start</tt>
23
+ #
24
+ # * <tt>db</tt> :: the Neo4j::Database instance
25
+ #
26
+ # ==== on_neo4j_shutdown(db)
27
+ #
28
+ # Called when the neo4j engine shutdown. You don't need to call <tt>Neo4j.shutdown</tt> since
29
+ # the it will automatically be shutdown when the application exits (using the at_exit ruby hook).
30
+ #
31
+ # * <tt>db</tt> :: the Neo4j::Database instance
32
+ #
33
+ # ==== on_after_commit(data, state)
34
+ #
35
+ # Called after the transaction has successfully committed.
36
+ # See http://api.neo4j.org/1.4/org/neo4j/graphdb/event/TransactionEventHandler.html for the data and state parameter.
37
+ #
38
+ # ==== on_node_created(node)
39
+ #
40
+ # * <tt>node</tt> :: the node that was created
41
+ #
42
+ # ==== on_node_deleted(node, old_props, deleted_relationship_set, deleted_node_identity_map)
43
+ #
44
+ # * <tt>node</tt> :: the node that was deleted
45
+ # * <tt>old_props</tt> :: a hash of the old properties this node had
46
+ # * <tt>deleted_relationship_set</tt> :: the set of deleted relationships. See Neo4j::Core::RelationshipSet
47
+ # * <tt>deleted_node_identity_map</tt> :: the identity map of deleted nodes. The key is the node id, and the value is the node
48
+ #
49
+ # ==== on_relationship_created(rel, created_node_identity_map)
50
+ #
51
+ # * <tt>rel</tt> :: the relationship that was created
52
+ # * <tt>created_node_identity_map</tt> :: the identity map of created nodes. The key is the node id, and the value is the node
53
+ #
54
+ # ==== on_relationship_deleted(rel, old_props, deleted_relationship_set, deleted_node_identity_map)
55
+ #
56
+ # * <tt>rel</tt> :: the relationship that was created
57
+ # * <tt>old_props</tt> :: a hash of the old properties this relationship had
58
+ # * <tt>deleted_relationship_set</tt> :: the set of deleted relationships. See Neo4j::Core::RelationshipSet
59
+ # * <tt>deleted_node_identity_map</tt> :: the identity map of deleted nodes. The key is the node id, and the value is the node
60
+ #
61
+ # ==== on_property_changed(node, key, old_value, new_value)
62
+ #
63
+ # * <tt>node</tt> :: the node
64
+ # * <tt>key</tt> :: the name of the property that was changed (String)
65
+ # * <tt>old_value</tt> :: old value of the property
66
+ # * <tt>new_value</tt> :: new value of the property
67
+ #
68
+ # ==== on_rel_property_changed(rel, key, old_value, new_value)
69
+ #
70
+ # * <tt>rel</tt> :: the node that was created
71
+ # * <tt>key</tt> :: the name of the property that was changed (String)
72
+ # * <tt>old_value</tt> :: old value of the property
73
+ # * <tt>new_value</tt> :: new value of the property
74
+ #
75
+ # ==== classes_changed(class_change_map)
76
+ # * <tt>class_change_map</tt> :: a hash with class names as keys, and class changes as values. See Neo4j::ClassChanges
77
+ #
78
+ # == Usage
79
+ #
80
+ # class MyListener
81
+ # def on_node_deleted(node, old_props, deleted_relationship_set, deleted_node_identity_map)
82
+ # end
83
+ # end
84
+ #
85
+ # # to add an listener without starting neo4j:
86
+ # Neo4j.unstarted_db.event_handler.add(MyListener.new)
87
+ #
88
+ # You only need to implement the methods that you need.
89
+ #
90
+ class EventHandler
91
+ include org.neo4j.graphdb.event.TransactionEventHandler
92
+
93
+ def initialize
94
+ @listeners = []
95
+ end
96
+
97
+
98
+ def after_commit(data, state)
99
+ @listeners.each { |li| li.on_after_commit(data, state) if li.respond_to?(:on_after_commit) }
100
+ end
101
+
102
+ def after_rollback(data, state)
103
+ end
104
+
105
+ def before_commit(data)
106
+ class_change_map = java.util.HashMap.new
107
+ created_node_identity_map = iterate_created_nodes(data.created_nodes, class_change_map)
108
+ deleted_node_identity_map = deleted_node_identity_map(data.deleted_nodes)
109
+ deleted_relationship_set = relationship_set(data.deleted_relationships)
110
+ removed_node_properties_map = property_map(data.removed_node_properties)
111
+ removed_relationship_properties_map = property_map(data.removed_relationship_properties)
112
+ add_deleted_nodes(data, class_change_map, removed_node_properties_map)
113
+ empty_map = java.util.HashMap.new
114
+ data.assigned_node_properties.each { |tx_data| property_changed(tx_data.entity, tx_data.key, tx_data.previously_commited_value, tx_data.value) unless tx_data.key == '_classname' }
115
+ data.removed_node_properties.each { |tx_data| property_changed(tx_data.entity, tx_data.key, tx_data.previously_commited_value, nil) unless deleted_node_identity_map.containsKey(tx_data.entity.getId) }
116
+ data.deleted_nodes.each { |node| node_deleted(node, removed_node_properties_map.get(node.getId)||empty_map, deleted_relationship_set, deleted_node_identity_map) }
117
+ data.created_relationships.each { |rel| relationship_created(rel, created_node_identity_map) }
118
+ data.deleted_relationships.each { |rel| relationship_deleted(rel, removed_relationship_properties_map.get(rel.getId)||empty_map, deleted_relationship_set, deleted_node_identity_map) }
119
+ data.assigned_relationship_properties.each { |tx_data| rel_property_changed(tx_data.entity, tx_data.key, tx_data.previously_commited_value, tx_data.value) }
120
+ data.removed_relationship_properties.each { |tx_data| rel_property_changed(tx_data.entity, tx_data.key, tx_data.previously_commited_value, nil) unless deleted_relationship_set.contains_rel?(tx_data.entity) }
121
+ classes_changed(class_change_map)
122
+ rescue Exception => e
123
+ # since these exceptions gets swallowed
124
+ puts "ERROR in before commit hook #{e}"
125
+ puts e.backtrace.join("\n")
126
+ end
127
+
128
+
129
+ def iterate_created_nodes(nodes, class_change_map)
130
+ identity_map = java.util.HashMap.new
131
+ nodes.each do |node|
132
+ identity_map.put(node.neo_id, node) #using put due to a performance regression in JRuby 1.6.4
133
+ instance_created(node, class_change_map)
134
+ node_created(node)
135
+ end
136
+ identity_map
137
+ end
138
+
139
+ def deleted_node_identity_map(nodes)
140
+ identity_map = java.util.HashMap.new
141
+ nodes.each { |node| identity_map.put(node.neo_id, node) } #using put due to a performance regression in JRuby 1.6.4
142
+ identity_map
143
+ end
144
+
145
+ def relationship_set(relationships)
146
+ relationship_set = Neo4j::Core::RelationshipSet.new #(relationships.size)
147
+ relationships.each { |rel| relationship_set.add(rel) }
148
+ relationship_set
149
+ end
150
+
151
+ def property_map(properties)
152
+ map = java.util.HashMap.new
153
+ properties.each do |property|
154
+ map(property.entity.getId, map).put(property.key, property.previously_commited_value)
155
+ end
156
+ map
157
+ end
158
+
159
+ def map(key, map)
160
+ map.get(key) || add_map(key, map)
161
+ end
162
+
163
+ def add_map(key, map)
164
+ map.put(key, java.util.HashMap.new)
165
+ map.get(key)
166
+ end
167
+
168
+ def add(listener)
169
+ @listeners << listener unless @listeners.include?(listener)
170
+ end
171
+
172
+ def remove(listener)
173
+ @listeners.delete(listener)
174
+ end
175
+
176
+ def remove_all
177
+ @listeners = []
178
+ end
179
+
180
+ def print
181
+ puts "Listeners #{@listeners.size}"
182
+ @listeners.each { |li| puts " Listener '#{li}'" }
183
+ end
184
+
185
+ def neo4j_started(db)
186
+ @listeners.each { |li| li.on_neo4j_started(db) if li.respond_to?(:on_neo4j_started) }
187
+ end
188
+
189
+ def neo4j_shutdown(db)
190
+ @listeners.each { |li| li.on_neo4j_shutdown(db) if li.respond_to?(:on_neo4j_shutdown) }
191
+ end
192
+
193
+ def node_created(node)
194
+ @listeners.each { |li| li.on_node_created(node) if li.respond_to?(:on_node_created) }
195
+ end
196
+
197
+ def node_deleted(node, old_properties, deleted_relationship_set, deleted_node_identity_map)
198
+ @listeners.each { |li| li.on_node_deleted(node, old_properties, deleted_relationship_set, deleted_node_identity_map) if li.respond_to?(:on_node_deleted) }
199
+ end
200
+
201
+ def relationship_created(relationship, created_node_identity_map)
202
+ @listeners.each { |li| li.on_relationship_created(relationship, created_node_identity_map) if li.respond_to?(:on_relationship_created) }
203
+ end
204
+
205
+ def relationship_deleted(relationship, old_props, deleted_relationship_set, deleted_node_identity_map)
206
+ @listeners.each { |li| li.on_relationship_deleted(relationship, old_props, deleted_relationship_set, deleted_node_identity_map) if li.respond_to?(:on_relationship_deleted) }
207
+ end
208
+
209
+ def property_changed(node, key, old_value, new_value)
210
+ @listeners.each { |li| li.on_property_changed(node, key, old_value, new_value) if li.respond_to?(:on_property_changed) }
211
+ end
212
+
213
+ def rel_property_changed(rel, key, old_value, new_value)
214
+ @listeners.each { |li| li.on_rel_property_changed(rel, key, old_value, new_value) if li.respond_to?(:on_rel_property_changed) }
215
+ end
216
+
217
+ def add_deleted_nodes(data, class_change_map, removed_node_properties_map)
218
+ data.deleted_nodes.each { |node| instance_deleted(node, removed_node_properties_map, class_change_map) }
219
+ end
220
+
221
+ def instance_created(node, class_change_map)
222
+ classname = node[:_classname]
223
+ class_change(classname, class_change_map).add(node) if classname
224
+ end
225
+
226
+ def instance_deleted(node, removed_node_properties_map, class_change_map)
227
+ properties = removed_node_properties_map.get(node.getId)
228
+ if properties
229
+ classname = properties.get("_classname")
230
+ class_change(classname, class_change_map).delete(node) if classname
231
+ end
232
+ end
233
+
234
+ def class_change(classname, class_change_map)
235
+ class_change_map.put(classname, ClassChanges.new) if class_change_map.get(classname).nil?
236
+ class_change_map.get(classname)
237
+ end
238
+
239
+ def classes_changed(changed_class_map)
240
+ @listeners.each { |li| li.classes_changed(changed_class_map) if li.respond_to?(:classes_changed) }
241
+ end
242
+ end
243
+
244
+ class ClassChanges
245
+ attr_accessor :added, :deleted
246
+
247
+ def initialize
248
+ self.added = []
249
+ self.deleted = []
250
+ end
251
+
252
+ def add(node)
253
+ self.added << node
254
+ end
255
+
256
+ def delete(node)
257
+ self.deleted << node
258
+ end
259
+
260
+ def net_change
261
+ self.added.size - self.deleted.size
262
+ end
263
+ end
264
+ end
265
+ end
@@ -0,0 +1,117 @@
1
+ module Neo4j
2
+ module Core
3
+ module Index
4
+ module ClassMethods
5
+
6
+ # The indexer which we are delegating to
7
+ attr_reader :_indexer
8
+
9
+
10
+ # TODO YARD
11
+ # Sets which indexer should be used for the given node class.
12
+ # You can share an indexer between several different classes.
13
+ #
14
+ # @example Using custom index
15
+ #
16
+ # class MyIndex
17
+ # extend Neo4j::Core::Index::ClassMethods
18
+ # include Neo4j::Core::Index
19
+ #
20
+ # node_indexer do
21
+ # index_names :exact => 'myindex_exact', :fulltext => 'myindex_fulltext'
22
+ # trigger_on :ntype => 'foo', :name => ['bar', 'foobar']
23
+ #
24
+ # prefix_index_name do
25
+ # return "" unless Neo4j.running?
26
+ # return "" unless @indexer_for.respond_to?(:ref_node_for_class)
27
+ # ref_node = @indexer_for.ref_node_for_class.wrapper
28
+ # prefix = ref_node.send(:_index_prefix) if ref_node.respond_to?(:_index_prefix)
29
+ # prefix ||= ref_node[:name] # To maintain backward compatiblity
30
+ # prefix.blank? ? "" : prefix + "_"
31
+ # end
32
+ #
33
+ #
34
+ # numeric do
35
+ # type = decl_props && decl_props[field.to_sym] && decl_props[field.to_sym][:type]
36
+ # type && !type.is_a?(String)
37
+ # end
38
+ # end
39
+ # end
40
+ #
41
+ # @example Using Neo4j::NodeMixin
42
+ #
43
+ # class Contact
44
+ # include Neo4j::NodeMixin
45
+ # index :name
46
+ # has_one :phone
47
+ # end
48
+ #
49
+ # class Phone
50
+ # include Neo4j::NodeMixin
51
+ # property :phone
52
+ # node_indexer Contact # put index on the Contact class instead
53
+ # index :phone
54
+ # end
55
+ #
56
+ # # Find an contact with a phone number, this works since they share the same index
57
+ # Contact.find('phone: 12345').first #=> a phone object !
58
+ #
59
+ # @return [Neo4j::Core::Index::Indexer] The indexer that should be used to index the given class
60
+ # @see Neo4j::Core::Index::IndexConfig for possible configuration values in the +config_dsl+ block
61
+ # @yield evaluated in the a Neo4j::Core::Index::IndexConfig object to configure it.
62
+ def node_indexer(&config_dsl)
63
+ # TODO reuse an existing index config
64
+ config = IndexConfig.new(:node)
65
+ config.instance_eval(&config_dsl)
66
+ indexer(config)
67
+ end
68
+
69
+ # Sets which indexer should be used for the given relationship class
70
+ # Same as #node_indexer except that it indexes relationships instead of nodes.
71
+ #
72
+ # @see #node_indexer
73
+ # @return [Neo4j::Core::Index::Indexer] The indexer that should be used to index the given class
74
+ def rel_indexer(clazz)
75
+ indexer(clazz, :rel)
76
+ end
77
+
78
+
79
+ def indexer(index_config)
80
+ @_indexer ||= IndexerRegistry.instance.register(Indexer.new(index_config))
81
+ end
82
+
83
+
84
+ class << self
85
+ private
86
+
87
+
88
+ # @macro [attach] index.delegate
89
+ # @method $1(*args, &block)
90
+ # Delegates the `$1` message to @_indexer instance with the supplied parameters.
91
+ # @see Neo4j::Core::Index::Indexer#$1
92
+ def delegate(method_name)
93
+ class_eval(<<-EOM, __FILE__, __LINE__)
94
+ def #{method_name}(*args, &block)
95
+ _indexer.send(:#{method_name}, *args, &block)
96
+ end
97
+ EOM
98
+ end
99
+
100
+ end
101
+
102
+ delegate :index
103
+ delegate :find
104
+ delegate :index?
105
+ delegate :has_index_type?
106
+ delegate :rm_index_type
107
+ delegate :rm_index_config
108
+ delegate :add_index
109
+ delegate :rm_index
110
+ delegate :index_type
111
+ delegate :index_name_for_type
112
+ end
113
+
114
+
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,36 @@
1
+ module Neo4j
2
+ module Core
3
+
4
+ # A mixin which adds indexing behaviour to your own Ruby class
5
+ # You are expected to implement the method `wrapped_entity` returning the underlying Neo4j Node or Relationship
6
+ module Index
7
+
8
+ # Adds an index on the given property
9
+ # Notice that you normally don't have to do that since you simply can declare
10
+ # that the property and index should be updated automatically by using the class method #index.
11
+ #
12
+ # The index operation will take place immediately unlike when using the Neo4j::Core::Index::ClassMethods#index
13
+ # method which instead will guarantee that the neo4j database and the lucene database will be consistent.
14
+ # It uses a two phase commit when the transaction is about to be committed.
15
+ #
16
+ # @see Neo4j::Core::Index::ClassMethods#add_index
17
+ #
18
+ def add_index(field, value=self[field])
19
+ converted_value = Neo4j::TypeConverters.convert(value, field, self.class)
20
+ self.class.add_index(wrapped_entity, field.to_s, converted_value)
21
+ end
22
+
23
+ # Removes an index on the given property.
24
+ # Just like #add_index this is normally not needed since you instead can declare it with the
25
+ # #index class method instead.
26
+ #
27
+ # @see Neo4j::Core::Index::ClassMethods#rm_index
28
+ #
29
+ def rm_index(field, value=self[field])
30
+ self.class.rm_index(wrapped_entity, field.to_s, value)
31
+ end
32
+
33
+ end
34
+ end
35
+
36
+ end