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.
- data/Gemfile +27 -0
- data/README.rdoc +27 -0
- data/config/neo4j/config.yml +102 -0
- data/lib/db/active_tx_log +1 -0
- data/lib/db/index/lucene-store.db +0 -0
- data/lib/db/index/lucene.log.1 +0 -0
- data/lib/db/index/lucene.log.active +0 -0
- data/lib/db/lock +0 -0
- data/lib/db/messages.log +530 -0
- data/lib/db/neostore +0 -0
- data/lib/db/neostore.id +0 -0
- data/lib/db/neostore.nodestore.db +0 -0
- data/lib/db/neostore.nodestore.db.id +0 -0
- data/lib/db/neostore.propertystore.db +0 -0
- data/lib/db/neostore.propertystore.db.arrays +0 -0
- data/lib/db/neostore.propertystore.db.arrays.id +0 -0
- data/lib/db/neostore.propertystore.db.id +0 -0
- data/lib/db/neostore.propertystore.db.index +0 -0
- data/lib/db/neostore.propertystore.db.index.id +0 -0
- data/lib/db/neostore.propertystore.db.index.keys +0 -0
- data/lib/db/neostore.propertystore.db.index.keys.id +0 -0
- data/lib/db/neostore.propertystore.db.strings +0 -0
- data/lib/db/neostore.propertystore.db.strings.id +0 -0
- data/lib/db/neostore.relationshipstore.db +0 -0
- data/lib/db/neostore.relationshipstore.db.id +0 -0
- data/lib/db/neostore.relationshiptypestore.db +0 -0
- data/lib/db/neostore.relationshiptypestore.db.id +0 -0
- data/lib/db/neostore.relationshiptypestore.db.names +0 -0
- data/lib/db/neostore.relationshiptypestore.db.names.id +0 -0
- data/lib/db/nioneo_logical.log.2 +0 -0
- data/lib/db/nioneo_logical.log.active +0 -0
- data/lib/db/tm_tx_log.1 +0 -0
- data/lib/neo4j/config.rb +139 -0
- data/lib/neo4j/cypher.rb +156 -0
- data/lib/neo4j/neo4j.rb +244 -0
- data/lib/neo4j/neo4j.rb~ +214 -0
- data/lib/neo4j/node.rb +39 -0
- data/lib/neo4j/relationship.rb +61 -0
- data/lib/neo4j/transaction.rb +86 -0
- data/lib/neo4j/type_converters/type_converters.rb +287 -0
- data/lib/neo4j-core/cypher/cypher.rb +867 -0
- data/lib/neo4j-core/cypher/result_wrapper.rb +39 -0
- data/lib/neo4j-core/database.rb +191 -0
- data/lib/neo4j-core/equal/equal.rb +23 -0
- data/lib/neo4j-core/event_handler.rb +265 -0
- data/lib/neo4j-core/index/class_methods.rb +117 -0
- data/lib/neo4j-core/index/index.rb +36 -0
- data/lib/neo4j-core/index/index_config.rb +112 -0
- data/lib/neo4j-core/index/indexer.rb +243 -0
- data/lib/neo4j-core/index/indexer_registry.rb +55 -0
- data/lib/neo4j-core/index/lucene_query.rb +264 -0
- data/lib/neo4j-core/lazy_map.rb +21 -0
- data/lib/neo4j-core/node/class_methods.rb +77 -0
- data/lib/neo4j-core/node/node.rb +47 -0
- data/lib/neo4j-core/property/property.rb +94 -0
- data/lib/neo4j-core/relationship/class_methods.rb +80 -0
- data/lib/neo4j-core/relationship/relationship.rb +97 -0
- data/lib/neo4j-core/relationship_set.rb +61 -0
- data/lib/neo4j-core/rels/rels.rb +147 -0
- data/lib/neo4j-core/rels/traverser.rb +99 -0
- data/lib/neo4j-core/to_java.rb +51 -0
- data/lib/neo4j-core/traversal/evaluator.rb +36 -0
- data/lib/neo4j-core/traversal/filter_predicate.rb +30 -0
- data/lib/neo4j-core/traversal/prune_evaluator.rb +20 -0
- data/lib/neo4j-core/traversal/rel_expander.rb +35 -0
- data/lib/neo4j-core/traversal/traversal.rb +130 -0
- data/lib/neo4j-core/traversal/traverser.rb +295 -0
- data/lib/neo4j-core/version.rb +5 -0
- data/lib/neo4j-core.rb +64 -0
- data/neo4j-core.gemspec +31 -0
- 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
|