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
@@ -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