Class: Neo4j::Core::Query

Inherits:
Object
  • Object
show all
Includes:
Enumerable, QueryClauses, QueryFindInBatches
Defined in:
lib/neo4j-core/query.rb

Overview

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

Constant Summary collapse

METHODS =

DELETE clause

Returns:

%w(with start match optional_match using where set create create_unique merge on_create_set on_match_set remove unwind delete return order skip limit)
CLAUSES =
METHODS.map { |method| const_get(method.split('_').map(&:capitalize).join + 'Clause') }
MEMOIZED_INSTANCE_VARIABLES =
[:response, :merge_params]

Instance Method Summary collapse

Methods included from QueryFindInBatches

#find_each, #find_in_batches

Constructor Details

#initialize(options = {}) ⇒ Query

Returns a new instance of Query.



18
19
20
21
22
23
24
# File 'lib/neo4j-core/query.rb', line 18

def initialize(options = {})
  @session = options[:session] || Neo4j::Session.current

  @options = options
  @clauses = []
  @_params = {}
end

Instance Method Details

#&(other) ⇒ Object



274
275
276
277
278
279
280
281
# File 'lib/neo4j-core/query.rb', line 274

def &(other)
  fail "Sessions don't match!" if @session != other.session

  self.class.new(session: @session).tap do |new_query|
    new_query.options = options.merge(other.options)
    new_query.clauses = clauses + other.clauses
  end.params(other._params)
end

#breakObject

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

Examples:

# 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')


138
139
140
# File 'lib/neo4j-core/query.rb', line 138

def break
  build_deeper_query(nil)
end

#copyObject



284
285
286
287
288
289
290
# File 'lib/neo4j-core/query.rb', line 284

def copy
  dup.tap do |query|
    MEMOIZED_INSTANCE_VARIABLES.each do |var|
      query.instance_variable_set("@#{var}", nil)
    end
  end
end

#create(*args) ⇒ Query

CREATE clause

Returns:



# File 'lib/neo4j-core/query.rb', line 78

#create_unique(*args) ⇒ Query

CREATE UNIQUE clause

Returns:



# File 'lib/neo4j-core/query.rb', line 82

#delete(*args) ⇒ Query

DELETE clause

Returns:



102
# File 'lib/neo4j-core/query.rb', line 102

METHODS = %w(with start match optional_match using where set create create_unique merge on_create_set on_match_set remove unwind delete return order skip limit)

#eachObject



170
171
172
173
174
175
176
177
# File 'lib/neo4j-core/query.rb', line 170

def each
  response = self.response
  if response.is_a?(Neo4j::Server::CypherResponse)
    response.to_node_enumeration
  else
    Neo4j::Embedded::ResultWrapper.new(response, to_cypher)
  end.each { |object| yield object }
end

#execBoolean

Executes a query without returning the result

Returns:

  • (Boolean)

    true if successful

Raises:



188
189
190
191
192
# File 'lib/neo4j-core/query.rb', line 188

def exec
  response

  true
end

#limit(*args) ⇒ Query

LIMIT clause

Returns:



# File 'lib/neo4j-core/query.rb', line 54

#match(*args) ⇒ Query

MATCH clause

Returns:



# File 'lib/neo4j-core/query.rb', line 30

#merge(*args) ⇒ Query

MERGE clause

Returns:



# File 'lib/neo4j-core/query.rb', line 86

#on_create_set(*args) ⇒ Query

ON CREATE SET clause

Returns:



# File 'lib/neo4j-core/query.rb', line 90

#on_match_set(*args) ⇒ Query

ON MATCH SET clause

Returns:



# File 'lib/neo4j-core/query.rb', line 94

#optional_match(*args) ⇒ Query

OPTIONAL MATCH clause

Returns:



# File 'lib/neo4j-core/query.rb', line 34

#order(*args) ⇒ Query Also known as: order_by

ORDER BY clause

Returns:



# File 'lib/neo4j-core/query.rb', line 50

#params(args) ⇒ Object

Allows for the specification of values for params specified in query

Examples:

# 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)


149
150
151
152
153
# File 'lib/neo4j-core/query.rb', line 149

def params(args)
  @_params = @_params.merge(args)

  self
end

#pluck(*columns) ⇒ Object

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

Examples:

Query.new.match(n: :Person).return(p: :name}.pluck(p: :name) # => Array of names
Query.new.match(n: :Person).return(p: :name}.pluck('p, DISTINCT p.name') # => Array of [node, name] pairs


203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/neo4j-core/query.rb', line 203

def pluck(*columns)
  query = return_query(columns)
  columns = query.response.columns

  case columns.size
  when 0
    fail ArgumentError, 'No columns specified for Query#pluck'
  when 1
    column = columns[0]
    query.map { |row| row[column] }
  else
    query.map do |row|
      columns.map do |column|
        row[column]
      end
    end
  end
end

#remove(*args) ⇒ Query

REMOVE clause

Returns:



# File 'lib/neo4j-core/query.rb', line 66

#reorder(*args) ⇒ Object

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



119
120
121
122
123
124
# File 'lib/neo4j-core/query.rb', line 119

def reorder(*args)
  query = copy

  query.remove_clause_class(OrderClause)
  query.order(*args)
end

#responseObject



155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/neo4j-core/query.rb', line 155

def response
  return @response if @response
  cypher = to_cypher
  @response = ActiveSupport::Notifications.instrument('neo4j.cypher_query', context: @options[:context] || 'CYPHER', cypher: cypher, params: merge_params) do
    @session._query(cypher, merge_params)
  end
  if !response.respond_to?(:error?) || !response.error?
    response
  else
    response.raise_cypher_error
  end
end

#return(*args) ⇒ Query

RETURN clause

Returns:



# File 'lib/neo4j-core/query.rb', line 74

#return_query(columns) ⇒ Object



222
223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/neo4j-core/query.rb', line 222

def return_query(columns)
  query = copy
  query.remove_clause_class(ReturnClause)

  columns = columns.map do |column_definition|
    if column_definition.is_a?(Hash)
      column_definition.map { |k, v| "#{k}.#{v}" }
    else
      column_definition
    end
  end.flatten.map(&:to_sym)

  query.return(columns)
end

#set(*args) ⇒ Query

SET clause

Returns:



# File 'lib/neo4j-core/query.rb', line 62

#set_props(*args) ⇒ Object

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

Examples:

# Creates a query representing the cypher: MATCH (n:Person) SET n.age = 19
Query.new.match(n: :Person).set_props(n: {age: 19})


130
131
132
# File 'lib/neo4j-core/query.rb', line 130

def set_props(*args)
  build_deeper_query(SetClause, args, set_props: true)
end

#skip(*args) ⇒ Query Also known as: offset

SKIP clause

Returns:



# File 'lib/neo4j-core/query.rb', line 58

#start(*args) ⇒ Query

START clause

Returns:



# File 'lib/neo4j-core/query.rb', line 26

#to_aArray

Class is Enumerable. Each yield is a Hash with the key matching the variable returned and the value being the value for that key from the response

Returns:

  • (Array)

Raises:



# File 'lib/neo4j-core/query.rb', line 179

#to_cypherString

Returns a CYPHER query string from the object query representation

Examples:

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

Returns:

  • (String)

    Resulting cypher query string



242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/neo4j-core/query.rb', line 242

def to_cypher
  cypher_string = partitioned_clauses.map do |clauses|
    clauses_by_class = clauses.group_by(&:class)

    cypher_parts = CLAUSES.map do |clause_class|
      clauses = clauses_by_class[clause_class]

      clause_class.to_cypher(clauses) if clauses
    end

    cypher_string = cypher_parts.compact.join(' ')
    cypher_string.strip
  end.join ' '

  cypher_string = "CYPHER #{@options[:parser]} #{cypher_string}" if @options[:parser]
  cypher_string.strip
end

#union_cypher(other, options = {}) ⇒ String

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

Examples:

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

Parameters:

  • other (Query)

    Second half of UNION

  • options (Hash) (defaults to: {})

    Specify true to use UNION ALL

Returns:

  • (String)

    Resulting UNION cypher query string



270
271
272
# File 'lib/neo4j-core/query.rb', line 270

def union_cypher(other, options = {})
  "#{to_cypher} UNION#{options[:all] ? ' ALL' : ''} #{other.to_cypher}"
end

#unwind(*args) ⇒ Query

UNWIND clause

Returns:



# File 'lib/neo4j-core/query.rb', line 70

#using(*args) ⇒ Query

USING clause

Returns:



# File 'lib/neo4j-core/query.rb', line 38

#where(*args) ⇒ Query

WHERE clause

Returns:



# File 'lib/neo4j-core/query.rb', line 42

#with(*args) ⇒ Query

WITH clause

Returns:



# File 'lib/neo4j-core/query.rb', line 46