neo4j-core 0.0.1-java
Sign up to get free protection for your applications and to get access to all the features.
- 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
|