class ActiveGraph::Core::Query

Allows for generation of cypher queries via ruby method calls (inspired by ActiveRecord / arel syntax)

Can be used to express cypher queries in ruby nicely, or to more easily generate queries programatically.

Also, queries can be passed around an application to progressively build a query across different concerns

See also the following link for full cypher language documentation: docs.neo4j.org/chunked/milestone/cypher-query-lang.html

Constants

BREAK_METHODS
CLAUSES
CLAUSIFY_CLAUSE
DEFINED_CLAUSES
EMPTY

Returns a CYPHER query string from the object query representation @example

Query.new.match(p: :Person).where(p: {age: 30})  # => "MATCH (p:Person) WHERE p.age = 30

@return [String] Resulting cypher query string

METHODS

@method detach_delete *args DETACH DELETE clause @return [Query]

NEWLINE

Attributes

pretty_cypher[RW]
_params[RW]
clauses[RW]
options[RW]
proxy_chain_level[RW]

For instances where you turn a QueryProxy into a Query and then back to a QueryProxy with `#proxy_as`

Public Class Methods

new(options = {}) click to toggle source
   # File lib/active_graph/core/query.rb
70 def initialize(options = {})
71   @options = options
72   @clauses = []
73   @_params = {}
74   @params = Parameters.new
75 end

Public Instance Methods

&(other) click to toggle source
    # File lib/active_graph/core/query.rb
374 def &(other)
375   self.class.new.tap do |new_query|
376     new_query.options = options.merge(other.options)
377     new_query.clauses = clauses + other.clauses
378   end.params(other._params)
379 end
break() click to toggle source

Allows what's been built of the query so far to be frozen and the rest built anew. Can be called multiple times in a string of method calls @example

# Creates a query representing the cypher: MATCH (q:Person), r:Car MATCH (p: Person)-->q
Query.new.match(q: Person).match('r:Car').break.match('(p: Person)-->q')
    # File lib/active_graph/core/query.rb
211 def break
212   build_deeper_query(nil)
213 end
clause?(method) click to toggle source
    # File lib/active_graph/core/query.rb
390 def clause?(method)
391   clause_class = DEFINED_CLAUSES[method] || CLAUSIFY_CLAUSE.call(method)
392   clauses.any? { |clause| clause.is_a?(clause_class) }
393 end
context() click to toggle source
    # File lib/active_graph/core/query.rb
343 def context
344   @options[:context]
345 end
copy() click to toggle source
    # File lib/active_graph/core/query.rb
381 def copy
382   dup.tap do |query|
383     to_cypher
384     query.instance_variable_set('@params'.freeze, @params.copy)
385     query.instance_variable_set('@partitioned_clauses'.freeze, nil)
386     query.instance_variable_set('@response'.freeze, nil)
387   end
388 end
count(var = nil) click to toggle source
    # File lib/active_graph/core/query.rb
260 def count(var = nil)
261   v = var.nil? ? '*' : var
262   pluck("count(#{v})").first
263 end
cypher(options = {})
Alias for: to_cypher
each() { |object| ... } click to toggle source
    # File lib/active_graph/core/query.rb
265 def each
266   response.each { |object| yield object }
267 end
exec() click to toggle source

Executes a query without returning the result @return [Boolean] true if successful @raise [ActiveGraph::Server::CypherResponse::ResponseError] Raises errors from neo4j server

    # File lib/active_graph/core/query.rb
278 def exec
279   response
280 
281   true
282 end
inspect() click to toggle source
   # File lib/active_graph/core/query.rb
77 def inspect
78   "#<Query CYPHER: #{ANSI::YELLOW}#{to_cypher.inspect}#{ANSI::CLEAR}>"
79 end
match_nodes(hash, optional_match = false) click to toggle source
    # File lib/active_graph/core/query.rb
245 def match_nodes(hash, optional_match = false)
246   hash.inject(self) do |query, (variable, node_object)|
247     neo_id = (node_object.respond_to?(:neo_id) ? node_object.neo_id : node_object)
248 
249     match_method = optional_match ? :optional_match : :match
250     query.send(match_method, variable).where(variable => {neo_id: neo_id})
251   end
252 end
optional_match_nodes(hash) click to toggle source
    # File lib/active_graph/core/query.rb
254 def optional_match_nodes(hash)
255   match_nodes(hash, true)
256 end
parameters() click to toggle source
    # File lib/active_graph/core/query.rb
347 def parameters
348   to_cypher
349   merge_params
350 end
params(args) click to toggle source

Allows for the specification of values for params specified in query @example

# Creates a query representing the cypher: MATCH (q: Person {id: $id})
# Calls to params don't affect the cypher query generated, but the params will be
# Passed down when the query is made
Query.new.match('(q: Person {id: $id})').params(id: 12)
    # File lib/active_graph/core/query.rb
222 def params(args)
223   copy.tap { |new_query| new_query.instance_variable_get('@params'.freeze).add_params(args) }
224 end
partitioned_clauses() click to toggle source
    # File lib/active_graph/core/query.rb
352 def partitioned_clauses
353   @partitioned_clauses ||= PartitionedClauses.new(@clauses)
354 end
pluck(*columns) click to toggle source

Return the specified columns as an array. If one column is specified, a one-dimensional array is returned with the values of that column If two columns are specified, a n-dimensional array is returned with the values of those columns

@example

Query.new.match(n: :Person).return(p: :name}.pluck(p: :name) # => Array of names

@example

Query.new.match(n: :Person).return(p: :name}.pluck('p, DISTINCT p.name') # => Array of [node, name] pairs
    # File lib/active_graph/core/query.rb
293 def pluck(*columns)
294   fail ArgumentError, 'No columns specified for Query#pluck' if columns.size.zero?
295 
296   query = return_query(columns)
297   columns = query.response.keys
298 
299   if columns.size == 1
300     column = columns[0]
301     query.map { |row| row[column] }
302   else
303     query.map { |row| columns.map { |column| row[column] } }
304   end
305 end
pretty_cypher() click to toggle source
    # File lib/active_graph/core/query.rb
339 def pretty_cypher
340   to_cypher(pretty: true)
341 end
print_cypher() click to toggle source
proxy_as(model, var, optional = false) click to toggle source

Creates a ActiveGraph::Node::Query::QueryProxy object that builds off of a Core::Query object.

@param [Class] model An Node model to be used as the start of a new QueryuProxy chain @param [Symbol] var The variable to be used to refer to the object from within the new QueryProxy @param [Boolean] optional Indicate whether the new QueryProxy will use MATCH or OPTIONAL MATCH. @return [ActiveGraph::Node::Query::QueryProxy] A QueryProxy object.

   # File lib/active_graph/core/query_ext.rb
10 def proxy_as(model, var, optional = false)
11   # TODO: Discuss whether it's necessary to call `break` on the query or if this should be left to the user.
12   ActiveGraph::Node::Query::QueryProxy.new(model, nil, node: var, optional: optional, starting_query: self, chain_level: @proxy_chain_level)
13 end
proxy_as_optional(model, var) click to toggle source

Calls proxy_as with `optional` set true. This doesn't offer anything different from calling `proxy_as` directly but it may be more readable.

   # File lib/active_graph/core/query_ext.rb
16 def proxy_as_optional(model, var)
17   proxy_as(model, var, true)
18 end
raise_if_cypher_error!(response) click to toggle source
    # File lib/active_graph/core/query.rb
241 def raise_if_cypher_error!(response)
242   response.raise_cypher_error if response.respond_to?(:error?) && response.error?
243 end
reorder(*args) click to toggle source

Clears out previous order clauses and allows only for those specified by args

    # File lib/active_graph/core/query.rb
186 def reorder(*args)
187   query = copy
188 
189   query.remove_clause_class(OrderClause)
190   query.order(*args)
191 end
response() click to toggle source
    # File lib/active_graph/core/query.rb
235 def response
236   return @response if @response
237 
238   @response = ActiveGraph::Base.query(self, wrap: !unwrapped?)
239 end
return_query(columns) click to toggle source
    # File lib/active_graph/core/query.rb
307 def return_query(columns)
308   query = copy
309   query.remove_clause_class(ReturnClause)
310 
311   query.return(*columns)
312 end
set_props(*args) click to toggle source

Works the same as the set method, but when given a nested array it will set properties rather than setting entire objects @example

# Creates a query representing the cypher: MATCH (n:Person) SET n.age = 19
Query.new.match(n: :Person).set_props(n: {age: 19})
    # File lib/active_graph/core/query.rb
203 def set_props(*args) # rubocop:disable Naming/AccessorMethodName
204   build_deeper_query(SetClause, args, set_props: true)
205 end
to_cypher(options = {}) click to toggle source
    # File lib/active_graph/core/query.rb
321 def to_cypher(options = {})
322   join_string = options[:pretty] ? NEWLINE : EMPTY
323 
324   cypher_string = partitioned_clauses.map do |clauses|
325     clauses_by_class = clauses.group_by(&:class)
326 
327     cypher_parts = CLAUSES.map do |clause_class|
328       clause_class.to_cypher(clauses, options[:pretty]) if clauses = clauses_by_class[clause_class]
329     end.compact
330 
331     cypher_parts.join(join_string).tap(&:strip!)
332   end.join(join_string)
333 
334   cypher_string = "CYPHER #{@options[:parser]} #{cypher_string}" if @options[:parser]
335   cypher_string.tap(&:strip!)
336 end
Also aliased as: cypher
union_cypher(other, options = {}) click to toggle source

Returns a CYPHER query specifying the union of the callee object's query and the argument's query

@example

# Generates cypher: MATCH (n:Person) UNION MATCH (o:Person) WHERE o.age = 10
q = ActiveGraph::Core::Query.new.match(o: :Person).where(o: {age: 10})
result = ActiveGraph::Core::Query.new.match(n: :Person).union_cypher(q)

@param other [Query] Second half of UNION @param options [Hash] Specify {all: true} to use UNION ALL @return [String] Resulting UNION cypher query string

    # File lib/active_graph/core/query.rb
370 def union_cypher(other, options = {})
371   "#{to_cypher} UNION#{options[:all] ? ' ALL' : ''} #{other.to_cypher}"
372 end
unwrapped() click to toggle source
    # File lib/active_graph/core/query.rb
226 def unwrapped
227   @_unwrapped_obj = true
228   self
229 end
unwrapped?() click to toggle source
    # File lib/active_graph/core/query.rb
231 def unwrapped?
232   !!@_unwrapped_obj
233 end
where_not(*args) click to toggle source

Works the same as the where method, but the clause is surrounded by a Cypher NOT() function

    # File lib/active_graph/core/query.rb
195 def where_not(*args)
196   build_deeper_query(WhereClause, args, not: true)
197 end

Protected Instance Methods

add_clauses(clauses) click to toggle source
    # File lib/active_graph/core/query.rb
399 def add_clauses(clauses)
400   @clauses += clauses
401 end
remove_clause_class(clause_class) click to toggle source
    # File lib/active_graph/core/query.rb
403 def remove_clause_class(clause_class)
404   @clauses = @clauses.reject { |clause| clause.is_a?(clause_class) }
405 end

Private Instance Methods

build_deeper_query(clause_class, args = {}, options = {}) click to toggle source
    # File lib/active_graph/core/query.rb
409 def build_deeper_query(clause_class, args = {}, options = {})
410   copy.tap do |new_query|
411     new_query.add_clauses [nil] if [nil, WithClause].include?(clause_class)
412     new_query.add_clauses clause_class.from_args(args, new_query.instance_variable_get('@params'.freeze), options) if clause_class
413   end
414 end
merge_params() click to toggle source

SHOULD BE DEPRECATED

    # File lib/active_graph/core/query.rb
479 def merge_params
480   @merge_params_base ||= @clauses.compact.inject({}) { |params, clause| params.merge!(clause.params) }
481   @params.to_hash.merge(@merge_params_base)
482 end