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
@@ -0,0 +1,107 @@
1
+ module Neo4j
2
+ module ActiveNode
3
+ module Query
4
+
5
+ class QueryProxy
6
+ include Enumerable
7
+
8
+ def initialize(model)
9
+ @model = model
10
+ @chain = []
11
+ end
12
+
13
+ def each
14
+ query_as(:n).pluck(:n).each do |obj|
15
+ yield obj
16
+ end
17
+ end
18
+
19
+ METHODS = %w[where order skip limit]
20
+
21
+ METHODS.each do |method|
22
+ module_eval(%Q{
23
+ def #{method}(*args)
24
+ build_deeper_query_proxy(:#{method}, args)
25
+ end}, __FILE__, __LINE__)
26
+ end
27
+
28
+ alias_method :offset, :skip
29
+ alias_method :order_by, :order
30
+
31
+ def query_as(var)
32
+ query = @model.query_as(var).return(var)
33
+
34
+ @chain.inject(query) do |query, (method, arg)|
35
+ if arg.respond_to?(:call)
36
+ query.send(method, arg.call(var))
37
+ else
38
+ query.send(method, arg)
39
+ end
40
+ end
41
+ end
42
+
43
+ def to_cypher
44
+ query_as(:n).to_cypher
45
+ end
46
+
47
+ protected
48
+
49
+ def add_links(links)
50
+ @chain += links
51
+ end
52
+
53
+ private
54
+
55
+ def build_deeper_query_proxy(method, args)
56
+ self.dup.tap do |new_query|
57
+ args.each do |arg|
58
+ new_query.add_links(links_for_arg(method, arg))
59
+ end
60
+ end
61
+ end
62
+
63
+ def links_for_arg(method, arg)
64
+ method_to_call = "links_for_#{method}_arg"
65
+
66
+ default = [[method, arg]]
67
+
68
+ self.send(method_to_call, arg) || default
69
+ rescue NoMethodError
70
+ default
71
+ end
72
+
73
+ def links_for_where_arg(arg)
74
+ node_num = 1
75
+ result = []
76
+ if arg.is_a?(Hash)
77
+ arg.map do |key, value|
78
+ if @model.has_one_relationship?(key)
79
+ neo_id = value.try(:neo_id) || value
80
+ raise ArgumentError, "Invalid value for '#{key}' condition" if not neo_id.is_a?(Integer)
81
+
82
+ n_string = "n#{node_num}"
83
+ dir = @model.relationship_dir(key)
84
+
85
+ arrow = dir == :outgoing ? '-->' : '<--'
86
+ result << [:match, ->(v) { "#{v}#{arrow}(#{n_string})" }]
87
+ result << [:where, ->(v) { {"ID(#{n_string})" => neo_id.to_i} }]
88
+ node_num += 1
89
+ else
90
+ result << [:where, ->(v) { {v => {key => value}}}]
91
+ end
92
+ end
93
+ end
94
+ result
95
+ end
96
+
97
+ def links_for_order_arg(arg)
98
+ [[:order, ->(v) { {v => arg} }]]
99
+ end
100
+
101
+
102
+ end
103
+
104
+ end
105
+ end
106
+ end
107
+
@@ -0,0 +1,254 @@
1
+ module Neo4j
2
+ module ActiveNode
3
+ # An abstraction layer to quickly build and return objects from the Neo4j Core Query class.
4
+ # It auto-increments node and relationship identifiers, uses relationships pre-defined in models to create match methods, and automatically maps
5
+ # results to collections.
6
+ class QuickQuery
7
+ attr_reader :quick_query
8
+
9
+ # Initialize sets the values of @node_on_deck and defines @rel_on_deck, among other things.
10
+ # The objects on deck are the objects implicitly modified when calling a method without specifying an identifier.
11
+ # They are auto-incremented at stages throughout the class.
12
+ def initialize(caller, as, caller_class = nil)
13
+ @caller_class = caller_class || caller
14
+ @node_on_deck = @return_obj = as.to_sym
15
+ @current_node_index = 2
16
+ @current_rel_index = 1
17
+ @rel_on_deck = nil
18
+ @return_set = false
19
+ @caller = caller
20
+ @quick_query = caller.query_as(as)
21
+ @identifiers = [@node_on_deck]
22
+ set_rel_methods(@caller_class)
23
+ return self
24
+ end
25
+
26
+ # sends the #to_cypher method to the core query class
27
+ def to_cypher
28
+ @quick_query.return(@return_obj).to_cypher
29
+ end
30
+
31
+
32
+ # Creates methods that send cleaned up arguments to the Core Query class
33
+ # Pass a symbol to specify the target identifier.
34
+ # Pass a hash to specify match parameters.
35
+ # Pass a valid cypher string to send directly to Core Query.
36
+ # If an identifier is not specified, it will apply them to the on-deck node.
37
+ # @example
38
+ # Student.qq.where(name: 'chris')
39
+ # Student.qq.lessons.where(:n2, name: 'history 101')
40
+ # Student.qq.lessons()
41
+ CUSTOM_METHODS = %w[where set set_props]
42
+
43
+ CUSTOM_METHODS.each do |method|
44
+ class_eval(%Q{
45
+ def #{method}(*args)
46
+ result = prepare(args)
47
+ final_query(__method__, result)
48
+ return self
49
+ end
50
+ }, __FILE__, __LINE__)
51
+ end
52
+
53
+
54
+ # Creates methods that send strings directly to Core Query class
55
+ LITERAL_METHODS = %w[limit skip match offset]
56
+
57
+ LITERAL_METHODS.each do |method|
58
+ class_eval(%Q{
59
+ def #{method}(s)
60
+ @quick_query = @quick_query.send(__method__, s)
61
+ return self
62
+ end
63
+ }, __FILE__, __LINE__)
64
+ end
65
+
66
+ # Sends #return to the core query class, does not map to an enumerable.
67
+ # Assumes the @return_obj if nothing is specified.
68
+ # if you want distinct, pass boolean true
69
+ # @example
70
+ # Student.qq.lessons.return(:n2)
71
+ # Student.qq.lessons.return(:n2, true)
72
+ def return(*args)
73
+ obj_sym = args.select{|el| el.is_a?(Symbol) }.first || @return_obj
74
+ distinct = args.select{|el| el.is_a?(TrueClass) }.first || false
75
+
76
+ r = final_return(obj_sym, distinct)
77
+ @quick_query = @quick_query.return(r)
78
+ return self
79
+ end
80
+
81
+ # Returns an enumerable of the query. If return has not been set, will set it to the on_deck node
82
+ def to_a(distinct = false)
83
+ @return_set ? result : self.return(distinct).to_a
84
+ end
85
+
86
+ # Same as to_a but with distinct set true
87
+ def to_a!
88
+ self.to_a(true)
89
+ end
90
+
91
+ # Set order for return.
92
+ # @param prop_sym [Symbol] a symbol matching the property on the return class use for order
93
+ # @param desc_bool [Boolean] boolean to dictate whether to sort descending. Defaults false, use true to descend
94
+ def order(prop_sym, desc_bool = false)
95
+ arg = "#{@return_obj}.#{prop_sym.to_s}"
96
+ end_arg = desc_bool ? arg + ' DESC' : arg
97
+ @quick_query = @quick_query.order(end_arg)
98
+ return self
99
+ end
100
+
101
+ private
102
+
103
+ def prepare(args)
104
+ target = args.select{|el| el.is_a?(Symbol) }
105
+ send_target = target.empty? ? @node_on_deck : target.first
106
+ result = process_args(args, send_target)
107
+ end
108
+
109
+ def result
110
+ response = @quick_query.response
111
+ if response.is_a?(Neo4j::Server::CypherResponse)
112
+ Neo4j::Session.current.search_result_to_enumerable_first_column(response)
113
+ else
114
+ Neo4j::Embedded::ResultWrapper.new(response, @quick_query.to_cypher).map{|x| x[0] }
115
+ end
116
+ end
117
+
118
+ def final_return(return_obj, distinct)
119
+ @return_set = true
120
+ distinct ? "distinct #{return_obj.to_s}" : return_obj.to_sym
121
+ end
122
+
123
+ def final_query(method, result)
124
+ @quick_query = @quick_query.send(method, result)
125
+ end
126
+
127
+ # Creates match methods based on the caller's relationships defined in the model.
128
+ # It works best when a relationship is defined explicitly with a direction and a receiving/incoming model.
129
+ # This fires once on initialize, again every time a matcher method is called to build methods for the next step.
130
+ # The dynamic classes accept the following:
131
+ # -a symbol to refer to the destination node
132
+ # -a hash with key :rel_as, value a symbol to act as relationship identifier (otherwise it uses r#{@current_rel_index})
133
+ # -a hash with key :rel_where containing other hashes of {parameter: value} to specify relationship conditions
134
+ # -hashes of {parameter: value} that specify conditions for the destination nodes
135
+ # @example
136
+ # Student.qq.lessons
137
+ # Student.qq.lessons(rel_as: :student_status)
138
+ # Student.qq.lessons(rel_as: :student_status, rel_where: { grade: 'b-' })
139
+ # Student.qq.lessons(rel_as: :student_status, rel_where: { grade: 'b-' }, :lessinzzz, class: 'history 101').teachers
140
+ def set_rel_methods(caller_class)
141
+ caller_class._decl_rels.each { |k,v|
142
+ if v.target_class.nil?
143
+ class_eval(%Q{
144
+ def #{k}(*args)
145
+ process_rel(args, "#{v.rel_type}")
146
+ return self
147
+ end}, __FILE__, __LINE__)
148
+ else
149
+ class_eval(%Q{
150
+ def #{k}(*args)
151
+ process_rel(args, "#{v.rel_type}", "#{v.target_class.name}")
152
+ return self
153
+ end}, __FILE__, __LINE__)
154
+ end
155
+ }
156
+ end
157
+
158
+ # Called when a matcher method is called
159
+ # args are the arguments sent along with the matcher method
160
+ # rel_type is the defined relationship type
161
+ # right_label is the label to use for the destination, if available. It's the "right label" cause it's on the right side... get it?
162
+ # A label can only be used if the model defines the destination class explicitly.
163
+ def process_rel(args, rel_type, right_label = nil)
164
+ from_node = @node_on_deck
165
+ hashes, strings = setup_rel_args(args)
166
+ set_on_deck(args)
167
+
168
+ hashes = process_rel_hashes(hashes) unless hashes.nil?
169
+ end_args = process_args([hashes] + strings, @node_on_deck) unless hashes.nil? && strings.nil?
170
+
171
+ destination = right_label.nil? ? @node_on_deck : "(#{@node_on_deck}:#{right_label})"
172
+ @quick_query = @quick_query.match("#{from_node}-[#{@rel_on_deck}:`#{rel_type}`]-#{destination}")
173
+ @quick_query = @quick_query.where(end_args) unless end_args.nil?
174
+ set_rel_methods(right_label.constantize) unless right_label.nil?
175
+
176
+ return self
177
+ end
178
+
179
+ # Prepares arguments passed with the relationship matcher. It finds hashes, which contain properties and values, and strings, which
180
+ # which literal cypher phrases.
181
+ def setup_rel_args(args)
182
+ hashes = args.select{|el| el.is_a?(Hash) }.first
183
+ strings = args.select{|el| el.is_a?(String) }
184
+ return hashes, strings
185
+ end
186
+
187
+ # Queues up the new node and relationship. This is only used during process_rel.
188
+ def set_on_deck(args)
189
+ @node_on_deck = @return_obj = node_as(args.select{|el| el.is_a?(Symbol) }.first)
190
+ @rel_on_deck = new_rel_id
191
+ @identifiers.push([@node_on_deck, @rel_on_deck])
192
+ end
193
+
194
+ # Prepares relationship-specific hashes found during the setup_rel_args method. Removes anything it finds from the hash and sends it back.
195
+ def process_rel_hashes(hashes)
196
+ @rel_on_deck = set_rel_as(hashes)
197
+ @identifiers.push @rel_on_deck
198
+
199
+ set_rel_where(hashes)
200
+
201
+ hashes.delete_if{|k,v| k == :rel_as || k == :rel_where }
202
+ end
203
+
204
+ # Creates a new node identifier
205
+ def new_node_id
206
+ n = "n#{@current_node_index}"
207
+ @current_node_index += 1
208
+ return n
209
+ end
210
+
211
+ # Creates a new relationship identifier
212
+ def new_rel_id
213
+ r = "r#{@current_rel_index}"
214
+ @current_rel_index += 1
215
+ return r
216
+ end
217
+
218
+ def node_as(node_as)
219
+ node_as.nil? ? new_node_id : node_as.to_sym
220
+ end
221
+
222
+ def set_rel_as(h)
223
+ h.has_key?(:rel_as) ? h[:rel_as] : new_rel_id
224
+ end
225
+
226
+ def set_rel_where(h)
227
+ if h.has_key?(:rel_where)
228
+ @quick_query = @quick_query.where(Hash[@rel_on_deck => h[:rel_where]])
229
+ end
230
+ end
231
+
232
+ # Utility method used to split up passed values and fix syntax to match Neo4j Core Query class
233
+ def process_args(args, where_target)
234
+ end_args = []
235
+ args.each do |arg|
236
+ if arg.is_a?(String)
237
+ end_args.push process_string(arg, where_target)
238
+ elsif arg.is_a?(Symbol)
239
+ @node_on_deck = arg
240
+ @return_obj = arg if @return_obj.nil?
241
+ elsif arg.is_a?(Hash)
242
+ end_args.push Hash[where_target => arg] unless arg.empty?
243
+ end
244
+ end
245
+ return end_args
246
+ end
247
+
248
+ # Attempts to determine whether the passed string already contains a node/rel identifier or if it needs one prepended.
249
+ def process_string(arg, where_target)
250
+ @identifiers.include?(arg.split('.').first.to_sym) ? arg : "#{where_target}.#{arg}"
251
+ end
252
+ end
253
+ end
254
+ end
@@ -57,7 +57,7 @@ module Neo4j
57
57
 
58
58
  # prevent that same object is returned
59
59
  # TODO: add negative condtion to not return current record
60
- found = record.class.all(conditions: conditions).to_a
60
+ found = record.class.where(conditions).to_a
61
61
  found.delete(record)
62
62
 
63
63
  if found.count > 0
data/lib/neo4j/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Neo4j
2
- VERSION = "3.0.0.alpha.7"
2
+ VERSION = "3.0.0.alpha.8"
3
3
  end
data/neo4j.gemspec CHANGED
@@ -33,7 +33,7 @@ It comes included with the Apache Lucene document database.
33
33
  s.add_dependency("activemodel", "~> 4")
34
34
  s.add_dependency("railties", "~> 4")
35
35
  s.add_dependency('active_attr', "~> 0.8")
36
- s.add_dependency("neo4j-core", "= 3.0.0.alpha.16")
36
+ s.add_dependency("neo4j-core", "= 3.0.0.alpha.17")
37
37
 
38
38
  if RUBY_PLATFORM =~ /java/
39
39
  s.add_dependency("neo4j-community", '~> 2.0')
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: neo4j
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0.alpha.7
4
+ version: 3.0.0.alpha.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andreas Ronge
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-06-07 00:00:00.000000000 Z
11
+ date: 2014-07-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: orm_adapter
@@ -72,14 +72,14 @@ dependencies:
72
72
  requirements:
73
73
  - - '='
74
74
  - !ruby/object:Gem::Version
75
- version: 3.0.0.alpha.16
75
+ version: 3.0.0.alpha.17
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - '='
81
81
  - !ruby/object:Gem::Version
82
- version: 3.0.0.alpha.16
82
+ version: 3.0.0.alpha.17
83
83
  description: "You can think of Neo4j as a high-performance graph engine with all the
84
84
  features of a mature and robust database.\nThe programmer works with an object-oriented,
85
85
  flexible network structure rather than with strict and static tables \nyet enjoys
@@ -99,51 +99,7 @@ files:
99
99
  - bin/neo4j-jars
100
100
  - config/locales/en.yml
101
101
  - config/neo4j/config.yml
102
- - lib/mydb2/active_tx_log
103
- - lib/mydb2/index/lucene-store.db
104
- - lib/mydb2/index/lucene.log.1
105
- - lib/mydb2/index/lucene.log.active
106
- - lib/mydb2/index/lucene.log.v0
107
- - lib/mydb2/lock
108
- - lib/mydb2/messages.log
109
- - lib/mydb2/neostore
110
- - lib/mydb2/neostore.id
111
- - lib/mydb2/neostore.labeltokenstore.db
112
- - lib/mydb2/neostore.labeltokenstore.db.id
113
- - lib/mydb2/neostore.labeltokenstore.db.names
114
- - lib/mydb2/neostore.labeltokenstore.db.names.id
115
- - lib/mydb2/neostore.nodestore.db
116
- - lib/mydb2/neostore.nodestore.db.id
117
- - lib/mydb2/neostore.nodestore.db.labels
118
- - lib/mydb2/neostore.nodestore.db.labels.id
119
- - lib/mydb2/neostore.propertystore.db
120
- - lib/mydb2/neostore.propertystore.db.arrays
121
- - lib/mydb2/neostore.propertystore.db.arrays.id
122
- - lib/mydb2/neostore.propertystore.db.id
123
- - lib/mydb2/neostore.propertystore.db.index
124
- - lib/mydb2/neostore.propertystore.db.index.id
125
- - lib/mydb2/neostore.propertystore.db.index.keys
126
- - lib/mydb2/neostore.propertystore.db.index.keys.id
127
- - lib/mydb2/neostore.propertystore.db.strings
128
- - lib/mydb2/neostore.propertystore.db.strings.id
129
- - lib/mydb2/neostore.relationshipstore.db
130
- - lib/mydb2/neostore.relationshipstore.db.id
131
- - lib/mydb2/neostore.relationshiptypestore.db
132
- - lib/mydb2/neostore.relationshiptypestore.db.id
133
- - lib/mydb2/neostore.relationshiptypestore.db.names
134
- - lib/mydb2/neostore.relationshiptypestore.db.names.id
135
- - lib/mydb2/neostore.schemastore.db
136
- - lib/mydb2/neostore.schemastore.db.id
137
- - lib/mydb2/nioneo_logical.log.1
138
- - lib/mydb2/nioneo_logical.log.active
139
- - lib/mydb2/nioneo_logical.log.v0
140
- - lib/mydb2/schema/label/lucene/segments.gen
141
- - lib/mydb2/schema/label/lucene/segments_1
142
- - lib/mydb2/schema/label/lucene/write.lock
143
- - lib/mydb2/store_lock
144
- - lib/mydb2/tm_tx_log.1
145
102
  - lib/neo4j.rb
146
- - lib/neo4j.rb~
147
103
  - lib/neo4j/active_node.rb
148
104
  - lib/neo4j/active_node/callbacks.rb
149
105
  - lib/neo4j/active_node/has_n.rb
@@ -155,6 +111,9 @@ files:
155
111
  - lib/neo4j/active_node/orm_adapter.rb
156
112
  - lib/neo4j/active_node/persistence.rb
157
113
  - lib/neo4j/active_node/property.rb
114
+ - lib/neo4j/active_node/query.rb
115
+ - lib/neo4j/active_node/query/query_proxy.rb
116
+ - lib/neo4j/active_node/quick_query.rb
158
117
  - lib/neo4j/active_node/rels.rb
159
118
  - lib/neo4j/active_node/validations.rb
160
119
  - lib/neo4j/railtie.rb