neo4j 1.2.4-java → 1.2.5-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/CHANGELOG CHANGED
@@ -1,3 +1,16 @@
1
+ == 1.2.5 / 2011-10-21
2
+ * Faster traversals by avoiding loading Ruby wrappers (new method 'raw' on traversals) [#189]
3
+ * Support for IdentityMap [#188]
4
+ * Improved performance in event handler (Vivek Prahlad)
5
+ * Fixing issue with validates_presence_of validation (Vivek Prahlad)
6
+ * Implemented compositions support on Neo4j::Rails::Relationship (Kalyan Akella)
7
+ * Added after_initialize callback (Deepak N)
8
+ * Fixed performance issues on node deleted (Vivek Prahlad)
9
+ * Fixed a performance issue in the index_registry (Vivek Prahlad)
10
+ * Fixed uniqueness validation for :case_sensitive => false (Vivek Prahlad)
11
+ * Fixed update_attributes deleting relations when model is invalid (Deepak N)
12
+ * Fixed timestamp rails generator (Marcio Toshio)
13
+
1
14
  == 1.2.4 / 2011-10-07
2
15
  * Support for traversing with Neo4j::Node#eval_paths and setting uniqueness on traversals [#187]
3
16
  * Removed unnecessary node creation on database start up (class nodes attached to reference node) (Vivek Prahlad)
@@ -2,7 +2,8 @@ Maintainer:
2
2
  Andreas Ronge <andreas dot ronge at gmail dot com>
3
3
 
4
4
  Contributors:
5
-
5
+ * Marcio Toshio
6
+ * Kalyan Akella
6
7
  * Vivek Prahlad
7
8
  * Deepak N
8
9
  * Frédéric Vanclef
@@ -7,6 +7,11 @@ storage_path: db
7
7
  # If disabled all custom rules will also be unavailable.
8
8
  enable_rules: true
9
9
 
10
+ # if identity map should be on or not
11
+ # It may impact the performance. Using the identity map will keep all loaded wrapper node/relationship
12
+ # object in memory for each thread and transaction - which may speed up or slow down operations.
13
+ identity_map: false
14
+
10
15
  # When using the Neo4j::Model you can let neo4j automatically set timestamps when updating/creating nodes.
11
16
  # If set to true neo4j.rb automatically timestamps create and update operations if the model has properties named created_at/created_on or updated_at/updated_on
12
17
  # (similar to ActiveRecord).
@@ -27,11 +27,11 @@ class Neo4j::Generators::ModelGenerator < Neo4j::Generators::Base #:nodoc:
27
27
 
28
28
  def timestamp_statements
29
29
  %q{
30
- property :created_at, DateTime
31
- # property :created_on, Date
30
+ property :created_at, :type => DateTime
31
+ # property :created_on, :type => Date
32
32
 
33
- property :updated_at, DateTime
34
- # property :updated_on, Date
33
+ property :updated_at, :type => DateTime
34
+ # property :updated_on, :type => Date
35
35
  }
36
36
  end
37
37
 
@@ -63,7 +63,7 @@ end
63
63
 
64
64
  module Neo4j
65
65
  include Java
66
-
66
+
67
67
  # Enumerator has been moved to top level in Ruby 1.9.2, make it compatible with Ruby 1.8.7
68
68
  Enumerator = Enumerable::Enumerator unless defined? Enumerator
69
69
  end
@@ -72,6 +72,7 @@ require 'neo4j/version'
72
72
  require 'neo4j/neo4j'
73
73
  require 'neo4j/node'
74
74
  require 'neo4j/relationship'
75
+ require 'neo4j/relationship_set'
75
76
 
76
77
  require 'neo4j/type_converters/type_converters'
77
78
 
@@ -103,4 +104,7 @@ require 'neo4j/batch/batch'
103
104
 
104
105
  require 'orm_adapter/adapters/neo4j'
105
106
 
107
+ require 'neo4j/identity_map'
108
+
109
+
106
110
 
@@ -14,6 +14,7 @@ module Neo4j
14
14
  # <tt>:timestamps</tt>:: default <tt>true</tt> for Rails Neo4j::Model - if timestamps should be used when saving the model
15
15
  # <tt>:lucene</tt>:: default hash keys: <tt>:fulltext</tt>, <tt>:exact</tt> configuration how the lucene index is stored
16
16
  # <tt>:enable_rules</tt>:: default true, if false the _all relationship to all instances will not be created and custom rules will not be available.
17
+ # <tt>:identity_map</tt>:: default false, See Neo4j::IdentityMap
17
18
  #
18
19
  class Config
19
20
  # This code is copied from merb-core/config.rb.
@@ -12,6 +12,7 @@ module Neo4j
12
12
  # * <tt>on_relationship_deleted</tt>
13
13
  # * <tt>on_property_changed</tt>
14
14
  # * <tt>on_rel_property_changed</tt>
15
+ # * <tt>on_after_commit</tt>
15
16
  #
16
17
  # ==== on_neo4j_started(db)
17
18
  #
@@ -28,26 +29,33 @@ module Neo4j
28
29
  #
29
30
  # * <tt>db</tt> :: the Neo4j::Database instance
30
31
  #
32
+ # ==== on_after_commit(data, state)
33
+ #
34
+ # Called after the transaction has successfully committed.
35
+ # See http://api.neo4j.org/1.4/org/neo4j/graphdb/event/TransactionEventHandler.html for the data and state parameter.
36
+ #
31
37
  # ==== on_node_created(node)
32
38
  #
33
39
  # * <tt>node</tt> :: the node that was created
34
40
  #
35
- # ==== on_node_deleted(node, old_props, tx_data)
41
+ # ==== on_node_deleted(node, old_props, deleted_relationship_set, deleted_node_identity_map)
36
42
  #
37
43
  # * <tt>node</tt> :: the node that was deleted
38
44
  # * <tt>old_props</tt> :: a hash of the old properties this node had
39
- # * <tt>tx_data</tt> :: the Java Transaction Data object, http://api.neo4j.org/current/org/neo4j/graphdb/event/TransactionData.html
45
+ # * <tt>deleted_relationship_set</tt> :: the set of deleted relationships. See Neo4j::RelationshipSet
46
+ # * <tt>deleted_node_identity_map</tt> :: the identity map of deleted nodes. The key is the node id, and the value is the node
40
47
  #
41
- # ==== on_relationship_created(rel, tx_data)
48
+ # ==== on_relationship_created(rel, created_node_identity_map)
42
49
  #
43
50
  # * <tt>rel</tt> :: the relationship that was created
44
- # * <tt>tx_data</tt> :: the Java Transaction Data object, http://api.neo4j.org/current/org/neo4j/graphdb/event/TransactionData.html
51
+ # * <tt>created_node_identity_map</tt> :: the identity map of created nodes. The key is the node id, and the value is the node
45
52
  #
46
- # ==== on_relationship_deleted(rel, old_props, tx_data)
53
+ # ==== on_relationship_deleted(rel, old_props, deleted_relationship_set, deleted_node_identity_map)
47
54
  #
48
55
  # * <tt>rel</tt> :: the relationship that was created
49
56
  # * <tt>old_props</tt> :: a hash of the old properties this relationship had
50
- # * <tt>tx_data</tt> :: the Java Transaction Data object, http://api.neo4j.org/current/org/neo4j/graphdb/event/TransactionData.html
57
+ # * <tt>deleted_relationship_set</tt> :: the set of deleted relationships. See Neo4j::RelationshipSet
58
+ # * <tt>deleted_node_identity_map</tt> :: the identity map of deleted nodes. The key is the node id, and the value is the node
51
59
  #
52
60
  # ==== on_property_changed(node, key, old_value, new_value)
53
61
  #
@@ -66,7 +74,7 @@ module Neo4j
66
74
  # == Usage
67
75
  #
68
76
  # class MyListener
69
- # def on_node_deleted(node, old_props, tx_data)
77
+ # def on_node_deleted(node, old_props, deleted_relationship_set, deleted_node_identity_map)
70
78
  # end
71
79
  # end
72
80
  #
@@ -84,34 +92,56 @@ module Neo4j
84
92
 
85
93
 
86
94
  def after_commit(data, state)
95
+ @listeners.each {|li| li.on_after_commit(data, state) if li.respond_to?(:on_after_commit)}
87
96
  end
88
97
 
89
98
  def after_rollback(data, state)
90
99
  end
91
100
 
92
101
  def before_commit(data)
102
+ created_node_identity_map = node_identity_map(data.created_nodes)
103
+ deleted_node_identity_map = node_identity_map(data.deleted_nodes)
104
+ deleted_relationship_set = relationship_set(data.deleted_relationships)
105
+ removed_node_properties_map = property_map(data.removed_node_properties)
106
+ removed_relationship_properties_map = property_map(data.removed_relationship_properties)
107
+ empty_map = java.util.HashMap.new
93
108
  data.created_nodes.each{|node| node_created(node)}
94
109
  data.assigned_node_properties.each { |tx_data| property_changed(tx_data.entity, tx_data.key, tx_data.previously_commited_value, tx_data.value) }
95
- data.removed_node_properties.each { |tx_data| property_changed(tx_data.entity, tx_data.key, tx_data.previously_commited_value, nil) unless data.deleted_nodes.include?(tx_data.entity) }
96
- data.deleted_nodes.each { |node| node_deleted(node, deleted_properties_for(node,data), data)}
97
- data.created_relationships.each {|rel| relationship_created(rel, data)}
98
- data.deleted_relationships.each {|rel| relationship_deleted(rel, deleted_rel_properties_for(rel, data), data)}
110
+ 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) }
111
+ data.deleted_nodes.each { |node| node_deleted(node, removed_node_properties_map.get(node.getId)||empty_map, deleted_relationship_set, deleted_node_identity_map)}
112
+ data.created_relationships.each {|rel| relationship_created(rel, created_node_identity_map)}
113
+ data.deleted_relationships.each {|rel| relationship_deleted(rel, removed_relationship_properties_map.get(rel.getId)||empty_map, deleted_relationship_set, deleted_node_identity_map)}
99
114
  data.assigned_relationship_properties.each { |tx_data| rel_property_changed(tx_data.entity, tx_data.key, tx_data.previously_commited_value, tx_data.value) }
100
- data.removed_relationship_properties.each {|tx_data| rel_property_changed(tx_data.entity, tx_data.key, tx_data.previously_commited_value, nil) unless data.deleted_relationships.include?(tx_data.entity) }
115
+ 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) }
101
116
  end
102
117
 
103
- def deleted_properties_for(node, data)
104
- data.removed_node_properties.find_all{|tx_data| tx_data.entity == node}.inject({}) do |memo, tx_data|
105
- memo[tx_data.key] = tx_data.previously_commited_value
106
- memo
107
- end
118
+ def node_identity_map(nodes)
119
+ identity_map = java.util.HashMap.new(nodes.size)
120
+ nodes.each{|node| identity_map.put(node.neo_id,node)}#using put due to a performance regression in JRuby 1.6.4
121
+ identity_map
122
+ end
123
+
124
+ def relationship_set(relationships)
125
+ relationship_set = Neo4j::RelationshipSet.new(relationships.size)
126
+ relationships.each{|rel| relationship_set.add(rel)}
127
+ relationship_set
108
128
  end
109
129
 
110
- def deleted_rel_properties_for(rel, data)
111
- data.removed_relationship_properties.find_all{|tx_data| tx_data.entity == rel}.inject({}) do |memo, tx_data|
112
- memo[tx_data.key] = tx_data.previously_commited_value
113
- memo
130
+ def property_map(properties)
131
+ map = java.util.HashMap.new
132
+ properties.each do |property|
133
+ map(property.entity.getId, map).put(property.key, property.previously_commited_value)
114
134
  end
135
+ map
136
+ end
137
+
138
+ def map(key,map)
139
+ map.get(key) || add_map(key,map)
140
+ end
141
+
142
+ def add_map(key,map)
143
+ map.put(key, java.util.HashMap.new)
144
+ map.get(key)
115
145
  end
116
146
 
117
147
  def add(listener)
@@ -143,16 +173,16 @@ module Neo4j
143
173
  @listeners.each {|li| li.on_node_created(node) if li.respond_to?(:on_node_created)}
144
174
  end
145
175
 
146
- def node_deleted(node,old_properties, data)
147
- @listeners.each {|li| li.on_node_deleted(node,old_properties, data) if li.respond_to?(:on_node_deleted)}
176
+ def node_deleted(node,old_properties, deleted_relationship_set, deleted_node_identity_map)
177
+ @listeners.each {|li| li.on_node_deleted(node,old_properties, deleted_relationship_set, deleted_node_identity_map) if li.respond_to?(:on_node_deleted)}
148
178
  end
149
179
 
150
- def relationship_created(relationship, tx_data)
151
- @listeners.each {|li| li.on_relationship_created(relationship, tx_data) if li.respond_to?(:on_relationship_created)}
180
+ def relationship_created(relationship, created_node_identity_map)
181
+ @listeners.each {|li| li.on_relationship_created(relationship, created_node_identity_map) if li.respond_to?(:on_relationship_created)}
152
182
  end
153
183
 
154
- def relationship_deleted(relationship, old_props, data)
155
- @listeners.each {|li| li.on_relationship_deleted(relationship, old_props, data) if li.respond_to?(:on_relationship_deleted)}
184
+ def relationship_deleted(relationship, old_props, deleted_relationship_set, deleted_node_identity_map)
185
+ @listeners.each {|li| li.on_relationship_deleted(relationship, old_props, deleted_relationship_set, deleted_node_identity_map) if li.respond_to?(:on_relationship_deleted)}
156
186
  end
157
187
 
158
188
  def property_changed(node, key, old_value, new_value)
@@ -0,0 +1,143 @@
1
+ # https://github.com/rails/rails/blob/master/activerecord/lib/active_record/identity_map.rb
2
+ # https://github.com/rails/rails/pull/76
3
+
4
+ module Neo4j
5
+
6
+ # = Neo4j Identity Map
7
+ #
8
+ # Ensures that each object gets loaded only once by keeping every loaded
9
+ # object in a map. Looks up objects using the map when referring to them.
10
+ #
11
+ # More information on Identity Map pattern:
12
+ # http://www.martinfowler.com/eaaCatalog/identityMap.html
13
+ #
14
+ # == Configuration
15
+ #
16
+ # In order to enable IdentityMap, set <tt>config.neo4j.identity_map = true</tt>
17
+ # in your <tt>config/application.rb</tt> file. If used outside rails, set Neo4j::Config[:identity_map] = true.
18
+ #
19
+ # IdentityMap is disabled by default and still in development (i.e. use it with care).
20
+ #
21
+ module IdentityMap
22
+
23
+ class << self
24
+ def enabled=(flag)
25
+ Thread.current[:neo4j_identity_map] = flag
26
+ end
27
+
28
+ def enabled
29
+ Thread.current[:neo4j_identity_map]
30
+ end
31
+
32
+ alias enabled? enabled
33
+
34
+ def node_repository
35
+ Thread.current[:node_identity_map] ||= java.util.HashMap.new
36
+ end
37
+
38
+ def rel_repository
39
+ Thread.current[:rel_identity_map] ||= java.util.HashMap.new
40
+ end
41
+
42
+ def repository_for(neo_entity)
43
+ return nil unless enabled?
44
+ if neo_entity.class == Neo4j::Node
45
+ node_repository
46
+ elsif neo_entity.class == Neo4j::Relationship
47
+ rel_repository
48
+ else
49
+ nil
50
+ end
51
+ end
52
+
53
+ def use
54
+ old, self.enabled = enabled, true
55
+ yield if block_given?
56
+ ensure
57
+ self.enabled = old
58
+ clear
59
+ end
60
+
61
+ def without
62
+ old, self.enabled = enabled, false
63
+ yield if block_given?
64
+ ensure
65
+ self.enabled = old
66
+ end
67
+
68
+ def get(neo_entity)
69
+ r = repository_for(neo_entity)
70
+ r && r.get(neo_entity.neo_id)
71
+ end
72
+
73
+ def add(neo_entity, wrapped_entity)
74
+ r = repository_for(neo_entity)
75
+ r && r.put(neo_entity.neo_id, wrapped_entity)
76
+ end
77
+
78
+ def remove(neo_entity)
79
+ r = repository_for(neo_entity)
80
+ r && r.remove(neo_entity.neo_id)
81
+ end
82
+
83
+ def remove_node_by_id(node_id)
84
+ node_repository.remove(node_id)
85
+ end
86
+
87
+ def remove_rel_by_id(rel_id)
88
+ rel_repository.remove(rel_id)
89
+ end
90
+
91
+ def clear
92
+ node_repository.clear
93
+ rel_repository.clear
94
+ end
95
+
96
+ def on_after_commit(*)
97
+ clear
98
+ end
99
+
100
+ def on_neo4j_started(db)
101
+ if not Neo4j::Config[:identity_map]
102
+ db.event_handler.remove(self)
103
+ end
104
+ end
105
+
106
+ end
107
+
108
+
109
+ class Middleware
110
+ class Body #:nodoc:
111
+ def initialize(target, original)
112
+ @target = target
113
+ @original = original
114
+ end
115
+
116
+ def each(&block)
117
+ @target.each(&block)
118
+ end
119
+
120
+ def close
121
+ @target.close if @target.respond_to?(:close)
122
+ ensure
123
+ IdentityMap.enabled = @original
124
+ IdentityMap.clear
125
+ end
126
+ end
127
+
128
+ def initialize(app)
129
+ @app = app
130
+ end
131
+
132
+ def call(env)
133
+ enabled = IdentityMap.enabled
134
+ IdentityMap.enabled = Neo4j::Config[:identity_map]
135
+ status, headers, body = @app.call(env)
136
+ [status, headers, Body.new(body, enabled)]
137
+ end
138
+ end
139
+ end
140
+ end
141
+
142
+ Neo4j.unstarted_db.event_handler.add(Neo4j::IdentityMap)
143
+
@@ -98,15 +98,13 @@ module Neo4j
98
98
  end
99
99
  end
100
100
 
101
- def remove_index_on_fields(node, props, tx_data) #:nodoc:
101
+ def remove_index_on_fields(node, props, deleted_relationship_set) #:nodoc:
102
102
  @field_types.keys.each { |field| rm_index(node, field, props[field]) if props[field] }
103
103
  # remove all via indexed fields
104
104
  @via_relationships.each_value do |dsl|
105
105
  indexer = dsl.target_class._indexer
106
- tx_data.deleted_relationships.each do |rel|
107
- start_node = rel._start_node
108
- next if node != rel._end_node
109
- indexer.remove_index_on_fields(start_node, props, tx_data)
106
+ deleted_relationship_set.relationships(node.getId).each do |rel|
107
+ indexer.remove_index_on_fields(rel._start_node, props, deleted_relationship_set)
110
108
  end
111
109
  end
112
110
  end
@@ -18,9 +18,9 @@ module Neo4j
18
18
  @@indexers[classname]
19
19
  end
20
20
 
21
- def on_node_deleted(node, old_props, tx_data)
21
+ def on_node_deleted(node, old_props, deleted_relationship_set, deleted_identity_map)
22
22
  indexer = find_by_class(old_props['_classname'] || node.class.to_s)
23
- indexer && indexer.remove_index_on_fields(node, old_props, tx_data)
23
+ indexer && indexer.remove_index_on_fields(node, old_props, deleted_relationship_set)
24
24
  end
25
25
 
26
26
  def on_property_changed(node, field, old_val, new_val)
@@ -35,23 +35,23 @@ module Neo4j
35
35
  on_property_changed(rel, field, old_val, new_val)
36
36
  end
37
37
 
38
- def on_relationship_created(rel, tx_data)
38
+ def on_relationship_created(rel,created_identity_map)
39
39
  end_node = rel._end_node
40
40
  # if end_node was created in this transaction then it will be handled in on_property_changed
41
- created = tx_data.created_nodes.find{|n| n.neo_id == end_node.neo_id}
41
+ created = created_identity_map.get(end_node.neo_id)
42
42
  unless created
43
43
  indexer = find_by_class(end_node['_classname'])
44
44
  indexer && indexer.update_on_new_relationship(rel)
45
45
  end
46
46
  end
47
47
 
48
- def on_relationship_deleted(rel, old_props, tx_data)
49
- on_node_deleted(rel, old_props, tx_data)
48
+ def on_relationship_deleted(rel, old_props, deleted_relationship_set, deleted_identity_map)
49
+ on_node_deleted(rel, old_props, deleted_relationship_set, deleted_identity_map)
50
50
  # if only the relationship has been deleted then we have to remove the index
51
51
  # if both the relationship and the node has been deleted then the index will be removed in the
52
52
  # on_node_deleted callback
53
53
  end_node = rel._end_node
54
- deleted = tx_data.deleted_nodes.find{|n| n.neo_id == end_node.neo_id}
54
+ deleted = deleted_identity_map.get(end_node.neo_id)
55
55
  unless deleted
56
56
  indexer = find_by_class(end_node['_classname'])
57
57
  indexer && indexer.update_on_deleted_relationship(rel)
@@ -117,7 +117,7 @@ module Neo4j
117
117
 
118
118
  # Performs a range query
119
119
  # Notice that if you don't specify a type when declaring a property a String range query will be performed.
120
- #
120
+ #
121
121
  def between(lower, upper, lower_incusive=false, upper_inclusive=false)
122
122
  raise "Expected a symbol. Syntax for range queries example: index(:weight).between(a,b)" unless Symbol === @query
123
123
  raise "Can't only do range queries on Neo4j::NodeMixin, Neo4j::Model, Neo4j::RelationshipMixin" unless @decl_props
@@ -188,7 +188,7 @@ module Neo4j
188
188
  type = case
189
189
  when Float == decl_type
190
190
  org.apache.lucene.search.SortField::DOUBLE
191
- when Fixnum == decl_type
191
+ when Fixnum == decl_type || DateTime == decl_type || Date == decl_type || Time == decl_type
192
192
  org.apache.lucene.search.SortField::LONG
193
193
  else
194
194
  org.apache.lucene.search.SortField::STRING
@@ -3,9 +3,13 @@ module Neo4j
3
3
  # === Mixin responsible for loading Ruby wrappers for Neo4j Nodes and Relationship.
4
4
  #
5
5
  module Load
6
- def wrapper(node) # :nodoc:
7
- return node unless node.property?(:_classname)
8
- to_class(node[:_classname]).load_wrapper(node)
6
+ def wrapper(entity) # :nodoc:
7
+ return entity unless entity.property?(:_classname)
8
+ existing_instance = Neo4j::IdentityMap.get(entity)
9
+ return existing_instance if existing_instance
10
+ new_instance = to_class(entity[:_classname]).load_wrapper(entity)
11
+ Neo4j::IdentityMap.add(entity, new_instance)
12
+ new_instance
9
13
  end
10
14
 
11
15
  def to_class(class_name) # :nodoc:
@@ -13,8 +17,8 @@ module Neo4j
13
17
  end
14
18
 
15
19
  # Checks if the given entity (node/relationship) or entity id (#neo_id) exists in the database.
16
- def exist?(node_or_node_id, db = Neo4j.started_db)
17
- id = node_or_node_id.kind_of?(Fixnum) ? node_or_node_id : node_or_node_id.id
20
+ def exist?(entity_or_entity_id, db = Neo4j.started_db)
21
+ id = entity_or_entity_id.kind_of?(Fixnum) ? entity_or_entity_id : entity_or_entity_id.id
18
22
  _load(id, db) != nil
19
23
  end
20
24
  end
@@ -28,7 +28,7 @@ module Neo4j
28
28
  def wrapped_entity #:nodoc:
29
29
  self
30
30
  end
31
-
31
+
32
32
  def wrapper #:nodoc:
33
33
  self.class.wrapper(self)
34
34
  end
@@ -39,6 +39,7 @@ module Neo4j
39
39
  def new(*args)
40
40
  node = Neo4j::Node.create
41
41
  wrapped_node = super()
42
+ Neo4j::IdentityMap.add(node, wrapped_node)
42
43
  wrapped_node.init_on_load(node)
43
44
  wrapped_node.init_on_create(*args)
44
45
  wrapped_node
@@ -216,6 +216,10 @@ module Neo4j
216
216
  def attribute?(name)
217
217
  name[0] != ?_ && property?(name)
218
218
  end
219
+
220
+ def _classname
221
+ self[:_classname]
222
+ end
219
223
 
220
224
  # To get ActiveModel::Dirty to work, we need to be able to call undeclared
221
225
  # properties as though they have get methods
@@ -4,7 +4,7 @@ module Neo4j
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  CALLBACKS = [
7
- :before_validation, :after_validation,
7
+ :after_initialize, :before_validation, :after_validation,
8
8
  :before_create, :around_create, :after_create,
9
9
  :before_destroy, :around_destroy, :after_destroy,
10
10
  :before_save, :around_save, :after_save,
@@ -12,35 +12,40 @@ module Neo4j
12
12
  ].freeze
13
13
 
14
14
  included do
15
- [:valid?, :create_or_update, :create, :update, :destroy].each do |method|
15
+ [:initialize, :valid?, :create_or_update, :create, :update, :destroy].each do |method|
16
16
  alias_method_chain method, :callbacks
17
17
  end
18
-
18
+
19
19
  extend ActiveModel::Callbacks
20
-
20
+
21
+ define_model_callbacks :initialize, :only => :after
21
22
  define_model_callbacks :validation, :create, :save, :update, :destroy
22
23
  end
23
-
24
+
24
25
  def valid_with_callbacks?(*) #:nodoc:
25
26
  _run_validation_callbacks { valid_without_callbacks? }
26
27
  end
27
-
28
+
28
29
  def destroy_with_callbacks #:nodoc:
29
30
  _run_destroy_callbacks { destroy_without_callbacks }
30
31
  end
31
-
32
+
32
33
  private
33
34
  def create_or_update_with_callbacks #:nodoc:
34
35
  _run_save_callbacks { create_or_update_without_callbacks }
35
36
  end
36
-
37
+
37
38
  def create_with_callbacks #:nodoc:
38
39
  _run_create_callbacks { create_without_callbacks }
39
40
  end
40
-
41
+
41
42
  def update_with_callbacks(*) #:nodoc:
42
43
  _run_update_callbacks { update_without_callbacks }
43
44
  end
45
+
46
+ def initialize_with_callbacks(*args, &block) #:nodoc:
47
+ _run_initialize_callbacks { initialize_without_callbacks(*args, &block) }
48
+ end
44
49
  end
45
50
  end
46
51
  end
@@ -5,7 +5,7 @@ module Neo4j
5
5
 
6
6
  included do
7
7
  extend TxMethods
8
- tx_methods :destroy, :create, :update, :update_nested_attributes, :delete
8
+ tx_methods :destroy, :create, :update, :update_nested_attributes, :delete, :update_attributes, :update_attributes!
9
9
  end
10
10
 
11
11
  # Persist the object to the database. Validations and Callbacks are included
@@ -154,6 +154,7 @@ module Neo4j
154
154
  def create
155
155
  node = Neo4j::Node.new
156
156
  @_java_node = node
157
+ Neo4j::IdentityMap.add(node, self)
157
158
  init_on_create
158
159
  clear_changes
159
160
  clear_relationships
@@ -172,7 +173,9 @@ module Neo4j
172
173
  end
173
174
 
174
175
  def reload_from_database
176
+ Neo4j::IdentityMap.remove_node_by_id(id)
175
177
  if reloaded = self.class.load(id)
178
+ clear_relationships
176
179
  send(:attributes=, reloaded.attributes, false)
177
180
  end
178
181
  reloaded
@@ -4,6 +4,7 @@ module Neo4j
4
4
 
5
5
  initializer "neo4j.tx" do |app|
6
6
  app.config.middleware.use Neo4j::Rails::RackMiddleware
7
+ app.config.middleware.use Neo4j::IdentityMap::Middleware
7
8
  end
8
9
 
9
10
  # Add ActiveModel translations to the I18n load_path
@@ -158,6 +158,7 @@ module Neo4j
158
158
  _persist_end_node
159
159
 
160
160
  @_java_rel = Neo4j::Relationship.new(type, start_node, end_node)
161
+ Neo4j::IdentityMap.add(@_java_rel, self)
161
162
  init_on_create
162
163
  clear_changes
163
164
  end unless @end_node.nil?
@@ -193,6 +194,10 @@ module Neo4j
193
194
  end
194
195
 
195
196
  def reload_from_database
197
+ Neo4j::IdentityMap.remove_rel_by_id(id) if persisted?
198
+ Neo4j::IdentityMap.remove_node_by_id(@end_node.id) if @end_node && @end_node.persisted?
199
+ Neo4j::IdentityMap.remove_node_by_id(@start_node.id) if @start_node && @start_node.persisted?
200
+
196
201
  if reloaded = self.class.load(id)
197
202
  send(:attributes=, reloaded.attributes, false)
198
203
  end
@@ -132,7 +132,8 @@ module Neo4j
132
132
  include Validations # enable validations
133
133
  include Callbacks # enable callbacks
134
134
  include Finders # ActiveRecord style find
135
+ include Compositions
135
136
  end
136
137
 
137
138
  end
138
- end
139
+ end
@@ -4,23 +4,24 @@ module Neo4j
4
4
 
5
5
 
6
6
  def write_changed_relationships #:nodoc:
7
- @relationships.each_value do |storage|
7
+ @_relationships.each_value do |storage|
8
8
  storage.persist
9
9
  end
10
10
  end
11
11
 
12
12
  def clear_relationships #:nodoc:
13
- @relationships = {}
13
+ @_relationships && @_relationships.each_value{|storage| storage.remove_from_identity_map}
14
+ @_relationships = {}
14
15
  end
15
16
 
16
17
 
17
18
  def _create_or_get_storage(rel_type) #:nodoc:
18
19
  dsl = _decl_rels_for(rel_type.to_sym)
19
- @relationships[rel_type.to_sym] ||= Storage.new(self, rel_type, dsl)
20
+ @_relationships[rel_type.to_sym] ||= Storage.new(self, rel_type, dsl)
20
21
  end
21
22
 
22
23
  def _create_or_get_storage_for_decl_rels(decl_rels) #:nodoc:
23
- @relationships[decl_rels.rel_type.to_sym] ||= Storage.new(self, decl_rels.rel_type, decl_rels)
24
+ @_relationships[decl_rels.rel_type.to_sym] ||= Storage.new(self, decl_rels.rel_type, decl_rels)
24
25
  end
25
26
 
26
27
 
@@ -14,9 +14,6 @@ module Neo4j
14
14
  @target_class = (dsl && dsl.target_class) || Neo4j::Rails::Model
15
15
  @outgoing_rels = []
16
16
  @incoming_rels = []
17
- @persisted_related_nodes = {}
18
- @persisted_relationships = {}
19
- @persisted_node_to_relationships = {}
20
17
  end
21
18
 
22
19
  def to_s #:nodoc:
@@ -24,6 +21,11 @@ module Neo4j
24
21
  end
25
22
 
26
23
 
24
+ def remove_from_identity_map
25
+ @outgoing_rels.each {|r| Neo4j::IdentityMap.remove(r._java_rel)}
26
+ @incoming_rels.each {|r| Neo4j::IdentityMap.remove(r._java_rel)}
27
+ end
28
+
27
29
  def size(dir)
28
30
  counter = 0
29
31
  # count persisted relationship
@@ -60,18 +62,12 @@ module Neo4j
60
62
  def each_rel(dir, &block) #:nodoc:
61
63
  relationships(dir).each { |rel| block.call rel }
62
64
  if @node.persisted?
63
- cache_relationships(dir) if @persisted_relationships[dir].nil?
64
- @persisted_relationships[dir].each {|rel| block.call rel unless !rel.exist?}
65
+ @node._java_node.getRelationships(java_rel_type, dir_to_java(dir)).each do |rel|
66
+ block.call(rel.wrapper)
67
+ end
65
68
  end
66
69
  end
67
70
 
68
- def cache_relationships(dir)
69
- @persisted_relationships[dir] ||= []
70
- node._java_node.getRelationships(java_rel_type, dir_to_java(dir)).each do |rel|
71
- @persisted_relationships[dir] << rel.wrapper
72
- end
73
- end
74
-
75
71
  def each_node(dir, &block)
76
72
  relationships(dir).each do |rel|
77
73
  if rel.start_node == @node
@@ -80,31 +76,21 @@ module Neo4j
80
76
  block.call rel.start_node
81
77
  end
82
78
  end
83
- if @node.persisted?
84
- cache_persisted_nodes_and_relationships(dir) if @persisted_related_nodes[dir].nil?
85
- @persisted_related_nodes[dir].each {|node| block.call node unless relationship_deleted?(dir,node)}
86
- end
87
- end
88
79
 
89
- def cache_persisted_nodes_and_relationships(dir)
90
- @persisted_related_nodes[dir] ||= []
91
- @persisted_node_to_relationships[dir] ||= {}
92
- @node._java_node.getRelationships(java_rel_type, dir_to_java(dir)).each do |rel|
93
- end_node = rel.getOtherNode(@node._java_node).wrapper
94
- @persisted_related_nodes[dir] << end_node
95
- @persisted_node_to_relationships[dir][end_node]=rel
80
+ if @node.persisted?
81
+ @node._java_node.getRelationships(java_rel_type, dir_to_java(dir)).each do |rel|
82
+ end_node = rel.getOtherNode(@node._java_node).wrapper
83
+ block.call(end_node)
84
+ end
96
85
  end
97
86
  end
98
87
 
99
- def relationship_deleted?(dir,node)
100
- @persisted_node_to_relationships[dir][node].nil? || !@persisted_node_to_relationships[dir][node].exist?
101
- end
102
-
103
- def single_relationship(dir)
88
+ def single_relationship(dir, raw = false)
104
89
  rel = relationships(dir).first
90
+ # puts "single_relationship #{dir} for #{self}, got #{rel && rel._java_rel} @node.persisted?=#{@node.persisted?}, #{@node._java_node}"
105
91
  if rel.nil? && @node.persisted?
106
92
  java_rel = @node._java_node.getSingleRelationship(java_rel_type, dir_to_java(dir))
107
- java_rel && java_rel.wrapper
93
+ raw ? java_rel : java_rel && java_rel.wrapper
108
94
  else
109
95
  rel
110
96
  end
@@ -115,10 +101,8 @@ module Neo4j
115
101
  end
116
102
 
117
103
  def single_node(dir)
118
- rel = single_relationship(dir)
119
- return nil if rel.nil?
120
- other = rel.other_node(@node)
121
- other && other.wrapper
104
+ rel = single_relationship(dir, true)
105
+ rel && rel.other_node(@node)
122
106
  end
123
107
 
124
108
  def destroy_rels(dir, *nodes)
@@ -161,7 +145,7 @@ module Neo4j
161
145
  out_rels = @outgoing_rels.clone
162
146
  in_rels = @incoming_rels.clone
163
147
 
164
- [@outgoing_rels, @incoming_rels, @persisted_related_nodes, @persisted_node_to_relationships, @persisted_relationships].each{|c| c.clear}
148
+ [@outgoing_rels, @incoming_rels].each{|c| c.clear}
165
149
 
166
150
  out_rels.each do |rel|
167
151
  rel.end_node.rm_incoming_rel(@rel_type.to_sym, rel) if rel.end_node
@@ -3,19 +3,19 @@ module Neo4j
3
3
  # Handle all the created_at, updated_at, created_on, updated_on type stuff.
4
4
  module Timestamps
5
5
  extend ActiveSupport::Concern
6
-
6
+
7
7
  TIMESTAMP_PROPERTIES = [ :created_at, :created_on, :updated_at, :updated_on ]
8
-
8
+
9
9
  def write_changed_attributes
10
10
  update_timestamp
11
11
  super
12
12
  end
13
-
13
+
14
14
  def init_on_create(*args)
15
15
  create_timestamp
16
16
  super
17
17
  end
18
-
18
+
19
19
  # Set the timestamps for this model if timestamps is set to true in the config
20
20
  # and the model is set up with the correct property name, e.g.:
21
21
  #
@@ -23,9 +23,9 @@ module Neo4j
23
23
  # property :updated_at, :type => DateTime
24
24
  # end
25
25
  def update_timestamp
26
- write_date_or_timestamp(:updated_at) if Neo4j::Config[:timestamps] && respond_to?(:updated_at)
26
+ #definition provided whenever an :updated_at property is defined
27
27
  end
28
-
28
+
29
29
  # Set the timestamps for this model if timestamps is set to true in the config
30
30
  # and the model is set up with the correct property name, e.g.:
31
31
  #
@@ -33,9 +33,9 @@ module Neo4j
33
33
  # property :created_at, :type => DateTime
34
34
  # end
35
35
  def create_timestamp
36
- write_date_or_timestamp(:created_at) if Neo4j::Config[:timestamps] && respond_to?(:created_at)
36
+ #definition provided whenever an :created_at property is defined
37
37
  end
38
-
38
+
39
39
  # Write the timestamp as a Date, DateTime or Time depending on the property type
40
40
  def write_date_or_timestamp(attribute)
41
41
  value = case self.class._decl_props[attribute][:type].to_s
@@ -49,16 +49,25 @@ module Neo4j
49
49
 
50
50
  send("#{attribute}=", value)
51
51
  end
52
-
52
+
53
53
  module ClassMethods
54
54
  def property_setup(property, options)
55
55
  super
56
-
56
+ define_timestamp_method(:create_timestamp,:created_at) if property == :created_at
57
+ define_timestamp_method(:update_timestamp,:updated_at) if property == :updated_at
57
58
  # ensure there's always a type on the timestamp properties
58
59
  if Neo4j::Config[:timestamps] && TIMESTAMP_PROPERTIES.include?(property)
59
60
  _decl_props[property][:type] ||= Time
60
61
  end
61
62
  end
63
+
64
+ def define_timestamp_method(method_name, property)
65
+ class_eval <<-RUBY, __FILE__, __LINE__
66
+ def #{method_name}
67
+ write_date_or_timestamp(:#{property}) if Neo4j::Config[:timestamps]
68
+ end
69
+ RUBY
70
+ end
62
71
  end
63
72
  end
64
73
  end
@@ -1,37 +1,76 @@
1
1
  module Neo4j
2
- module Rails
3
- module Validations
4
- extend ActiveSupport::Concern
5
-
6
- class UniquenessValidator < ActiveModel::EachValidator
7
- def initialize(options)
8
- super(options.reverse_merge(:case_sensitive => true))
9
- end
10
-
11
- def setup(klass)
12
- @attributes.each do |attribute|
13
- if klass.index_type_for(attribute) != :exact
14
- raise "Can't validate property #{attribute.inspect} on class #{klass} since there is no :exact lucene index on that property or the index declaration #{attribute} comes after the validation declaration in #{klass} (try to move it before the validation rules)"
15
- end
16
- end
17
- end
18
-
19
- def validate_each(record, attribute, value)
20
- return if options[:allow_blank] && value.blank?
21
- record.class.all("#{attribute}: \"#{value}\"").each do |rec|
2
+ module Rails
3
+ module Validations
4
+ extend ActiveSupport::Concern
5
+
6
+ class UniquenessValidator < ActiveModel::EachValidator
7
+ def initialize(options)
8
+ super(options.reverse_merge(:case_sensitive => true))
9
+ @validator = options[:case_sensitive].nil? || options[:case_sensitive] ? ExactMatchValidator : FulltextMatchValidator
10
+ end
11
+
12
+ def setup(klass)
13
+ @attributes.each do |attribute|
14
+ if klass.index_type_for(attribute) != @validator.index_type
15
+ raise index_error_message(klass,attribute,@validator.index_type)
16
+ end
17
+ end
18
+ end
19
+
20
+ def index_error_message(klass,attribute,index_type)
21
+ "Can't validate property #{attribute.inspect} on class #{klass} since there is no :#{index_type} lucene index on that property or the index declaration #{attribute} comes after the validation declaration in #{klass} (try to move it before the validation rules)"
22
+ end
23
+
24
+ def validate_each(record, attribute, value)
25
+ return if options[:allow_blank] && value.blank?
26
+ @validator.query(record.class,attribute,value).each do |rec|
22
27
  if rec.id != record.id # it doesn't count if we find ourself!
23
- record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(:value => value))
28
+ if @validator.match(rec, attribute, value)
29
+ record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(:value => value))
30
+ end
24
31
  break
25
32
  end
26
- end
27
- end
28
- end
29
-
30
- module ClassMethods
31
- def validates_uniqueness_of(*attr_names)
32
- validates_with UniquenessValidator, _merge_attributes(attr_names)
33
- end
34
- end
35
- end
36
- end
33
+ end
34
+ end
35
+ end
36
+
37
+ class ExactMatchValidator
38
+ def self.index_type
39
+ :exact
40
+ end
41
+
42
+ def self.query(model,attribute,value)
43
+ model.all("#{attribute}: \"#{value}\"")
44
+ end
45
+
46
+ def self.match(rec,attribute,value)
47
+ rec[attribute] == value
48
+ end
49
+ end
50
+
51
+ class FulltextMatchValidator
52
+ def self.index_type
53
+ :fulltext
54
+ end
55
+
56
+ def self.query(model,attribute,value)
57
+ value.blank? ? model.all("*:* -#{attribute}:[* TO *]", :type => :fulltext) : model.all("#{attribute}: \"#{value}\"", :type => :fulltext)
58
+ end
59
+
60
+ def self.match(rec,attribute,value)
61
+ downcase(rec[attribute]) == downcase(value)
62
+ end
63
+
64
+ def self.downcase(value)
65
+ value.nil? ? value : value.strip.downcase
66
+ end
67
+ end
68
+
69
+ module ClassMethods
70
+ def validates_uniqueness_of(*attr_names)
71
+ validates_with UniquenessValidator, _merge_attributes(attr_names)
72
+ end
73
+ end
74
+ end
75
+ end
37
76
  end
@@ -24,6 +24,7 @@ module Neo4j
24
24
  type, from_node, to_node = args
25
25
  rel = Neo4j::Relationship.create(type, from_node, to_node)
26
26
  wrapped_rel = super()
27
+ Neo4j::IdentityMap.add(rel, wrapped_rel)
27
28
  wrapped_rel.init_on_load(rel)
28
29
  wrapped_rel.init_on_create(*args)
29
30
  wrapped_rel
@@ -0,0 +1,58 @@
1
+ module Neo4j
2
+ # == Represents a set of relationships.
3
+ # See Neo4j::EventHandler
4
+ class RelationshipSet
5
+ def initialize(size=0)
6
+ @relationship_type_set = java.util.HashSet.new(size)
7
+ @relationship_set = java.util.HashSet.new(size)
8
+ @relationship_map = java.util.HashMap.new(size)
9
+ end
10
+
11
+ # Adds a relationship to the set
12
+ def add(rel)
13
+ @relationship_type_set.add(RelationshipSetEntry.new(rel.getEndNode().getId(),rel.rel_type))
14
+ relationships(rel.getEndNode().getId()) << rel
15
+ @relationship_set.add(rel.getId)
16
+ end
17
+
18
+ # Returns a collection of relationships where the node with the specified end node id is the end node.
19
+ def relationships(end_node_id)
20
+ @relationship_map.get(end_node_id) || add_list(end_node_id)
21
+ end
22
+
23
+ # Returns true if the specified relationship is in the set
24
+ def contains_rel?(rel)
25
+ @relationship_set.contains(rel.getId)
26
+ end
27
+
28
+ # Returns true if a relationship with the specified end_node_id and relationship_type is present in the set.
29
+ def contains?(end_node_id,relationship_type)
30
+ @relationship_type_set.contains(RelationshipSetEntry.new(end_node_id,relationship_type))
31
+ end
32
+
33
+ protected
34
+ def add_list(node_id)
35
+ @relationship_map.put(node_id,[])
36
+ @relationship_map.get(node_id)
37
+ end
38
+ end
39
+
40
+ class RelationshipSetEntry
41
+ attr_accessor :nodeid, :relationship_type
42
+ def initialize(nodeid,relationship_type)
43
+ @nodeid,@relationship_type = nodeid.to_s, relationship_type.to_s
44
+ end
45
+
46
+ def ==(o)
47
+ eql?(o)
48
+ end
49
+
50
+ def eql?(other)
51
+ @nodeid == other.nodeid && @relationship_type == other.relationship_type
52
+ end
53
+
54
+ def hash
55
+ 3 * @nodeid.hash + @relationship_type.hash
56
+ end
57
+ end
58
+ end
@@ -17,7 +17,7 @@ module Neo4j
17
17
  Rule.trigger_rules(node, *changes) if Rule.trigger?(node)
18
18
  end
19
19
 
20
- def on_node_deleted(node, old_properties, data)
20
+ def on_node_deleted(node, old_properties, deleted_relationship_set, deleted_identity_map)
21
21
  # have we deleted a rule node ?
22
22
  del_rule_node = Rule.find_rule_node(node)
23
23
  del_rule_node && del_rule_node.clear_rule_node
@@ -34,13 +34,11 @@ module Neo4j
34
34
  rule_name = rule.rule_name.to_s
35
35
 
36
36
  # is the rule node deleted ?
37
- deleted_rule_node = data.deletedNodes.find { |n| n == rule_node.rule_node }
37
+ deleted_rule_node = deleted_identity_map.get(rule_node.rule_node.neo_id)
38
38
  next if deleted_rule_node
39
39
 
40
40
  rule.functions.each do |function|
41
- next unless data.deletedRelationships.find do |r|
42
- r.getEndNode().getId() == id && r.rel_type == rule_name
43
- end
41
+ next unless deleted_relationship_set.contains?(id,rule_name)
44
42
  previous_value = old_properties[function.function_id]
45
43
  function.delete(rule_name, rule_node.rule_node, previous_value) if previous_value
46
44
  end if rule.functions
@@ -115,16 +115,16 @@ module Neo4j
115
115
  rule_node.execute_rules(node, *changes)
116
116
 
117
117
  # recursively add relationships for all the parent classes with rules that also pass for this node
118
- recursive(node,classname,*changes)
118
+ recursive(node,rule_node.model_class,*changes)
119
119
  end
120
120
 
121
121
  private
122
122
 
123
- def recursive(node,classname,*changes)
124
- if (clazz = eval("#{classname}.superclass")) && clazz.include?(Neo4j::NodeMixin)
123
+ def recursive(node,model_class,*changes)
124
+ if (clazz = model_class.superclass) && clazz.include?(Neo4j::NodeMixin)
125
125
  rule_node = rule_node_for(clazz)
126
126
  rule_node && rule_node.execute_rules(node, *changes)
127
- recursive(node,clazz.name,*changes)
127
+ recursive(node,clazz,*changes)
128
128
  end
129
129
  end
130
130
  end
@@ -9,27 +9,28 @@ module Neo4j
9
9
  class RuleNode
10
10
  include ToJava
11
11
  attr_reader :rules
12
+ attr_reader :model_class
12
13
 
13
14
  def initialize(clazz)
14
- @type = eval("#{clazz}")
15
- @clazz = clazz
15
+ @model_class = eval("#{clazz}")
16
+ @classname = clazz
16
17
  @rules = []
17
18
  @rule_node_key = ("rule_" + clazz.to_s).to_sym
18
19
  @ref_node_key = ("rule_ref_for_" + clazz.to_s).to_sym
19
20
  end
20
21
 
21
22
  def to_s
22
- "RuleNode #{@clazz}, node #{rule_node} #rules: #{@rules.size}"
23
+ "RuleNode #{@classname}, node #{rule_node} #rules: #{@rules.size}"
23
24
  end
24
25
 
25
26
  # returns true if the rule node exist yet in the database
26
27
  def node_exist?
27
- !ref_node.rel?(@clazz)
28
+ !ref_node.rel?(@classname)
28
29
  end
29
30
 
30
31
  def ref_node
31
- if @type.respond_to? :ref_node_for_class
32
- @type.ref_node_for_class
32
+ if @model_class.respond_to? :ref_node_for_class
33
+ @model_class.ref_node_for_class
33
34
  else
34
35
  Neo4j.ref_node
35
36
  end
@@ -38,7 +39,7 @@ module Neo4j
38
39
  def create_node
39
40
  Neo4j::Transaction.run do
40
41
  node = Neo4j::Node.new
41
- ref_node.create_relationship_to(node, type_to_java(@clazz))
42
+ ref_node.create_relationship_to(node, type_to_java(@classname))
42
43
  node
43
44
  end
44
45
  end
@@ -50,14 +51,14 @@ module Neo4j
50
51
  end
51
52
 
52
53
  def delete_node
53
- if ref_node.rel?(@clazz)
54
- ref_node.outgoing(@clazz).each { |n| n.del }
54
+ if ref_node.rel?(@classname)
55
+ ref_node.outgoing(@classname).each { |n| n.del }
55
56
  end
56
57
  clear_rule_node
57
58
  end
58
59
 
59
60
  def find_node
60
- ref_node.rel?(@clazz.to_s) && ref_node._rel(:outgoing, @clazz.to_s)._end_node
61
+ ref_node.rel?(@classname.to_s) && ref_node._rel(:outgoing, @classname.to_s)._end_node
61
62
  end
62
63
 
63
64
  def rule_node
@@ -36,7 +36,10 @@ module Neo4j
36
36
  #
37
37
  # ==== Examples
38
38
  # # Find all my friends (nodes of depth 1 of type <tt>friends</tt>)
39
- # me.outgoing(:friends).each {|friend| puts friend[:name]}
39
+ # me.outgoing(:friends).each {|friend| puts friend.name}
40
+ #
41
+ # # A possible faster way, avoid loading wrapper Ruby classes, instead use raw java neo4j node objects
42
+ # me.outgoing(:friends).raw.each {|friend| puts friend[:name]}
40
43
  #
41
44
  # # Find all my friends and their friends (nodes of depth 1 of type <tt>friends</tt>)
42
45
  # # me.outgoing(:friends).depth(2).each {|friend| puts friend[:name]}
@@ -194,7 +194,9 @@ module Neo4j
194
194
  end
195
195
 
196
196
  def size
197
- to_a.size
197
+ s = 0
198
+ iterator.each { |_| s += 1 }
199
+ s
198
200
  end
199
201
 
200
202
  alias_method :length, :size
@@ -208,7 +210,7 @@ module Neo4j
208
210
  end
209
211
 
210
212
  def each
211
- @traversal_result == :paths ? iterator.each { |i| yield i } : iterator.each { |i| yield i.wrapper }
213
+ @raw ? iterator.each { |i| yield i } : iterator.each { |i| yield i.wrapper }
212
214
  end
213
215
 
214
216
  # Same as #each but does not wrap each node in a Ruby class, yields the Java Neo4j Node instance instead.
@@ -223,10 +225,18 @@ module Neo4j
223
225
  self
224
226
  end
225
227
 
226
- # Returns an enumerable of relationships instead of nodes
228
+ # If this is called then it will not wrap the nodes but instead return the raw Java Neo4j::Node objects when traversing
229
+ #
230
+ def raw
231
+ @raw = true
232
+ self
233
+ end
234
+
235
+ # Returns an enumerable of paths instead of nodes
227
236
  #
228
237
  def paths
229
238
  @traversal_result = :paths
239
+ @raw = true
230
240
  self
231
241
  end
232
242
 
@@ -1,3 +1,3 @@
1
1
  module Neo4j
2
- VERSION = "1.2.4"
2
+ VERSION = "1.2.5"
3
3
  end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: neo4j
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 1.2.4
5
+ version: 1.2.5
6
6
  platform: java
7
7
  authors:
8
8
  - Andreas Ronge
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-10-07 00:00:00 +02:00
13
+ date: 2011-10-21 00:00:00 +02:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -84,6 +84,7 @@ files:
84
84
  - lib/generators/neo4j/model/model_generator.rb
85
85
  - lib/generators/neo4j/model/templates/model.erb
86
86
  - lib/orm_adapter/adapters/neo4j.rb
87
+ - lib/neo4j/identity_map.rb
87
88
  - lib/neo4j/event_handler.rb
88
89
  - lib/neo4j/model.rb
89
90
  - lib/neo4j/config.rb
@@ -92,6 +93,7 @@ files:
92
93
  - lib/neo4j/node.rb
93
94
  - lib/neo4j/load.rb
94
95
  - lib/neo4j/database.rb
96
+ - lib/neo4j/relationship_set.rb
95
97
  - lib/neo4j/version.rb
96
98
  - lib/neo4j/equal.rb
97
99
  - lib/neo4j/relationship.rb