neo4j-core 0.0.1-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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