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

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