Class: ActiveGraph::Node::Query::QueryProxy
- Inherits:
-
Object
- Object
- ActiveGraph::Node::Query::QueryProxy
- Includes:
- Dependent::QueryProxyMethods, QueryProxyEagerLoading, QueryProxyEnumerable, QueryProxyFindInBatches, QueryProxyMethods, QueryProxyMethodsOfMassUpdating
- Defined in:
- lib/active_graph/node/query/query_proxy.rb,
lib/active_graph/node/query/query_proxy_link.rb
Overview
rubocop:disable Metrics/ClassLength
Defined Under Namespace
Classes: Link
Constant Summary collapse
- METHODS =
%w(where where_not rel_where rel_where_not rel_order order skip limit union)
Constants included from QueryProxyMethods
ActiveGraph::Node::Query::QueryProxyMethods::FIRST, ActiveGraph::Node::Query::QueryProxyMethods::LAST
Instance Attribute Summary collapse
-
#association ⇒ Object
readonly
The most recent node to start a QueryProxy chain.
-
#context ⇒ Object
readonly
Returns the value of attribute context.
-
#model ⇒ Object
readonly
The most recent node to start a QueryProxy chain.
-
#node_var ⇒ Object
readonly
The current node identifier on deck, so to speak.
-
#query_proxy ⇒ Object
readonly
Returns the value of attribute query_proxy.
-
#rel_var ⇒ Object
readonly
The relationship identifier most recently used by the QueryProxy chain.
-
#source_object ⇒ Object
readonly
The most recent node to start a QueryProxy chain.
-
#start_object ⇒ Object
readonly
Returns the value of attribute start_object.
-
#starting_query ⇒ Object
readonly
The most recent node to start a QueryProxy chain.
Instance Method Summary collapse
-
#<<(other_node) ⇒ Object
To add a relationship for the node for the association on this QueryProxy.
- #[](index) ⇒ Object
- #_create_relationship(other_node_or_nodes, properties) ⇒ Object
-
#_model_label_string(with_labels = true) ⇒ Object
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.
- #_nodeify!(*args) ⇒ Object
- #base_query(var, with_labels = true) ⇒ Object
-
#branch { ... } ⇒ QueryProxy
Executes the relation chain specified in the block, while keeping the current scope.
- #create(other_nodes, properties = {}) ⇒ Object
- #identity ⇒ Object (also: #node_identity)
- #init_outer_query_var(var) ⇒ Object
-
#initialize(model, association = nil, options = {}) ⇒ QueryProxy
constructor
QueryProxy is Node’s Cypher DSL.
- #inspect ⇒ Object
-
#method_missing(method_name, *args, **kwargs, &block) ⇒ Object
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.
- #new_link(node_var = nil) ⇒ Object
- #optional? ⇒ Boolean
- #params(params) ⇒ Object
-
#query ⇒ Object
Like calling #query_as, but for when you don’t care about the variable name.
-
#query_as(var, with_labels = true) ⇒ Object
Build a ActiveGraph::Core::Query object for the QueryProxy.
- #query_from_chain(chain, base_query, var) ⇒ Object
- #read_attribute_for_serialization(*args) ⇒ Object
- #rel_identity ⇒ Object
- #respond_to_missing?(method_name, include_all = false) ⇒ Boolean
-
#scoping ⇒ Object
Scope all queries to the current scope.
-
#to_cypher_with_params(columns = [self.identity]) ⇒ String
Returns a string of the cypher query with return objects and params.
- #union(*args) ⇒ Object
- #unpersisted_start_object? ⇒ Boolean
Methods included from Dependent::QueryProxyMethods
#each_for_destruction, #unique_nodes
Methods included from QueryProxyEagerLoading
#association_tree_class, #first, #perform_query, #pluck_vars, #propagate_context, #with_associations, #with_associations_tree, #with_associations_tree=
Methods included from QueryProxyFindInBatches
Methods included from QueryProxyMethodsOfMassUpdating
#add_rels, #delete, #delete_all, #delete_all_rels, #delete_rels_for_nodes, #destroy, #replace_with, #update_all, #update_all_rels
Methods included from QueryProxyMethods
#as, #as_models, #count, #distinct, #empty?, #exists?, #find, #find_or_create_by, #find_or_initialize_by, #first, #first_or_initialize, #first_rel_to, #having_rel, #include?, #last, #limit_value, #match_to, #not_having_rel, #optional, #order_property, #propagate_context, #rel, #rels, #rels_to, #size
Methods included from QueryProxyEnumerable
#==, #each, #each_rel, #each_with_rel, #fetch_result_cache, #pluck, #result, #result_cache?, #result_cache_for
Constructor Details
#initialize(model, association = nil, options = {}) ⇒ QueryProxy
QueryProxy is Node’s Cypher DSL. While the name might imply that it creates queries in a general sense, it is actually referring to ActiveGraph::Core::Query, which is a pure Ruby Cypher DSL provided by the activegraph gem. QueryProxy provides ActiveRecord-like methods for common patterns. When it’s not handling CRUD for relationships and queries, it provides Node’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.
originated. has_many) that created this object. QueryProxy objects are evaluated lazily.
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 40 def initialize(model, association = nil, = {}) @model = model @association = association @context = .delete(:context) @options = @associations_spec = [] () @match_type = @optional ? :optional_match : :match @rel_var = [:rel] || _rel_chain_var @chain = [] @params = @query_proxy ? @query_proxy.instance_variable_get('@params') : {} end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method_name, *args, **kwargs, &block) ⇒ Object
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
263 264 265 266 267 268 269 270 271 272 273 274 275 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 263 def method_missing(method_name, *args, **kwargs, &block) if @model && @model.respond_to?(method_name) scoping do if RUBY_VERSION < '3' && kwargs.empty? @model.public_send(method_name, *args, &block) else @model.public_send(method_name, *args, **kwargs, &block) end end else super end end |
Instance Attribute Details
#association ⇒ Object (readonly)
The most recent node to start a QueryProxy chain. Will be nil when using QueryProxy chains on class methods.
16 17 18 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 16 def association @association end |
#context ⇒ Object
Returns the value of attribute context.
285 286 287 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 285 def context @context end |
#model ⇒ Object (readonly)
The most recent node to start a QueryProxy chain. Will be nil when using QueryProxy chains on class methods.
16 17 18 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 16 def model @model end |
#node_var ⇒ Object (readonly)
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.
66 67 68 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 66 def node_var @node_var end |
#query_proxy ⇒ Object (readonly)
Returns the value of attribute query_proxy.
62 63 64 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 62 def query_proxy @query_proxy end |
#rel_var ⇒ Object (readonly)
The relationship identifier most recently used by the QueryProxy chain.
73 74 75 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 73 def rel_var @rel_var end |
#source_object ⇒ Object (readonly)
The most recent node to start a QueryProxy chain. Will be nil when using QueryProxy chains on class methods.
16 17 18 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 16 def source_object @source_object end |
#start_object ⇒ Object (readonly)
Returns the value of attribute start_object.
62 63 64 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 62 def start_object @start_object end |
#starting_query ⇒ Object (readonly)
The most recent node to start a QueryProxy chain. Will be nil when using QueryProxy chains on class methods.
16 17 18 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 16 def starting_query @starting_query end |
Instance Method Details
#<<(other_node) ⇒ Object
To add a relationship for the node for the association on this QueryProxy
188 189 190 191 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 188 def <<(other_node) _create_relation_or_defer(other_node) self end |
#[](index) ⇒ Object
214 215 216 217 218 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 214 def [](index) # TODO: Maybe for this and other methods, use array if already loaded, otherwise # use OFFSET and LIMIT 1? self.to_a[index] end |
#_create_relationship(other_node_or_nodes, properties) ⇒ Object
251 252 253 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 251 def _create_relationship(other_node_or_nodes, properties) association._create_relationship(@start_object, other_node_or_nodes, properties) end |
#_model_label_string(with_labels = true) ⇒ Object
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.
134 135 136 137 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 134 def _model_label_string(with_labels = true) return if !@model || (!with_labels || @association_labels == false) @model.mapped_label_names.map { |label_name| ":`#{label_name}`" }.join end |
#_nodeify!(*args) ⇒ Object
239 240 241 242 243 244 245 246 247 248 249 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 239 def _nodeify!(*args) other_nodes = [args].flatten!.map! do |arg| (arg.is_a?(Integer) || arg.is_a?(String)) ? @model.find_by(id: arg) : arg end.compact if @model && other_nodes.any? { |other_node| !other_node.class.mapped_label_names.include?(@model.mapped_label_name) } fail ArgumentError, "Node must be of the association's class when model is specified" end other_nodes end |
#base_query(var, with_labels = true) ⇒ Object
121 122 123 124 125 126 127 128 129 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 121 def base_query(var, with_labels = true) if @association chain_var = _association_chain_var (_association_query_start(chain_var) & _query).break.send(@match_type, "(#{chain_var})#{_association_arrow}(#{var}#{_model_label_string})") else starting_query ? starting_query : _query_model_as(var, with_labels) end end |
#branch { ... } ⇒ QueryProxy
Executes the relation chain specified in the block, while keeping the current scope
204 205 206 207 208 209 210 211 212 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 204 def branch(&block) fail LocalJumpError, 'no block given' if block.nil? # `as(identity)` is here to make sure we get the right variable # There might be a deeper problem of the variable changing when we # traverse an association as(identity).instance_eval(&block).query.proxy_as(self.model, identity).tap do |new_query_proxy| propagate_context(new_query_proxy) end end |
#create(other_nodes, properties = {}) ⇒ Object
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 220 def create(other_nodes, properties = {}) fail 'Can only create relationships on associations' if !@association other_nodes = _nodeify!(*other_nodes) ActiveGraph::Base.transaction do other_nodes.each do |other_node| if other_node.neo_id other_node.try(:delete_reverse_has_one_core_rel, association) else other_node.save end @start_object.association_proxy_cache.clear _create_relationship(other_node, properties) end end end |
#identity ⇒ Object Also known as: node_identity
67 68 69 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 67 def identity @node_var || _result_string(_chain_level + 1) end |
#init_outer_query_var(var) ⇒ Object
108 109 110 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 108 def init_outer_query_var(var) chain.find(&:start_of_subquery?)&.tap { |link| @outer_query_var = link.subquery_var(var) } end |
#inspect ⇒ Object
57 58 59 60 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 57 def inspect formatted_nodes = ActiveGraph::Node::NodeListFormatter.new(to_a) "#<QueryProxy #{@context} #{formatted_nodes.inspect}>" end |
#new_link(node_var = nil) ⇒ Object
287 288 289 290 291 292 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 287 def new_link(node_var = nil) self.clone.tap do |new_query_proxy| new_query_proxy.instance_variable_set('@result_cache', nil) new_query_proxy.instance_variable_set('@node_var', node_var) if node_var end end |
#optional? ⇒ Boolean
281 282 283 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 281 def optional? @optional == true end |
#params(params) ⇒ Object
80 81 82 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 80 def params(params) new_link.tap { |new_query| new_query._add_params(params) } end |
#query ⇒ Object
Like calling #query_as, but for when you don’t care about the variable name
85 86 87 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 85 def query query_as(identity) end |
#query_as(var, with_labels = true) ⇒ Object
Build a ActiveGraph::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) ActiveGraph::Core::Query.
- .. code-block
-
ruby
student.lessons.query_as(:l).with('your cypher here...')
96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 96 def query_as(var, with_labels = true) init_outer_query_var(var) var_name = @outer_query_var || var query_obj = if chain.first&.start_of_subquery? && !@association && !starting_query _query else base_query(var_name, with_labels).params(@params) end query_from_chain(chain, query_obj, var_name).tap { |query| query.proxy_chain_level = _chain_level } end |
#query_from_chain(chain, base_query, var) ⇒ Object
112 113 114 115 116 117 118 119 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 112 def query_from_chain(chain, base_query, var) chain.inject(base_query) do |query, link| args = link.args(var, rel_var) var = link.update_outer_query_var(var) args.is_a?(Array) ? query.send(link.clause, *args) : query.send(link.clause, args) end end |
#read_attribute_for_serialization(*args) ⇒ Object
255 256 257 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 255 def read_attribute_for_serialization(*args) to_a.map { |o| o.read_attribute_for_serialization(*args) } end |
#rel_identity ⇒ Object
74 75 76 77 78 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 74 def rel_identity ActiveSupport::Deprecation.warn 'rel_identity is deprecated and may be removed from future releases, use rel_var instead.', caller @rel_var end |
#respond_to_missing?(method_name, include_all = false) ⇒ Boolean
277 278 279 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 277 def respond_to_missing?(method_name, include_all = false) (@model && @model.respond_to?(method_name, include_all)) || super end |
#scoping ⇒ Object
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.
150 151 152 153 154 155 156 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 150 def scoping previous = @model.current_scope @model.current_scope = self yield ensure @model.current_scope = previous end |
#to_cypher_with_params(columns = [self.identity]) ⇒ String
Returns a string of the cypher query with return objects and params
182 183 184 185 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 182 def to_cypher_with_params(columns = [self.identity]) final_query = query.return_query(columns) "#{final_query.to_cypher} | params: #{final_query.send(:merge_params)}" end |
#union(*args) ⇒ Object
164 165 166 167 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 164 def union(*args) hash_args = {proxy: self, subquery_parts: args, first_clause: @chain.blank?} build_deeper_query_proxy(:union, hash_args) end |
#unpersisted_start_object? ⇒ Boolean
294 295 296 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 294 def unpersisted_start_object? @start_object && @start_object.new_record? end |