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)
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)
-
#initialize(model, association = nil, options = {}) ⇒ QueryProxy
constructor
QueryProxy is Node’s Cypher DSL.
- #inspect ⇒ Object
-
#method_missing(method_name, *args, &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.
- #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, &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
246 247 248 249 250 251 252 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 246 def method_missing(method_name, *args, &block) if @model && @model.respond_to?(method_name) scoping { @model.public_send(method_name, *args, &block) } 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.
262 263 264 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 262 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
171 172 173 174 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 171 def <<(other_node) _create_relation_or_defer(other_node) self end |
#[](index) ⇒ Object
197 198 199 200 201 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 197 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
234 235 236 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 234 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.
122 123 124 125 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 122 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
222 223 224 225 226 227 228 229 230 231 232 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 222 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
109 110 111 112 113 114 115 116 117 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 109 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
187 188 189 190 191 192 193 194 195 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 187 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
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 203 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 |
#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
264 265 266 267 268 269 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 264 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
258 259 260 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 258 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 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 96 def query_as(var, with_labels = true) query_from_chain(chain, base_query(var, with_labels).params(@params), var) .tap { |query| query.proxy_chain_level = _chain_level } end |
#query_from_chain(chain, base_query, var) ⇒ Object
101 102 103 104 105 106 107 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 101 def query_from_chain(chain, base_query, var) chain.inject(base_query) do |query, link| args = link.args(var, rel_var) args.is_a?(Array) ? query.send(link.clause, *args) : query.send(link.clause, args) end end |
#read_attribute_for_serialization(*args) ⇒ Object
238 239 240 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 238 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
254 255 256 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 254 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.
138 139 140 141 142 143 144 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 138 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
165 166 167 168 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 165 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 |
#unpersisted_start_object? ⇒ Boolean
271 272 273 |
# File 'lib/active_graph/node/query/query_proxy.rb', line 271 def unpersisted_start_object? @start_object && @start_object.new_record? end |