Class: Tina4::Drivers::UltipaGraphDriver

Inherits:
GraphAdapter show all
Defined in:
lib/tina4/drivers/ultipa_graph_driver.rb

Constant Summary collapse

UNBOUNDED_CONNECT_TIMEOUT =

nil (unbounded) is mapped to a very large finite deadline for the gRPC driver, whose connect deadline is Time.now + connect_timeout (a 0 there would mean "do not wait", not "wait forever").

315_360_000

Instance Method Summary collapse

Methods inherited from GraphAdapter

#get_error

Constructor Details

#initialize(graph_url, username: "", password: "") ⇒ UltipaGraphDriver

~10 years



27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/tina4/drivers/ultipa_graph_driver.rb', line 27

def initialize(graph_url, username: "", password: "")
  @url = graph_url
  @last_error = nil
  timeout = Tina4.resolve_graph_connect_timeout
  @client = Tina4Ultipa::Client.new(
    host: graph_url.host,
    port: graph_url.port,
    username: graph_url.username || (username unless username.to_s.empty?),
    password: graph_url.password || (password unless password.to_s.empty?),
    graph: graph_url.graph,
    connect_timeout: timeout.nil? ? UNBOUNDED_CONNECT_TIMEOUT : timeout,
    use_tls: graph_url.use_tls,
  )
end

Instance Method Details

#add_edge(from_id, to_id, type, properties = nil) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/tina4/drivers/ultipa_graph_driver.rb', line 64

def add_edge(from_id, to_id, type, properties = nil)
  # id(e) requires EDGE_ID enabled on the Ultipa graph
  # (`ALTER GRAPH <g> SET EDGE_ID ENABLED`), a one-time per-graph setting.
  prop_map, params = prop_clause(properties)
  params["from_id"] = from_id
  params["to_id"] = to_id
  gql = "MATCH (a), (b) WHERE id(a) = $from_id AND id(b) = $to_id " \
        "INSERT (a)-[e:`#{type}` #{prop_map}]->(b) " \
        "RETURN id(e) AS id, type(e) AS type, id(a) AS f, id(b) AS t, " \
        "properties(e) AS props"
  rows = run(gql, params: params, read_only: false).dicts
  return nil if rows.empty?

  row = rows[0]
  Tina4::GraphEdge.new(
    id: row["id"], type: row["type"], from: row["f"], to: row["t"],
    properties: row["props"] || {},
  )
end

#add_node(label, properties = nil) ⇒ Object

-- portable node/edge/traverse core (GQL) --------------------------



56
57
58
59
60
61
62
# File 'lib/tina4/drivers/ultipa_graph_driver.rb', line 56

def add_node(label, properties = nil)
  prop_map, params = prop_clause(properties)
  gql = "INSERT (n:`#{label}` #{prop_map}) " \
        "RETURN id(n) AS id, labels(n) AS labels, properties(n) AS props"
  rows = run(gql, params: params, read_only: false).dicts
  node_from_row(rows[0])
end

#closeObject



140
141
142
# File 'lib/tina4/drivers/ultipa_graph_driver.rb', line 140

def close
  @client.close
end

#delete_node(node_id) ⇒ Object



103
104
105
106
107
# File 'lib/tina4/drivers/ultipa_graph_driver.rb', line 103

def delete_node(node_id)
  run("MATCH (n) WHERE id(n) = $id DETACH DELETE n",
      params: { "id" => node_id }, read_only: false)
  true
end

#execute(text, params = nil) ⇒ Object



49
50
51
52
# File 'lib/tina4/drivers/ultipa_graph_driver.rb', line 49

def execute(text, params = nil)
  result = run(text, params: params, read_only: false)
  Tina4::GraphResult.new(records: result.dicts, columns: result.columns)
end

#get_node(node_id) ⇒ Object



84
85
86
87
88
89
# File 'lib/tina4/drivers/ultipa_graph_driver.rb', line 84

def get_node(node_id)
  gql = "MATCH (n) WHERE id(n) = $id " \
        "RETURN id(n) AS id, labels(n) AS labels, properties(n) AS props"
  rows = run(gql, params: { "id" => node_id }, read_only: true).dicts
  node_from_row(rows[0])
end

#neighbors(node_id, direction: "both", edge_type: nil, limit: 100) ⇒ Object



109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/tina4/drivers/ultipa_graph_driver.rb', line 109

def neighbors(node_id, direction: "both", edge_type: nil, limit: 100)
  edge = edge_type ? ":`#{edge_type}`" : ""
  pattern = case direction
            when "out" then "(n)-[#{edge}]->(m)"
            when "in" then "(n)<-[#{edge}]-(m)"
            else "(n)-[#{edge}]-(m)"
            end
  gql = "MATCH #{pattern} WHERE id(n) = $id " \
        "RETURN DISTINCT id(m) AS id, labels(m) AS labels, properties(m) AS props " \
        "LIMIT #{limit.to_i}"
  rows = run(gql, params: { "id" => node_id }, read_only: true).dicts
  rows.map { |row| node_from_row(row) }
end

#query(text, params = nil) ⇒ Object

-- connection + raw pass-through -----------------------------------



44
45
46
47
# File 'lib/tina4/drivers/ultipa_graph_driver.rb', line 44

def query(text, params = nil)
  result = run(text, params: params, read_only: true)
  Tina4::GraphResult.new(records: result.dicts, columns: result.columns)
end

#traverse(start_id, depth: 1, direction: "both", edge_type: nil, limit: 1000) ⇒ Object



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/tina4/drivers/ultipa_graph_driver.rb', line 123

def traverse(start_id, depth: 1, direction: "both", edge_type: nil, limit: 1000)
  # Ultipa GQL uses the ISO quantified-path form `-[]->{1,N}`, NOT Cypher's
  # `-[*1..N]->` (which is a parse error on gqldb).
  edge = edge_type ? ":`#{edge_type}`" : ""
  quant = "{1,#{depth.to_i}}"
  pattern = case direction
            when "out" then "(n)-[#{edge}]->#{quant}(m)"
            when "in" then "(n)<-[#{edge}]-#{quant}(m)"
            else "(n)-[#{edge}]-#{quant}(m)"
            end
  gql = "MATCH #{pattern} WHERE id(n) = $start " \
        "RETURN DISTINCT id(m) AS id, labels(m) AS labels, properties(m) AS props " \
        "LIMIT #{limit.to_i}"
  rows = run(gql, params: { "start" => start_id }, read_only: true).dicts
  rows.map { |row| node_from_row(row) }
end

#update_node(node_id, properties) ⇒ Object



91
92
93
94
95
96
97
98
99
100
101
# File 'lib/tina4/drivers/ultipa_graph_driver.rb', line 91

def update_node(node_id, properties)
  properties ||= {}
  sets = properties.keys.map { |key| "n.#{key} = $p_#{key}" }.join(", ")
  params = {}
  properties.each { |key, value| params["p_#{key}"] = value }
  params["id"] = node_id
  gql = "MATCH (n) WHERE id(n) = $id SET #{sets} " \
        "RETURN id(n) AS id, labels(n) AS labels, properties(n) AS props"
  rows = run(gql, params: params, read_only: false).dicts
  node_from_row(rows[0])
end