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
34
35
36
37
# 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 = if adapter.id_function == 'elementId'
             "CREATE (n#{label_string} $props) RETURN elementId(n) AS internal_id"
           else
             "CREATE (n#{label_string} $props) RETURN id(n) AS internal_id"
           end

  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



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/active_cypher/connection_adapters/persistence_methods.rb', line 78

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 = if adapter.id_function == 'elementId'
             "MATCH (n#{label_string}) WHERE elementId(n) = $node_id DETACH DELETE n RETURN count(*) AS deleted"
           else
             "MATCH (n#{label_string}) WHERE id(n) = $node_id DETACH DELETE n RETURN count(*) AS deleted"
           end

  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



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/active_cypher/connection_adapters/persistence_methods.rb', line 42

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 = if adapter.id_function == 'elementId'
             "MATCH (n#{label_string}) WHERE elementId(n) = $node_id SET #{set_clauses} RETURN n"
           else
             "MATCH (n#{label_string}) WHERE id(n) = $node_id SET #{set_clauses} RETURN n"
           end

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

  model.send(:changes_applied)
  true
end