class Neo4j::ActiveNode::Query::QueryProxy
Constants
- METHODS
Attributes
The most recent node to start a QueryProxy
chain. Will be nil when using QueryProxy
chains on class methods.
The most recent node to start a QueryProxy
chain. Will be nil when using QueryProxy
chains on class methods.
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.
The relationship identifier most recently used by the QueryProxy
chain.
The most recent node to start a QueryProxy
chain. Will be nil when using QueryProxy
chains on class methods.
The most recent node to start a QueryProxy
chain. Will be nil when using QueryProxy
chains on class methods.
Public Class Methods
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, Fixnum, Symbol, Hash] :rel_length A Range, a Fixnum, 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 39 def initialize(model, association = nil, options = {}) 40 @model = model 41 @association = association 42 @context = options.delete(:context) 43 @options = options 44 @associations_spec = [] 45 46 instance_vars_from_options!(options) 47 48 @match_type = @optional ? :optional_match : :match 49 50 @rel_var = options[:rel] || _rel_chain_var 51 52 @chain = [] 53 @params = @query_proxy ? @query_proxy.instance_variable_get('@params') : {} 54 end
Public Instance Methods
To add a relationship for the node for the association on this QueryProxy
# File lib/neo4j/active_node/query/query_proxy.rb 166 def <<(other_node) 167 if @start_object._persisted_obj 168 create(other_node, {}) 169 elsif @association 170 @start_object.defer_create(@association.name, other_node) 171 else 172 fail 'Another crazy error!' 173 end 174 self 175 end
# File lib/neo4j/active_node/query/query_proxy.rb 196 def [](index) 197 # TODO: Maybe for this and other methods, use array if already loaded, otherwise 198 # use OFFSET and LIMIT 1? 199 self.to_a[index] 200 end
# File lib/neo4j/active_node/query/query_proxy.rb 233 def _create_relationship(other_node_or_nodes, properties) 234 association._create_relationship(@start_object, other_node_or_nodes, properties) 235 end
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 117 def _model_label_string(with_labels = true) 118 return if !@model || (!with_labels || @association_labels == false) 119 @model.mapped_label_names.map { |label_name| ":`#{label_name}`" }.join 120 end
# File lib/neo4j/active_node/query/query_proxy.rb 221 def _nodeify!(*args) 222 other_nodes = [args].flatten!.map! do |arg| 223 (arg.is_a?(Integer) || arg.is_a?(String)) ? @model.find_by(id: arg) : arg 224 end.compact 225 226 if @model && other_nodes.any? { |other_node| !other_node.class.mapped_label_names.include?(@model.mapped_label_name) } 227 fail ArgumentError, "Node must be of the association's class when model is specified" 228 end 229 230 other_nodes 231 end
# File lib/neo4j/active_node/query/query_proxy.rb 104 def base_query(var, with_labels = true) 105 if @association 106 chain_var = _association_chain_var 107 (_association_query_start(chain_var) & _query).break.send(@match_type, 108 "(#{chain_var})#{_association_arrow}(#{var}#{_model_label_string})") 109 else 110 starting_query ? starting_query : _query_model_as(var, with_labels) 111 end 112 end
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 if block 190 instance_eval(&block).query.proxy_as(self.model, identity) 191 else 192 fail LocalJumpError, 'no block given' 193 end 194 end
# File lib/neo4j/active_node/query/query_proxy.rb 202 def create(other_nodes, properties = {}) 203 fail 'Can only create relationships on associations' if !@association 204 other_nodes = _nodeify!(*other_nodes) 205 206 Neo4j::Transaction.run do 207 other_nodes.each do |other_node| 208 other_node.save unless other_node.neo_id 209 210 return false if @association.perform_callback(@start_object, other_node, :before) == false 211 212 @start_object.association_proxy_cache.clear 213 214 _create_relationship(other_node, properties) 215 216 @association.perform_callback(@start_object, other_node, :after) 217 end 218 end 219 end
# File lib/neo4j/active_node/query/query_proxy.rb 65 def identity 66 @node_var || _result_string 67 end
# File lib/neo4j/active_node/query/query_proxy.rb 56 def inspect 57 "#<QueryProxy #{@context} CYPHER: #{self.to_cypher.inspect}>" 58 end
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
# File lib/neo4j/active_node/query/query_proxy.rb 245 def method_missing(method_name, *args, &block) 246 if @model && @model.respond_to?(method_name) 247 scoping { @model.public_send(method_name, *args, &block) } 248 else 249 super 250 end 251 end
# File lib/neo4j/active_node/query/query_proxy.rb 263 def new_link(node_var = nil) 264 self.clone.tap do |new_query_proxy| 265 new_query_proxy.instance_variable_set('@result_cache', nil) 266 new_query_proxy.instance_variable_set('@node_var', node_var) if node_var 267 end 268 end
# File lib/neo4j/active_node/query/query_proxy.rb 257 def optional? 258 @optional == true 259 end
# File lib/neo4j/active_node/query/query_proxy.rb 78 def params(params) 79 new_link.tap { |new_query| new_query._add_params(params) } 80 end
Like calling query_as
, but for when you don’t care about the variable name
# File lib/neo4j/active_node/query/query_proxy.rb 83 def query 84 query_as(identity) 85 end
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 94 def query_as(var, with_labels = true) 95 result_query = @chain.inject(base_query(var, with_labels).params(@params)) do |query, link| 96 args = link.args(var, rel_var) 97 98 args.is_a?(Array) ? query.send(link.clause, *args) : query.send(link.clause, args) 99 end 100 101 result_query.tap { |query| query.proxy_chain_level = _chain_level } 102 end
# File lib/neo4j/active_node/query/query_proxy.rb 237 def read_attribute_for_serialization(*args) 238 to_a.map { |o| o.read_attribute_for_serialization(*args) } 239 end
# File lib/neo4j/active_node/query/query_proxy.rb 72 def rel_identity 73 ActiveSupport::Deprecation.warn 'rel_identity is deprecated and may be removed from future releases, use rel_var instead.', caller 74 75 @rel_var 76 end
# File lib/neo4j/active_node/query/query_proxy.rb 253 def respond_to_missing?(method_name, include_all = false) 254 (@model && @model.respond_to?(method_name, include_all)) || super 255 end
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 133 def scoping 134 previous = @model.current_scope 135 @model.current_scope = self 136 yield 137 ensure 138 @model.current_scope = previous 139 end
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 160 def to_cypher_with_params(columns = [self.identity]) 161 final_query = query.return_query(columns) 162 "#{final_query.to_cypher} | params: #{final_query.send(:merge_params)}" 163 end
Protected Instance Methods
# File lib/neo4j/active_node/query/query_proxy.rb 277 def _add_links(links) 278 @chain += links 279 end
Methods are underscored to prevent conflict with user class methods
# File lib/neo4j/active_node/query/query_proxy.rb 273 def _add_params(params) 274 @params = @params.merge(params) 275 end
# File lib/neo4j/active_node/query/query_proxy.rb 313 def _association_arrow(properties = {}, create = false) 314 @association && @association.arrow_cypher(@rel_var, properties, create, false, @rel_length) 315 end
# File lib/neo4j/active_node/query/query_proxy.rb 321 def _association_chain_var 322 fail 'Crazy error' if !(start_object || @query_proxy) 323 324 if start_object 325 :"#{start_object.class.name.gsub('::', '_').downcase}#{start_object.neo_id}" 326 else 327 @query_proxy.node_var || :"node#{_chain_level}" 328 end 329 end
# File lib/neo4j/active_node/query/query_proxy.rb 331 def _association_query_start(var) 332 # TODO: Better error 333 fail 'Crazy error' if !(object = (start_object || @query_proxy)) 334 335 object.query_as(var) 336 end
# File lib/neo4j/active_node/query/query_proxy.rb 317 def _chain_level 318 (@query_proxy ? @query_proxy._chain_level : (@chain_level || 0)) + 1 319 end
@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 287 def _match_arg(var, with_labels) 288 if @model && with_labels != false 289 labels = @model.respond_to?(:mapped_label_names) ? _model_label_string : @model 290 {var.to_sym => labels} 291 else 292 var.to_sym 293 end 294 end
# File lib/neo4j/active_node/query/query_proxy.rb 296 def _query 297 _session.query(context: @context) 298 end
# File lib/neo4j/active_node/query/query_proxy.rb 281 def _query_model_as(var, with_labels = true) 282 _query.break.send(@match_type, _match_arg(var, with_labels)) 283 end
# File lib/neo4j/active_node/query/query_proxy.rb 338 def _rel_chain_var 339 :"rel#{_chain_level - 1}" 340 end
TODO: Refactor this. Too much happening here.
# File lib/neo4j/active_node/query/query_proxy.rb 301 def _result_string 302 s = (self.association && self.association.name) || (self.model && self.model.name) || '' 303 304 s ? "result_#{s}".downcase.tr(':', '').to_sym : :result 305 end
# File lib/neo4j/active_node/query/query_proxy.rb 307 def _session 308 (@session || (@model && @model.neo4j_session)).tap do |session| 309 fail 'No session found!' if session.nil? 310 end 311 end
Private Instance Methods
# File lib/neo4j/active_node/query/query_proxy.rb 353 def build_deeper_query_proxy(method, args) 354 new_link.tap do |new_query_proxy| 355 Link.for_args(@model, method, args, association).each { |link| new_query_proxy._add_links(link) } 356 end 357 end
# File lib/neo4j/active_node/query/query_proxy.rb 346 def instance_vars_from_options!(options) 347 @node_var, @session, @source_object, @starting_query, @optional, 348 @start_object, @query_proxy, @chain_level, @association_labels, 349 @rel_length = options.values_at(:node, :session, :source_object, :starting_query, :optional, 350 :start_object, :query_proxy, :chain_level, :association_labels, :rel_length) 351 end