neo4j 1.2.4-java → 1.2.5-java

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