neo4j 3.0.0.alpha.7 → 3.0.0.alpha.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +9 -0
  3. data/lib/neo4j.rb +3 -0
  4. data/lib/neo4j/active_node.rb +1 -0
  5. data/lib/neo4j/active_node/has_n.rb +6 -6
  6. data/lib/neo4j/active_node/has_n/decl_rel.rb +22 -2
  7. data/lib/neo4j/active_node/has_n/nodes.rb +1 -2
  8. data/lib/neo4j/active_node/labels.rb +35 -74
  9. data/lib/neo4j/active_node/orm_adapter.rb +8 -13
  10. data/lib/neo4j/active_node/persistence.rb +9 -6
  11. data/lib/neo4j/active_node/property.rb +24 -4
  12. data/lib/neo4j/active_node/query.rb +54 -0
  13. data/lib/neo4j/active_node/query/query_proxy.rb +107 -0
  14. data/lib/neo4j/active_node/quick_query.rb +254 -0
  15. data/lib/neo4j/active_node/validations.rb +1 -1
  16. data/lib/neo4j/version.rb +1 -1
  17. data/neo4j.gemspec +1 -1
  18. metadata +7 -48
  19. data/lib/mydb2/active_tx_log +0 -1
  20. data/lib/mydb2/index/lucene-store.db +0 -0
  21. data/lib/mydb2/index/lucene.log.1 +0 -0
  22. data/lib/mydb2/index/lucene.log.active +0 -0
  23. data/lib/mydb2/index/lucene.log.v0 +0 -0
  24. data/lib/mydb2/lock +0 -0
  25. data/lib/mydb2/messages.log +0 -618
  26. data/lib/mydb2/neostore +0 -0
  27. data/lib/mydb2/neostore.id +0 -0
  28. data/lib/mydb2/neostore.labeltokenstore.db +0 -0
  29. data/lib/mydb2/neostore.labeltokenstore.db.id +0 -0
  30. data/lib/mydb2/neostore.labeltokenstore.db.names +0 -0
  31. data/lib/mydb2/neostore.labeltokenstore.db.names.id +0 -0
  32. data/lib/mydb2/neostore.nodestore.db +0 -0
  33. data/lib/mydb2/neostore.nodestore.db.id +0 -0
  34. data/lib/mydb2/neostore.nodestore.db.labels +0 -0
  35. data/lib/mydb2/neostore.nodestore.db.labels.id +0 -0
  36. data/lib/mydb2/neostore.propertystore.db +0 -0
  37. data/lib/mydb2/neostore.propertystore.db.arrays +0 -0
  38. data/lib/mydb2/neostore.propertystore.db.arrays.id +0 -0
  39. data/lib/mydb2/neostore.propertystore.db.id +0 -0
  40. data/lib/mydb2/neostore.propertystore.db.index +0 -0
  41. data/lib/mydb2/neostore.propertystore.db.index.id +0 -0
  42. data/lib/mydb2/neostore.propertystore.db.index.keys +0 -0
  43. data/lib/mydb2/neostore.propertystore.db.index.keys.id +0 -0
  44. data/lib/mydb2/neostore.propertystore.db.strings +0 -0
  45. data/lib/mydb2/neostore.propertystore.db.strings.id +0 -0
  46. data/lib/mydb2/neostore.relationshipstore.db +0 -0
  47. data/lib/mydb2/neostore.relationshipstore.db.id +0 -0
  48. data/lib/mydb2/neostore.relationshiptypestore.db +0 -0
  49. data/lib/mydb2/neostore.relationshiptypestore.db.id +0 -0
  50. data/lib/mydb2/neostore.relationshiptypestore.db.names +0 -0
  51. data/lib/mydb2/neostore.relationshiptypestore.db.names.id +0 -0
  52. data/lib/mydb2/neostore.schemastore.db +0 -0
  53. data/lib/mydb2/neostore.schemastore.db.id +0 -0
  54. data/lib/mydb2/nioneo_logical.log.1 +0 -0
  55. data/lib/mydb2/nioneo_logical.log.active +0 -0
  56. data/lib/mydb2/nioneo_logical.log.v0 +0 -0
  57. data/lib/mydb2/schema/label/lucene/segments.gen +0 -0
  58. data/lib/mydb2/schema/label/lucene/segments_1 +0 -0
  59. data/lib/mydb2/schema/label/lucene/write.lock +0 -0
  60. data/lib/mydb2/store_lock +0 -0
  61. data/lib/mydb2/tm_tx_log.1 +0 -0
  62. data/lib/neo4j.rb~ +0 -40
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1a71bb2f48417b359fdef8ba5db8e94d13cfcd34
4
- data.tar.gz: 03f4c0aa740b9f3b9a8b2f222cecc659a9cd50be
3
+ metadata.gz: d97243db16c4dce2d4e87eb9551116729065fe2a
4
+ data.tar.gz: 311559376e0de462bbde5d9dae2d348c041fd3f6
5
5
  SHA512:
6
- metadata.gz: 8e04c5ccc3a1c56420dfcfa589afbab3219bc995ba92068b2f09d40a1e4e387222869a87f80bd8678b18baea3d606ee7b315020049d7a6d381b459b1908cd1f2
7
- data.tar.gz: 6696edb9bc1aa5fcf1ba8c7f0ae7aea8fbd6e5cc881e62aa24ca0cf97dd199ef592cae2fac6ac410efa88a9436d9343121e1a92d93e7c13e5a94638bf24d1baf
6
+ metadata.gz: 197a923f6617ced1dab9a81adea38fb13fe55b5bd7429b4e84c461b53c2d98b31ff053ee2eb6228541792ba58309b3058d9dee4a0cece6ec68dff79eae921ac4
7
+ data.tar.gz: c1a71b84e759fd55ab9040c6d357d08cbdf22710e5e7eacbb7b4ddff01a3b07834a4e52c2dc378feb20a6d5c9a01891839216da5918d6ac4559b9e24cfcf87f2
data/CHANGELOG CHANGED
@@ -1,3 +1,12 @@
1
+ == 3.0.0.alpha.8
2
+ * Integration with new Query API from neo4j-core including:
3
+ * - .query_as and #query_as methods to get queries from models (#366)
4
+ * - .qq method for QuickQuery syntax ( https://github.com/andreasronge/neo4j/wiki/Neo4j-v3#quickquery-work-in-progress / #366)
5
+ * Before and after callbacks on associations (#373)
6
+ * .find / .all / .count changed to be more like ActiveRecord
7
+ * .first / .last methods (#378)
8
+ * .find_by / .find_by! (#375)
9
+
1
10
  == 3.0.0.alpha.7
2
11
  * Bug fix uniqueness-validator (#356 from JohnKellyFerguson)
3
12
  * Many improvements, like update_attributes and validation while impl orm_adapter, Brian Underwood
data/lib/neo4j.rb CHANGED
@@ -28,6 +28,9 @@ require 'neo4j/active_node/rels'
28
28
  require 'neo4j/active_node/has_n'
29
29
  require 'neo4j/active_node/has_n/decl_rel'
30
30
  require 'neo4j/active_node/has_n/nodes'
31
+ require 'neo4j/active_node/query/query_proxy'
32
+ require 'neo4j/active_node/query'
33
+ require 'neo4j/active_node/quick_query'
31
34
  require 'neo4j/active_node'
32
35
 
33
36
  require 'neo4j/active_node/orm_adapter'
@@ -38,6 +38,7 @@ module Neo4j
38
38
  include Neo4j::ActiveNode::Callbacks
39
39
  include Neo4j::ActiveNode::Rels
40
40
  include Neo4j::ActiveNode::HasN
41
+ include Neo4j::ActiveNode::Query
41
42
 
42
43
  def wrapper
43
44
  self
@@ -75,7 +75,7 @@ module Neo4j::ActiveNode
75
75
  #
76
76
  #
77
77
  # @return [Neo4j::ActiveNode::HasN::DeclRel] a DSL object where the has_n relationship can be further specified
78
- def has_n(rel_type)
78
+ def has_n(rel_type, *callbacks)
79
79
  clazz = self
80
80
  module_eval(%Q{def #{rel_type}=(values)
81
81
  #{rel_type}_rels.each {|rel| rel.del }
@@ -103,8 +103,7 @@ module Neo4j::ActiveNode
103
103
  def #{rel_type}
104
104
  _decl_rels[:#{rel_type}].rel_type
105
105
  end}, __FILE__, __LINE__)
106
-
107
- _decl_rels[rel_type.to_sym] = DeclRel.new(rel_type, false, clazz)
106
+ _decl_rels[rel_type.to_sym] = DeclRel.new(rel_type, false, clazz, *callbacks)
108
107
  end
109
108
 
110
109
 
@@ -126,13 +125,14 @@ module Neo4j::ActiveNode
126
125
  # file.folder_rel # => the relationship object between those nodes
127
126
  #
128
127
  # @return [Neo4j::ActiveNode::HasN::DeclRel] a DSL object where the has_one relationship can be futher specified
129
- def has_one(rel_type)
128
+ def has_one(rel_type, *callbacks)
130
129
  clazz = self
131
130
  module_eval(%Q{def #{rel_type}=(value)
131
+ return if !value
132
132
  dsl = _decl_rels_for(:#{rel_type})
133
133
  rel = dsl.single_relationship(self)
134
134
  rel && rel.del
135
- dsl.create_relationship_to(self, value) if value
135
+ dsl.create_relationship_to(self, value)
136
136
  end}, __FILE__, __LINE__)
137
137
 
138
138
  module_eval(%Q{def #{rel_type}
@@ -150,7 +150,7 @@ module Neo4j::ActiveNode
150
150
  _decl_rels[:#{rel_type}].rel_type
151
151
  end}, __FILE__, __LINE__)
152
152
 
153
- _decl_rels[rel_type.to_sym] = DeclRel.new(rel_type, true, clazz)
153
+ _decl_rels[rel_type.to_sym] = DeclRel.new(rel_type, true, clazz, *callbacks)
154
154
  end
155
155
 
156
156
 
@@ -33,12 +33,16 @@ module Neo4j
33
33
  class DeclRel
34
34
  attr_reader :source_class, :dir, :rel_type, :method_id
35
35
 
36
- def initialize(method_id, has_one, source_class)
36
+ def initialize(method_id, has_one, source_class, *callbacks)
37
37
  @method_id = method_id
38
38
  @has_one = has_one
39
39
  @dir = :outgoing
40
40
  @rel_type = method_id.to_sym
41
41
  @source_class = source_class
42
+ unless callbacks.empty?
43
+ @before_callback = callbacks.first[:before] || nil
44
+ @after_callback = callbacks.first[:after] || nil
45
+ end
42
46
  end
43
47
 
44
48
  def inherit_new
@@ -223,7 +227,23 @@ module Neo4j
223
227
  # @private
224
228
  def create_relationship_to(node, other, relationship_props={}) # :nodoc:
225
229
  from, to = incoming? ? [other, node] : [node, other]
226
- from.create_rel(@rel_type, to, relationship_props)
230
+ before_callback_result = do_before_callback(node, from, to)
231
+ return false if before_callback_result == false
232
+
233
+ result = from.create_rel(@rel_type, to, relationship_props)
234
+
235
+ after_callback_result = do_after_callback(node, from, to)
236
+ after_callback_result == false ? false : result
237
+ end
238
+
239
+ private
240
+
241
+ def do_before_callback(caller, from, to)
242
+ @before_callback ? caller.send(@before_callback, from, to) : true
243
+ end
244
+
245
+ def do_after_callback(caller, from, to)
246
+ @after_callback ? caller.send(@after_callback, from, to) : true
227
247
  end
228
248
 
229
249
  end
@@ -81,8 +81,7 @@ module Neo4j
81
81
  #
82
82
  # @return self
83
83
  def <<(other)
84
- @decl_rel.create_relationship_to(@node, other)
85
- self
84
+ !@decl_rel.create_relationship_to(@node, other) ? false : self
86
85
  end
87
86
  end
88
87
 
@@ -8,6 +8,7 @@ module Neo4j
8
8
 
9
9
  WRAPPED_CLASSES = []
10
10
  class InvalidQueryError < StandardError; end
11
+ class RecordNotFound < StandardError; end
11
12
 
12
13
  # @return the labels
13
14
  # @see Neo4j-core
@@ -59,37 +60,45 @@ module Neo4j
59
60
 
60
61
  module ClassMethods
61
62
 
62
- # Find all nodes/objects of this class, with given search criteria
63
- # @param [Hash, nil] args the search critera or nil if finding all
64
- # @param [Neo4j::Session] session defaults to the model's session
65
- def all(args = nil, session = self.neo4j_session)
66
- if args
67
- find_by_hash(args, session)
68
- else
69
- Neo4j::Label.find_all_nodes(mapped_label_name, session)
70
- end
63
+ # Find all nodes/objects of this class
64
+ def all
65
+ self.query_as(:n).pluck(:n)
66
+ end
67
+
68
+ def first
69
+ self.query_as(:n).limit(1).order('n.neo_id').pluck(:n).first
70
+ end
71
+
72
+ def last
73
+ count = self.count
74
+ final_count = count == 0 ? 0 : count - 1
75
+ self.query_as(:n).order('n.neo_id').skip(final_count).limit(1).pluck(:n).first
71
76
  end
72
77
 
73
78
  # @return [Fixnum] number of nodes of this class
74
- def count(session = self.neo4j_session)
75
- q = session.query(label: mapped_label_name, return: "count(n) AS count", map_return: :value)
76
- q.to_a[0]
77
- end
78
-
79
- # Same as #all but return only one object
80
- # If given a String or Fixnum it will return the object with that neo4j id.
81
- # @param [Hash,String,Fixnum] args search criteria
82
- def find(args, session = self.neo4j_session)
83
- case args
84
- when Hash
85
- find_by_hash(args, session).first
86
- when String, Fixnum
87
- Neo4j::Node.load(args.to_i)
88
- else
89
- raise "Unknown argument #{args.class} in find method"
90
- end
79
+ def count
80
+ self.query_as(:n).return("count(n) AS count").first.count
81
+ end
82
+
83
+ # Returns the object with the specified neo4j id.
84
+ # @param [String,Fixnum] neo_id of node to find
85
+ def find(id)
86
+ raise "Unknown argument #{id.class} in find method" if not [String, Fixnum].include?(id.class)
87
+
88
+ Neo4j::Node.load(id.to_i)
89
+ end
90
+
91
+ # Finds the first record matching the specified conditions. There is no implied ordering so if order matters, you should specify it yourself.
92
+ # @param [Hash] hash of arguments to find
93
+ def find_by(*args)
94
+ self.query_as(:n).where(n: eval(args.join)).limit(1).pluck(:n).first
91
95
  end
92
96
 
97
+ # Like find_by, except that if no record is found, raises a RecordNotFound error.
98
+ def find_by!(*args)
99
+ a = eval(args.join)
100
+ find_by(args) or raise RecordNotFound, "#{self.query_as(:n).where(n: a).limit(1).to_cypher} returned no results"
101
+ end
93
102
 
94
103
  # Destroy all nodes an connected relationships
95
104
  def destroy_all
@@ -129,54 +138,6 @@ module Neo4j
129
138
 
130
139
  protected
131
140
 
132
- def find_by_hash(query, session)
133
- validate_query!(query)
134
-
135
- extract_relationship_conditions!(query)
136
-
137
- session.query(query.merge(label: mapped_label_name))
138
- end
139
-
140
- # Raises an error if query is malformed
141
- def validate_query!(query)
142
- invalid_query_keys = query.keys.map(&:to_sym) - [:conditions, :order, :limit, :skip]
143
-
144
- raise InvalidQueryError, "Invalid query keys: #{invalid_query_keys.join(', ')}" if not invalid_query_keys.empty?
145
- end
146
-
147
- # Takes out :conditions query keys for associations and creates corresponding :conditions and :match keys
148
- # example:
149
- # class Person
150
- # property :name
151
- # has_n :friend
152
- # end
153
- #
154
- # :conditions => {name: 'Fred', friend: person}
155
- # should result in:
156
- # :conditions => {name => 'Fred', 'id(n1)' => person.id}, :match => 'n--n1'
157
- #
158
- def extract_relationship_conditions!(query)
159
- node_num = 1
160
- if query[:conditions]
161
- query[:conditions].dup.each do |key, value|
162
- if has_one_relationship?(key)
163
- neo_id = value.try(:neo_id) || value
164
- raise InvalidQueryError, "Invalid value for '#{key}' condition" if not neo_id.is_a?(Integer)
165
-
166
- query[:match] ||= []
167
- n_string = "n#{node_num}"
168
- dir = relationship_dir(key)
169
-
170
- match = dir == :outgoing ? "n-->(#{n_string})" : "n<--(#{n_string})"
171
- query[:match] << match
172
- query[:conditions]["id(#{n_string})"] = neo_id.to_i
173
- query[:conditions].delete(key)
174
- node_num += 1
175
- end
176
- end
177
- end
178
- end
179
-
180
141
  def _index(property)
181
142
  mapped_labels.each do |label|
182
143
  # make sure the property is not indexed twice
@@ -37,12 +37,9 @@ module Neo4j
37
37
  extract_id!(conditions)
38
38
  order = hasherize_order(order)
39
39
 
40
- if !order.empty?
41
- klass.find(conditions: conditions, order: order)
42
- else
43
- result = klass.find(conditions: conditions)
44
- result
45
- end
40
+ result = klass.where(conditions)
41
+ result = result.order(order) unless order.empty?
42
+ result.first
46
43
  end
47
44
 
48
45
  # Find all models matching conditions
@@ -51,12 +48,10 @@ module Neo4j
51
48
  extract_id!(conditions)
52
49
  order = hasherize_order(order)
53
50
 
54
- result = if !order.empty?
55
- klass.all(conditions: conditions, order: order, limit: limit, skip: offset)
56
- else
57
- klass.all(conditions: conditions)
58
- end
59
-
51
+ result = klass.where(conditions)
52
+ result = result.order(order) unless order.empty?
53
+ result = result.skip(offset) if offset
54
+ result = result.limit(limit) if limit
60
55
  result.to_a
61
56
  end
62
57
 
@@ -78,7 +73,7 @@ module Neo4j
78
73
 
79
74
  def extract_id!(conditions)
80
75
  if id = conditions.delete(:id)
81
- conditions['id(n)'] = id.to_i
76
+ conditions[:neo_id] = id.to_i
82
77
  end
83
78
  end
84
79
 
@@ -19,20 +19,15 @@ module Neo4j::ActiveNode
19
19
  # If any of them fail the action is cancelled and save returns false. If the flag is false validations are bypassed altogether. See ActiveRecord::Validations for more information.
20
20
  # There’s a series of callbacks associated with save. If any of the before_* callbacks return false the action is cancelled and save returns false.
21
21
  def save(*)
22
- # Update magic properties
23
22
  update_magic_properties
24
23
  create_or_update
25
24
  end
26
25
 
27
- def update_magic_properties
28
- self.updated_at = DateTime.now if respond_to?(:updated_at=) && changed?
29
- end
30
-
31
26
  # Creates a model with values matching those of the instance attributes and returns its id.
32
27
  # @private
33
28
  # @return true
34
29
  def create_model(*)
35
- self.created_at = DateTime.now if respond_to?(:created_at=)
30
+ set_timestamps
36
31
  properties = convert_properties_to :db, props
37
32
  node = _create_node(properties)
38
33
  init_on_load(node, node.props)
@@ -234,12 +229,20 @@ module Neo4j::ActiveNode
234
229
 
235
230
  private
236
231
 
232
+ def update_magic_properties
233
+ self.updated_at = DateTime.now if respond_to?(:updated_at=) && changed?
234
+ end
235
+
237
236
  def assign_attributes(attributes)
238
237
  attributes.each do |attribute, value|
239
238
  send("#{attribute}=", value)
240
239
  end
241
240
  end
242
241
 
242
+ def set_timestamps
243
+ self.created_at = DateTime.now if respond_to?(:created_at=)
244
+ self.updated_at = self.created_at if respond_to?(:updated_at=)
245
+ end
243
246
  end
244
247
 
245
248
  end
@@ -15,9 +15,7 @@ module Neo4j::ActiveNode
15
15
  def initialize(attributes={}, options={})
16
16
  relationship_props = self.class.extract_relationship_attributes!(attributes)
17
17
  writer_method_props = extract_writer_methods!(attributes)
18
-
19
18
  validate_attributes!(attributes)
20
-
21
19
  writer_method_props.each do |key, value|
22
20
  self.send("#{key}=", value)
23
21
  end
@@ -59,11 +57,15 @@ module Neo4j::ActiveNode
59
57
  module ClassMethods
60
58
 
61
59
  def property(name, options={})
62
- # Magic properties
63
- options[:type] = DateTime if name.to_sym == :created_at || name.to_sym == :updated_at
60
+ magic_properties(name, options)
61
+
62
+ # if (name.to_s == 'remember_created_at')
63
+ # binding.pry
64
+ # end
64
65
  attribute(name, options)
65
66
  end
66
67
 
68
+ #overrides ActiveAttr's attribute! method
67
69
  def attribute!(name, options={})
68
70
  super(name, options)
69
71
  define_method("#{name}=") do |value|
@@ -83,6 +85,24 @@ module Neo4j::ActiveNode
83
85
  end
84
86
  end
85
87
 
88
+ private
89
+
90
+ # Tweaks properties
91
+ def magic_properties(name, options)
92
+ set_stamp_type(name, options)
93
+ set_time_as_datetime(options)
94
+ end
95
+
96
+ def set_stamp_type(name, options)
97
+ options[:type] = DateTime if (name.to_sym == :created_at || name.to_sym == :updated_at)
98
+ end
99
+
100
+ # ActiveAttr does not handle "Time", Rails and Neo4j.rb 2.3 did
101
+ # Convert it to DateTime in the interest of consistency
102
+ def set_time_as_datetime(options)
103
+ options[:type] = DateTime if options[:type] == Time
104
+ end
105
+
86
106
  end
87
107
  end
88
108
 
@@ -0,0 +1,54 @@
1
+ module Neo4j
2
+ module ActiveNode
3
+
4
+ def qq(as = :n1)
5
+ QuickQuery.new(self, as, self.class)
6
+ end
7
+
8
+ # Helper methods to return Neo4j::Core::Query objects. A query object can be used to successively build a cypher query
9
+ #
10
+ # person.query_as(:n).match('n-[:friend]-o').return(o: :name) # Return the names of all the person's friends
11
+ #
12
+ module Query
13
+ extend ActiveSupport::Concern
14
+
15
+ # Returns a Query object with the current node matched the specified variable name
16
+ #
17
+ # @example Return the names of all of Mike's friends
18
+ # # Generates: MATCH (mike:Person), mike-[:friend]-friend WHERE ID(mike) = 123 RETURN friend.name
19
+ # mike.query_as(:mike).match('mike-[:friend]-friend').return(friend: :name)
20
+ #
21
+ # @param var [Symbol, String] The variable name to specify in the query
22
+ # @return [Neo4j::Core::Query]
23
+ def query_as(var)
24
+ self.class.query_as(var).where("ID(#{var}) = #{self.neo_id}")
25
+ end
26
+
27
+ module ClassMethods
28
+ # Returns a Query object with all nodes for the model matched as the specified variable name
29
+ #
30
+ # @example Return the registration number of all cars owned by a person over the age of 30
31
+ # # Generates: MATCH (person:Person), person-[:owned]-car WHERE person.age > 30 RETURN car.registration_number
32
+ # Person.query_as(:person).where('person.age > 30').match('person-[:owned]-car').return(car: :registration_number)
33
+ #
34
+ # @param var [Symbol, String] The variable name to specify in the query
35
+ # @return [Neo4j::Core::Query]
36
+ def query_as(var)
37
+ label = self.respond_to?(:mapped_label_name) ? self.mapped_label_name : self
38
+ neo4j_session.query.match(var => label)
39
+ end
40
+
41
+ Neo4j::ActiveNode::Query::QueryProxy::METHODS.each do |method|
42
+ module_eval(%Q{
43
+ def #{method}(*args)
44
+ Neo4j::ActiveNode::Query::QueryProxy.new(self).#{method}(*args)
45
+ end}, __FILE__, __LINE__)
46
+ end
47
+
48
+ def qq(as = :n1)
49
+ QuickQuery.new(self.name.constantize, as)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end