class Neo4j::ActiveNode::Query::QueryProxy

rubocop:disable Metrics/ClassLength

Constants

METHODS

Attributes

association[R]

The most recent node to start a QueryProxy chain. Will be nil when using QueryProxy chains on class methods.

context[RW]
model[R]

The most recent node to start a QueryProxy chain. Will be nil when using QueryProxy chains on class methods.

node_var[R]

The current node identifier on deck, so to speak. It is the object that will be returned by calling `each` and the last node link in the QueryProxy chain.

query_proxy[R]
rel_var[R]

The relationship identifier most recently used by the QueryProxy chain.

source_object[R]

The most recent node to start a QueryProxy chain. Will be nil when using QueryProxy chains on class methods.

start_object[R]
starting_query[R]

The most recent node to start a QueryProxy chain. Will be nil when using QueryProxy chains on class methods.

Public Class Methods

new(model, association = nil, options = {}) click to toggle source

QueryProxy is ActiveNode's Cypher DSL. While the name might imply that it creates queries in a general sense, it is actually referring to Neo4j::Core::Query, which is a pure Ruby Cypher DSL provided by the neo4j-core gem. QueryProxy provides ActiveRecord-like methods for common patterns. When it's not handling CRUD for relationships and queries, it provides ActiveNode's association chaining (`student.lessons.teachers.where(age: 30).hobbies`) and enjoys long walks on the beach.

It should not ever be necessary to instantiate a new QueryProxy object directly, it always happens as a result of calling a method that makes use of it.

@param [Constant] model The class which included ActiveNode (typically a model, hence the name) from which the query originated. @param [Neo4j::ActiveNode::HasN::Association] association The ActiveNode association (an object created by a has_one or has_many) that created this object. @param [Hash] options Additional options pertaining to the QueryProxy object. These may include: @option options [String, Symbol] :node_var A string or symbol to be used by Cypher within its query string as an identifier @option options [String, Symbol] :rel_var Same as above but pertaining to a relationship identifier @option options [Range, Integer, Symbol, Hash] :rel_length A Range, a Integer, a Hash or a Symbol to indicate the variable-length/fixed-length

qualifier of the relationship. See http://neo4jrb.readthedocs.org/en/latest/Querying.html#variable-length-relationships.

@option options [Neo4j::Session] :session The session to be used for this query @option options [Neo4j::ActiveNode] :source_object The node instance at the start of the QueryProxy chain @option options [QueryProxy] :query_proxy An existing QueryProxy chain upon which this new object should be built

QueryProxy objects are evaluated lazily.

   # File lib/neo4j/active_node/query/query_proxy.rb
41 def initialize(model, association = nil, options = {})
42   @model = model
43   @association = association
44   @context = options.delete(:context)
45   @options = options
46   @associations_spec = []
47 
48   instance_vars_from_options!(options)
49 
50   @match_type = @optional ? :optional_match : :match
51 
52   @rel_var = options[:rel] || _rel_chain_var
53 
54   @chain = []
55   @params = @query_proxy ? @query_proxy.instance_variable_get('@params') : {}
56 end

Public Instance Methods

<<(other_node) click to toggle source

To add a relationship for the node for the association on this QueryProxy

    # File lib/neo4j/active_node/query/query_proxy.rb
172 def <<(other_node)
173   _create_relation_or_defer(other_node)
174   self
175 end
[](index) click to toggle source
    # File lib/neo4j/active_node/query/query_proxy.rb
198 def [](index)
199   # TODO: Maybe for this and other methods, use array if already loaded, otherwise
200   # use OFFSET and LIMIT 1?
201   self.to_a[index]
202 end
_create_relationship(other_node_or_nodes, properties) click to toggle source
    # File lib/neo4j/active_node/query/query_proxy.rb
235 def _create_relationship(other_node_or_nodes, properties)
236   association._create_relationship(@start_object, other_node_or_nodes, properties)
237 end
_model_label_string(with_labels = true) click to toggle source

param [TrueClass, FalseClass] with_labels This param is used by certain QueryProxy methods that already have the neo_id and therefore do not need labels. The @association_labels instance var is set during init and used during association chaining to keep labels out of Cypher queries.

    # File lib/neo4j/active_node/query/query_proxy.rb
123 def _model_label_string(with_labels = true)
124   return if !@model || (!with_labels || @association_labels == false)
125   @model.mapped_label_names.map { |label_name| ":`#{label_name}`" }.join
126 end
_nodeify!(*args) click to toggle source
    # File lib/neo4j/active_node/query/query_proxy.rb
223 def _nodeify!(*args)
224   other_nodes = [args].flatten!.map! do |arg|
225     (arg.is_a?(Integer) || arg.is_a?(String)) ? @model.find_by(id: arg) : arg
226   end.compact
227 
228   if @model && other_nodes.any? { |other_node| !other_node.class.mapped_label_names.include?(@model.mapped_label_name) }
229     fail ArgumentError, "Node must be of the association's class when model is specified"
230   end
231 
232   other_nodes
233 end
base_query(var, with_labels = true) click to toggle source
    # File lib/neo4j/active_node/query/query_proxy.rb
110 def base_query(var, with_labels = true)
111   if @association
112     chain_var = _association_chain_var
113     (_association_query_start(chain_var) & _query).break.send(@match_type,
114                                                               "(#{chain_var})#{_association_arrow}(#{var}#{_model_label_string})")
115   else
116     starting_query ? starting_query : _query_model_as(var, with_labels)
117   end
118 end
branch(&block) click to toggle source

Executes the relation chain specified in the block, while keeping the current scope

@example Load all people that have friends

Person.all.branch { friends }.to_a # => Returns a list of `Person`

@example Load all people that has old friends

Person.all.branch { friends.where('age > 70') }.to_a # => Returns a list of `Person`

@yield the block that will be evaluated starting from the current scope

@return [QueryProxy] A new QueryProxy

    # File lib/neo4j/active_node/query/query_proxy.rb
188 def branch(&block)
189   fail LocalJumpError, 'no block given' if block.nil?
190   # `as(identity)` is here to make sure we get the right variable
191   # There might be a deeper problem of the variable changing when we
192   # traverse an association
193   as(identity).instance_eval(&block).query.proxy_as(self.model, identity).tap do |new_query_proxy|
194     propagate_context(new_query_proxy)
195   end
196 end
create(other_nodes, properties = {}) click to toggle source
    # File lib/neo4j/active_node/query/query_proxy.rb
204 def create(other_nodes, properties = {})
205   fail 'Can only create relationships on associations' if !@association
206   other_nodes = _nodeify!(*other_nodes)
207 
208   Neo4j::ActiveBase.run_transaction do
209     other_nodes.each do |other_node|
210       if other_node.neo_id
211         other_node.try(:validate_reverse_has_one_core_rel, association, @start_object)
212       else
213         other_node.save
214       end
215 
216       @start_object.association_proxy_cache.clear
217 
218       _create_relationship(other_node, properties)
219     end
220   end
221 end
identity() click to toggle source
   # File lib/neo4j/active_node/query/query_proxy.rb
68 def identity
69   @node_var || _result_string(_chain_level + 1)
70 end
Also aliased as: node_identity
inspect() click to toggle source
   # File lib/neo4j/active_node/query/query_proxy.rb
58 def inspect
59   formatted_nodes = Neo4j::ActiveNode::NodeListFormatter.new(to_a)
60   "#<QueryProxy #{@context} #{formatted_nodes.inspect}>"
61 end
method_missing(method_name, *args, &block) click to toggle source

QueryProxy objects act as a representation of a model at the class level so we pass through calls This allows us to define class functions for reusable query chaining or for end-of-query aggregation/summarizing

Calls superclass method
    # File lib/neo4j/active_node/query/query_proxy.rb
247 def method_missing(method_name, *args, &block)
248   if @model && @model.respond_to?(method_name)
249     scoping { @model.public_send(method_name, *args, &block) }
250   else
251     super
252   end
253 end
node_identity()
Alias for: identity
optional?() click to toggle source
    # File lib/neo4j/active_node/query/query_proxy.rb
259 def optional?
260   @optional == true
261 end
params(params) click to toggle source
   # File lib/neo4j/active_node/query/query_proxy.rb
81 def params(params)
82   new_link.tap { |new_query| new_query._add_params(params) }
83 end
query() click to toggle source

Like calling query_as, but for when you don't care about the variable name

   # File lib/neo4j/active_node/query/query_proxy.rb
86 def query
87   query_as(identity)
88 end
query_as(var, with_labels = true) click to toggle source

Build a Neo4j::Core::Query object for the QueryProxy. This is necessary when you want to take an existing QueryProxy chain and work with it from the more powerful (but less friendly) Neo4j::Core::Query. @param [String,Symbol] var The identifier to use for node at this link of the QueryProxy chain.

.. code-block

ruby

student.lessons.query_as(:l).with('your cypher here...')
    # File lib/neo4j/active_node/query/query_proxy.rb
 97 def query_as(var, with_labels = true)
 98   query_from_chain(chain, base_query(var, with_labels).params(@params), var)
 99     .tap { |query| query.proxy_chain_level = _chain_level }
100 end
query_from_chain(chain, base_query, var) click to toggle source
    # File lib/neo4j/active_node/query/query_proxy.rb
102 def query_from_chain(chain, base_query, var)
103   chain.inject(base_query) do |query, link|
104     args = link.args(var, rel_var)
105 
106     args.is_a?(Array) ? query.send(link.clause, *args) : query.send(link.clause, args)
107   end
108 end
read_attribute_for_serialization(*args) click to toggle source
    # File lib/neo4j/active_node/query/query_proxy.rb
239 def read_attribute_for_serialization(*args)
240   to_a.map { |o| o.read_attribute_for_serialization(*args) }
241 end
rel_identity() click to toggle source
   # File lib/neo4j/active_node/query/query_proxy.rb
75 def rel_identity
76   ActiveSupport::Deprecation.warn 'rel_identity is deprecated and may be removed from future releases, use rel_var instead.', caller
77 
78   @rel_var
79 end
respond_to_missing?(method_name, include_all = false) click to toggle source
Calls superclass method
    # File lib/neo4j/active_node/query/query_proxy.rb
255 def respond_to_missing?(method_name, include_all = false)
256   (@model && @model.respond_to?(method_name, include_all)) || super
257 end
scoping() { || ... } click to toggle source

Scope all queries to the current scope.

.. code-block

ruby

Comment.where(post_id: 1).scoping do
  Comment.first
end

TODO: unscoped Please check unscoped if you want to remove all previous scopes (including the default_scope) during the execution of a block.

    # File lib/neo4j/active_node/query/query_proxy.rb
139 def scoping
140   previous = @model.current_scope
141   @model.current_scope = self
142   yield
143 ensure
144   @model.current_scope = previous
145 end
to_cypher_with_params(columns = [self.identity]) click to toggle source

Returns a string of the cypher query with return objects and params @param [Array] columns array containing symbols of identifiers used in the query @return [String]

    # File lib/neo4j/active_node/query/query_proxy.rb
166 def to_cypher_with_params(columns = [self.identity])
167   final_query = query.return_query(columns)
168   "#{final_query.to_cypher} | params: #{final_query.send(:merge_params)}"
169 end
unpersisted_start_object?() click to toggle source
    # File lib/neo4j/active_node/query/query_proxy.rb
272 def unpersisted_start_object?
273   @start_object && @start_object.new_record?
274 end

Protected Instance Methods

_add_params(params) click to toggle source

Methods are underscored to prevent conflict with user class methods

    # File lib/neo4j/active_node/query/query_proxy.rb
289 def _add_params(params)
290   @params = @params.merge(params)
291 end
_association_arrow(properties = {}, create = false) click to toggle source
    # File lib/neo4j/active_node/query/query_proxy.rb
326 def _association_arrow(properties = {}, create = false)
327   @association && @association.arrow_cypher(@rel_var, properties, create, false, @rel_length)
328 end
_association_chain_var() click to toggle source
    # File lib/neo4j/active_node/query/query_proxy.rb
334 def _association_chain_var
335   fail 'Crazy error' if !(start_object || @query_proxy)
336 
337   if start_object
338     :"#{start_object.class.name.gsub('::', '_').downcase}#{start_object.neo_id}"
339   else
340     @query_proxy.node_var || :"node#{_chain_level}"
341   end
342 end
_association_query_start(var) click to toggle source
    # File lib/neo4j/active_node/query/query_proxy.rb
344 def _association_query_start(var)
345   # TODO: Better error
346   fail 'Crazy error' if !(object = (start_object || @query_proxy))
347 
348   object.query_as(var)
349 end
_chain_level() click to toggle source
    # File lib/neo4j/active_node/query/query_proxy.rb
330 def _chain_level
331   (@query_proxy ? @query_proxy._chain_level : (@chain_level || 0)) + 1
332 end
_create_relation_or_defer(other_node) click to toggle source
    # File lib/neo4j/active_node/query/query_proxy.rb
278 def _create_relation_or_defer(other_node)
279   if @start_object._persisted_obj
280     create(other_node, {})
281   elsif @association
282     @start_object.defer_create(@association.name, other_node)
283   else
284     fail 'Another crazy error!'
285   end
286 end
_match_arg(var, with_labels) click to toggle source

@param [String, Symbol] var The Cypher identifier to use within the match string @param [Boolean] with_labels Send “true” to include model labels where possible.

    # File lib/neo4j/active_node/query/query_proxy.rb
303 def _match_arg(var, with_labels)
304   if @model && with_labels != false
305     labels = @model.respond_to?(:mapped_label_names) ? _model_label_string : @model
306     {var.to_sym => labels}
307   else
308     var.to_sym
309   end
310 end
_query() click to toggle source
    # File lib/neo4j/active_node/query/query_proxy.rb
312 def _query
313   Neo4j::ActiveBase.new_query(context: @context)
314 end
_query_model_as(var, with_labels = true) click to toggle source
    # File lib/neo4j/active_node/query/query_proxy.rb
297 def _query_model_as(var, with_labels = true)
298   _query.break.send(@match_type, _match_arg(var, with_labels))
299 end
_rel_chain_var() click to toggle source
    # File lib/neo4j/active_node/query/query_proxy.rb
351 def _rel_chain_var
352   :"rel#{_chain_level - 1}"
353 end
_result_string(index = nil) click to toggle source
    # File lib/neo4j/active_node/query/query_proxy.rb
316 def _result_string(index = nil)
317   "result_#{(association || model).try(:name)}#{index}".downcase.tr(':', '').to_sym
318 end
_session() click to toggle source
    # File lib/neo4j/active_node/query/query_proxy.rb
320 def _session
321   (@session || (@model && @model.neo4j_session)).tap do |session|
322     fail 'No session found!' if session.nil?
323   end
324 end

Private Instance Methods

build_deeper_query_proxy(method, args) click to toggle source
    # File lib/neo4j/active_node/query/query_proxy.rb
366 def build_deeper_query_proxy(method, args)
367   new_link.tap do |new_query_proxy|
368     Link.for_args(@model, method, args, association).each { |link| new_query_proxy._add_links(link) }
369   end
370 end
instance_vars_from_options!(options) click to toggle source
    # File lib/neo4j/active_node/query/query_proxy.rb
359 def instance_vars_from_options!(options)
360   @node_var, @session, @source_object, @starting_query, @optional,
361       @start_object, @query_proxy, @chain_level, @association_labels,
362       @rel_length = options.values_at(:node, :session, :source_object, :starting_query, :optional,
363                                       :start_object, :query_proxy, :chain_level, :association_labels, :rel_length)
364 end