Class: Boltless::Transaction

Inherits:
Object
  • Object
show all
Defined in:
lib/boltless/transaction.rb

Overview

A single neo4j transaction representation.

When passing Cypher statements you can tweak some HTTP API result options while passing the following keys to the Cypher parameters (they wont be sent to neo4j):

* +with_stats: true|false+: whenever to include statement
  statistics, or not (see: https://bit.ly/3SKXfC8)
* +result_as_graph: true|false+: whenever to return the result as a graph
  structure that can be visualized (see: https://bit.ly/3doJw3Z)

Error handling details (see: bit.ly/3pdqTCy):

> If there is an error in a request, the server will roll back the > transaction. You can tell if the transaction is still open by inspecting > the response for the presence/absence of the transaction key.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(connection, database: Boltless.configuration.default_db, access_mode: :write, raw_results: false) ⇒ Transaction

Setup a new neo4j transaction management instance.

Parameters:

  • connection (HTTP::Client)

    a ready to use persistent connection object

  • database (String, Symbol) (defaults to: Boltless.configuration.default_db)

    the neo4j database to use

  • access_mode (String, Symbol) (defaults to: :write)

    the neo4j transaction mode (:read, or :write)

  • raw_results (Boolean) (defaults to: false)

    whenever to return the plain HTTP API JSON results (as plain Hash{Symbol => Mixed}/Array data), or not (then we return Array<Boltless::Result> structs



39
40
41
42
43
44
45
46
# File 'lib/boltless/transaction.rb', line 39

def initialize(connection, database: Boltless.configuration.default_db,
               access_mode: :write, raw_results: false)
  @request = Request.new(connection, access_mode: access_mode,
                                     database: database,
                                     raw_results: raw_results)
  @access_mode = access_mode
  @raw_state = :not_yet_started
end

Instance Attribute Details

#access_modeObject (readonly)

We allow to read some internal configurations



22
23
24
# File 'lib/boltless/transaction.rb', line 22

def access_mode
  @access_mode
end

#idObject (readonly)

We allow to read some internal configurations



22
23
24
# File 'lib/boltless/transaction.rb', line 22

def id
  @id
end

#raw_stateObject (readonly)

We allow to read some internal configurations



22
23
24
# File 'lib/boltless/transaction.rb', line 22

def raw_state
  @raw_state
end

Instance Method Details

#beginBoolean

Begin a new transaction. We rescue all errors transparently.

Returns:

  • (Boolean)

    whenever the transaction was successfully started, or not



82
83
84
# File 'lib/boltless/transaction.rb', line 82

def begin
  handle_errors(false) { begin! }
end

#begin!TrueClass

Begin a new transaction. No exceptions will be rescued.

rubocop:disable Naming/PredicateMethod – because this method performs an

action, not a predicate check (bool is for error signaling)

Returns:

  • (TrueClass)

    when the transaction was successfully started

Raises:



65
66
67
68
69
70
71
72
73
74
75
# File 'lib/boltless/transaction.rb', line 65

def begin!
  # We do not allow messing around in wrong states
  unless @raw_state == :not_yet_started
    raise Errors::TransactionInBadStateError,
          "Transaction already #{@raw_state}"
  end

  @id = @request.begin_transaction
  @raw_state = :open
  true
end

#cleanupObject

Clean the transaction, in order to make it unusable for further interaction. This prevents users from leaking the transaction context and mess around with the connection pool.



237
238
239
240
# File 'lib/boltless/transaction.rb', line 237

def cleanup
  @request = nil
  @raw_state = :cleaned
end

#commit(*statements) ⇒ Array<Hash{Symbol => Mixed}>?

Commit the transaction, while also sending finalizing Cypher statement(s). This results in a single HTTP API request for all the statement(s). You can also omit the statement(s) in order to just commit the transaction. We rescue all errors transparently.

Parameters:

  • statements (Array<Hash>)

    the Cypher statements to run

Returns:

  • (Array<Hash{Symbol => Mixed}>, nil)

    the raw neo4j results, or nil in case of errors

Raises:



177
178
179
# File 'lib/boltless/transaction.rb', line 177

def commit(*statements)
  handle_errors { commit!(*statements) }
end

#commit!(*statements) ⇒ Array<Hash{Symbol => Mixed}>

Commit the transaction, while also sending finalizing Cypher statement(s). This results in a single HTTP API request for all the statement(s). You can also omit the statement(s) in order to just commit the transaction.

Parameters:

  • statements (Array<Hash>)

    the Cypher statements to run

Returns:

  • (Array<Hash{Symbol => Mixed}>)

    the raw neo4j results

Raises:



155
156
157
158
159
160
161
162
163
164
# File 'lib/boltless/transaction.rb', line 155

def commit!(*statements)
  # We do not allow messing around in wrong states
  raise Errors::TransactionInBadStateError, 'Transaction not open' \
    unless @raw_state == :open

  @request.commit_transaction(
    @id,
    *Request.statement_payloads(*statements)
  ).tap { @raw_state = :closed }
end

#handle_errors(error_result = nil) { ... } ⇒ Mixed

Handle all request/response errors of the low-level connection for our non-bang methods in a generic way.

Parameters:

  • error_result (Proc, Mixed) (defaults to: nil)

    the object to return on errors, when a proc is given, we call it with the actual exception object as parameter and use the result of the proc as return value

Yields:

  • the given block

Returns:

  • (Mixed)

    the result of the block, or on exceptions the given error_result



218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/boltless/transaction.rb', line 218

def handle_errors(error_result = nil)
  yield
rescue Errors::RequestError, Errors::ResponseError,
       Errors::TransactionInBadStateError => e
  # When an error occured, the transaction is automatically rolled back by
  # neo4j, so we cannot handle any further interaction
  cleanup
  @raw_state = :closed

  # When we got a proc/lambda as error result, call it
  return error_result.call(e) if error_result.is_a? Proc

  # Otherwise use the error result as it is
  error_result
end

#rollbackBoolean

Rollback this transaction. We rescue all errors transparently.

Returns:

  • (Boolean)

    whenever the transaction was successfully rolled back, or not



205
206
207
# File 'lib/boltless/transaction.rb', line 205

def rollback
  handle_errors(false) { rollback! }
end

#rollback!TrueClass

Rollback this transaction. No exceptions will be rescued.

rubocop:disable Naming/PredicateMethod – because this method performs

an action, not a predicate check (bool is for error signaling)

Returns:

  • (TrueClass)

    when the transaction was successfully rolled back

Raises:



190
191
192
193
194
195
196
197
198
# File 'lib/boltless/transaction.rb', line 190

def rollback!
  # We do not allow messing around in wrong states
  raise Errors::TransactionInBadStateError, 'Transaction not open' \
    unless @raw_state == :open

  @request.rollback_transaction(@id)
  @raw_state = :closed
  true
end

#run(cypher, **args) ⇒ Array<Hash{Symbol => Mixed}>?

Run a single Cypher statement inside the transaction. This results in a single HTTP API request for the statement. We rescue all errors transparently.

Parameters:

  • cypher (String)

    the Cypher statement to run

  • args (Hash{Symbol => Mixed})

    the additional Cypher parameters

Returns:

  • (Array<Hash{Symbol => Mixed}>, nil)

    the raw neo4j results, or nil in case of errors



111
112
113
# File 'lib/boltless/transaction.rb', line 111

def run(cypher, **args)
  handle_errors { run!(cypher, **args) }
end

#run!(cypher, **args) ⇒ Hash{Symbol => Mixed}

Run a single Cypher statement inside the transaction. This results in a single HTTP API request for the statement.

Parameters:

  • cypher (String)

    the Cypher statement to run

  • args (Hash{Symbol => Mixed})

    the additional Cypher parameters

Returns:

  • (Hash{Symbol => Mixed})

    the raw neo4j results

Raises:



95
96
97
98
99
100
101
# File 'lib/boltless/transaction.rb', line 95

def run!(cypher, **args)
  # We do not allow messing around in wrong states
  raise Errors::TransactionInBadStateError, 'Transaction not open' \
    unless @raw_state == :open

  @request.run_query(@id, Request.statement_payload(cypher, **args)).first
end

#run_in_batch(*statements) ⇒ Array<Hash{Symbol => Mixed}>?

Run a multiple Cypher statement inside the transaction. This results in a single HTTP API request for all the statements. We rescue all errors transparently.

Parameters:

  • statements (Array<Hash>)

    the Cypher statements to run

Returns:

  • (Array<Hash{Symbol => Mixed}>, nil)

    the raw neo4j results, or nil in case of errors

Raises:



141
142
143
# File 'lib/boltless/transaction.rb', line 141

def run_in_batch(*statements)
  handle_errors { run_in_batch!(*statements) }
end

#run_in_batch!(*statements) ⇒ Array<Hash{Symbol => Mixed}>

Run a multiple Cypher statement inside the transaction. This results in a single HTTP API request for all the statements.

Parameters:

  • statements (Array<Hash>)

    the Cypher statements to run

Returns:

  • (Array<Hash{Symbol => Mixed}>)

    the raw neo4j results

Raises:



123
124
125
126
127
128
129
# File 'lib/boltless/transaction.rb', line 123

def run_in_batch!(*statements)
  # We do not allow messing around in wrong states
  raise Errors::TransactionInBadStateError, 'Transaction not open' \
    unless @raw_state == :open

  @request.run_query(@id, *Request.statement_payloads(*statements))
end

#stateActiveSupport::StringInquirer

Return the transaction state as ActiveSupport::StringInquirer for convenience.

Returns:

  • (ActiveSupport::StringInquirer)

    the transaction state



52
53
54
# File 'lib/boltless/transaction.rb', line 52

def state
  ActiveSupport::StringInquirer.new(@raw_state.to_s)
end