Module: ActiveCypher::ConnectionAdapters::PersistenceMethods

Included in:
MemgraphAdapter::Persistence, Neo4jAdapter::Persistence
Defined in:
lib/active_cypher/connection_adapters/persistence_methods.rb

Overview

Common persistence helpers shared by adapters

Instance Method Summary collapse

Instance Method Details

#create_record(model) ⇒ Boolean

Create a record in the database and update model state.

Parameters:

Returns:

  • (Boolean)

    true if created successfully



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/active_cypher/connection_adapters/persistence_methods.rb', line 10

def create_record(model)
  props = model.send(:attributes_for_persistence)
  labels = if model.class.respond_to?(:labels)
             model.class.labels
           else
             [model.class.label_name.to_s]
           end

  adapter = model.connection.id_handler

  # OPTIMIZED: Use string template instead of Cyrel for known-safe CREATE pattern
  # Labels come from model class (safe), props are parameterized (safe)
  label_string = labels.map { |l| ":#{l}" }.join
  cypher = "CREATE (n#{label_string} $props) RETURN #{node_id_expr(adapter)} AS internal_id"

  data = model.connection.execute_cypher(cypher, { props: props }, 'Create')

  return false if data.blank? || !data.first.key?(:internal_id)

  model.internal_id = data.first[:internal_id]
  model.instance_variable_set(:@new_record, false)
  model.send(:changes_applied)
  true
end

#destroy_record(model) ⇒ Boolean

Destroy a record in the database.

Parameters:

Returns:

  • (Boolean)

    true if a record was deleted



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/active_cypher/connection_adapters/persistence_methods.rb', line 70

def destroy_record(model)
  labels = if model.class.respond_to?(:labels)
             model.class.labels
           else
             [model.class.label_name]
           end

  adapter = model.connection.id_handler
  # Convert internal_id to whatever format makes the database feel validated
  # It's like therapy, but for graph databases
  node_id_param = adapter.id_function == 'elementId' ? model.internal_id.to_s : model.internal_id.to_i

  # OPTIMIZED: Use string template for known-safe DELETE pattern
  # Labels come from model class (safe)
  label_string = labels.map { |l| ":#{l}" }.join

  cypher = "MATCH (n#{label_string}) WHERE #{node_id_expr(adapter)} = $node_id DETACH DELETE n RETURN count(*) AS deleted"

  result = model.connection.execute_cypher(cypher, { node_id: node_id_param }, 'Destroy')
  result.present? && result.first[:deleted].to_i.positive?
end

#update_record(model) ⇒ Boolean

Update a record in the database based on model changes.

Parameters:

Returns:

  • (Boolean)

    true if update succeeded



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/active_cypher/connection_adapters/persistence_methods.rb', line 38

def update_record(model)
  changes = model.send(:changes_to_save)
  return true if changes.empty?

  labels = if model.class.respond_to?(:labels)
             model.class.labels
           else
             [model.class.label_name.to_s]
           end

  adapter = model.connection.id_handler
  # Convert internal_id to its preferred existential format
  # Neo4j wants strings because it's complicated, Memgraph wants integers because it's not
  node_id_param = adapter.id_function == 'elementId' ? model.internal_id.to_s : model.internal_id.to_i

  # OPTIMIZED: Use string template for known-safe UPDATE pattern
  # Labels come from model class (safe), property names from model attributes (safe)
  label_string = labels.map { |l| ":#{l}" }.join
  set_clauses = changes.keys.map { |property| "n.#{property} = $#{property}" }.join(', ')

  cypher = "MATCH (n#{label_string}) WHERE #{node_id_expr(adapter)} = $node_id SET #{set_clauses} RETURN n"

  params = changes.merge(node_id: node_id_param)
  model.connection.execute_cypher(cypher, params, 'Update')

  model.send(:changes_applied)
  true
end