redgraph 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +8 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile.lock +1 -1
- data/README.md +19 -4
- data/lib/redgraph.rb +5 -0
- data/lib/redgraph/edge.rb +3 -1
- data/lib/redgraph/graph.rb +23 -151
- data/lib/redgraph/graph/edge_methods.rb +78 -0
- data/lib/redgraph/graph/node_methods.rb +86 -0
- data/lib/redgraph/query_response.rb +81 -4
- data/lib/redgraph/version.rb +1 -1
- data/test/graph_edge_methods_test.rb +100 -0
- data/test/graph_node_methods_test.rb +116 -0
- data/test/graph_queries_test.rb +19 -131
- data/test/graph_test.rb +13 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1a432a1b2f9b30601701af0c1464c3c93c14c94da044bad69e531bd3a9f6b26d
|
4
|
+
data.tar.gz: 42f9ce782a67034c5d64c28c02e44d31272945073a1a868c521d7f4213346fc1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fc2c9b7a5d6653fcc997807c994ec55f9be3f514f510c20d9b4569e9d14faedf112462c68e7127e5c7310b7b444d84dfe14b2b8d0332f5a884d3d7c096d5e85e
|
7
|
+
data.tar.gz: 7e031759d2edc7d7577b8df4db41ae055a35f5f46fdb434cf11b36aa7874e3f297c5512164c5e739a56f3ce5eba5200f0a18af9b9eeb0a8feeda09f61e8e73f3
|
data/.github/workflows/main.yml
CHANGED
@@ -5,6 +5,12 @@ on: [push,pull_request]
|
|
5
5
|
jobs:
|
6
6
|
build:
|
7
7
|
runs-on: ubuntu-latest
|
8
|
+
|
9
|
+
services:
|
10
|
+
redis:
|
11
|
+
image: redislabs/redisgraph
|
12
|
+
ports: ["6379:6379"]
|
13
|
+
|
8
14
|
steps:
|
9
15
|
- uses: actions/checkout@v2
|
10
16
|
- name: Set up Ruby
|
@@ -12,6 +18,8 @@ jobs:
|
|
12
18
|
with:
|
13
19
|
ruby-version: 3.0.0
|
14
20
|
- name: Run the default task
|
21
|
+
env:
|
22
|
+
TEST_REDIS_URL: redis://localhost:6379/0
|
15
23
|
run: |
|
16
24
|
gem install bundler -v 2.2.15
|
17
25
|
bundle install
|
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -25,26 +25,35 @@ The gem assumes you have a recent version of [RedisGraph](https://oss.redislabs.
|
|
25
25
|
Basic usage:
|
26
26
|
|
27
27
|
graph = Redgraph::Graph.new('movies', url: "redis://localhost:6379/1")
|
28
|
+
=> #<Redgraph::Graph:0x00007f8d5c2b7e38 @connection=#<Redis client v4.2.5 for redis://localhost:6379/1>, @graph_name="movies", @module_version=999999>
|
28
29
|
|
29
30
|
Create a couple nodes:
|
30
31
|
|
31
|
-
actor = Redgraph::Node.new(label: 'actor',
|
32
|
+
actor = Redgraph::Node.new(label: 'actor', properties: {name: "Al Pacino"})
|
33
|
+
=> #<Redgraph::Node:0x00007f8d5f95cf88 @label="actor", @properties={:name=>"Al Pacino"}>
|
32
34
|
graph.add_node(actor)
|
33
|
-
|
35
|
+
=> #<Redgraph::Node:0x00007f8d5f95cf88 @id=0, @label="actor", @properties={:name=>"Al Pacino"}>
|
36
|
+
film = Redgraph::Node.new(label: 'film', properties: {name: "Scarface"})
|
37
|
+
=> #<Redgraph::Node:0x00007f8d5f85ccc8 @label="film", @properties={:name=>"Scarface"}>
|
34
38
|
graph.add_node(film)
|
39
|
+
=> #<Redgraph::Node:0x00007f8d5f85ccc8 @id=1, @label="film", @properties={:name=>"Scarface"}>
|
35
40
|
|
36
41
|
Create an edge between those nodes:
|
37
42
|
|
38
43
|
edge = Redgraph::Edge.new(src: actor, dest: film, type: 'ACTOR_IN', properties: {role: "Tony Montana"})
|
39
|
-
|
44
|
+
=> #<Redgraph::Edge:0x00007f8d5f9ae3d8 @dest=#<Redgraph::Node:0x00007f8d5f85ccc8 @id=1, @label="film", @properties={:name=>"Scarface"}>, @dest_id=1, @properties={:role=>"Tony Montana"}, @src=#<Redgraph::Node:0x00007f8d5f95cf88 @id=0, @label="actor", @properties={:name=>"Al Pacino"}>, @src_id=0, @type="ACTOR_IN">
|
45
|
+
@graph.add_edge(edge)
|
46
|
+
=> #<Redgraph::Edge:0x00007f8d5f9ae3d8 @dest=#<Redgraph::Node:0x00007f8d5f85ccc8 @id=1, @label="film", @properties={:name=>"Scarface"}>, @dest_id=1, @id=0, @properties={:role=>"Tony Montana"}, @src=#<Redgraph::Node:0x00007f8d5f95cf88 @id=0, @label="actor", @properties={:name=>"Al Pacino"}>, @src_id=0, @type="ACTOR_IN">
|
40
47
|
|
41
48
|
Find a node by id:
|
42
49
|
|
43
|
-
@graph.find_node_by_id(
|
50
|
+
@graph.find_node_by_id(1)
|
51
|
+
=> #<Redgraph::Node:0x00007f8d5c2c6e88 @id=1, @label="film", @properties={"name"=>"Scarface"}>
|
44
52
|
|
45
53
|
To get all nodes:
|
46
54
|
|
47
55
|
@graph.nodes
|
56
|
+
=> [#<Redgraph::Node:0x00007f8d5c2ee0a0 @id=0, @label="actor", @properties={"name"=>"Al Pacino"}>, #<Redgraph::Node:0x00007f8d5c2edfd8 @id=1, @label="film", @properties={"name"=>"Scarface"}>]
|
48
57
|
|
49
58
|
Optional filters that can be combined:
|
50
59
|
|
@@ -55,6 +64,7 @@ Optional filters that can be combined:
|
|
55
64
|
Counting nodes
|
56
65
|
|
57
66
|
@graph.count_nodes(label: 'actor')
|
67
|
+
=> 1
|
58
68
|
|
59
69
|
Getting edges:
|
60
70
|
|
@@ -62,6 +72,11 @@ Getting edges:
|
|
62
72
|
@graph.edges(src: actor, dest: film)
|
63
73
|
@graph.edges(kind: 'FRIEND_OF', limit: 10, skip: 20)
|
64
74
|
|
75
|
+
Running custom queries
|
76
|
+
|
77
|
+
@graph.query("MATCH (src)-[edge:FRIEND_OF]->(dest) RETURN src, edge")
|
78
|
+
|
79
|
+
|
65
80
|
## Development
|
66
81
|
|
67
82
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
data/lib/redgraph.rb
CHANGED
@@ -10,4 +10,9 @@ require_relative "redgraph/query_response"
|
|
10
10
|
module Redgraph
|
11
11
|
class Error < StandardError; end
|
12
12
|
class ServerError < Error; end
|
13
|
+
class MissingAliasPrefixError < Error
|
14
|
+
def message
|
15
|
+
"The order clause requires the node/edge alias prefix, ie order('node.foo') instead order('foo')"
|
16
|
+
end
|
17
|
+
end
|
13
18
|
end
|
data/lib/redgraph/edge.rb
CHANGED
@@ -2,11 +2,13 @@
|
|
2
2
|
|
3
3
|
module Redgraph
|
4
4
|
class Edge
|
5
|
-
attr_accessor :id, :src, :dest, :type, :properties
|
5
|
+
attr_accessor :id, :src, :dest, :src_id, :dest_id, :type, :properties
|
6
6
|
|
7
7
|
def initialize(src: nil, dest: nil, type: nil, properties: {})
|
8
8
|
@src = src
|
9
|
+
@src_id = @src.id if @src
|
9
10
|
@dest = dest
|
11
|
+
@dest_id = @dest.id if @dest
|
10
12
|
@type = type
|
11
13
|
@properties = properties
|
12
14
|
end
|
data/lib/redgraph/graph.rb
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "graph/node_methods"
|
4
|
+
require_relative "graph/edge_methods"
|
5
|
+
|
3
6
|
module Redgraph
|
4
7
|
class Graph
|
8
|
+
include NodeMethods
|
9
|
+
include EdgeMethods
|
10
|
+
|
5
11
|
attr_accessor :connection, :graph_name
|
6
12
|
|
7
13
|
def initialize(graph, redis_options = {})
|
@@ -38,140 +44,49 @@ module Redgraph
|
|
38
44
|
# Returns an array of existing labels
|
39
45
|
#
|
40
46
|
def labels
|
41
|
-
result =
|
47
|
+
result = _query("CALL db.labels()")
|
42
48
|
result.resultset.map(&:values).flatten
|
43
49
|
end
|
44
50
|
|
45
51
|
# Returns an array of existing properties
|
46
52
|
#
|
47
53
|
def properties
|
48
|
-
result =
|
54
|
+
result = _query("CALL db.propertyKeys()")
|
49
55
|
result.resultset.map(&:values).flatten
|
50
56
|
end
|
51
57
|
|
52
58
|
# Returns an array of existing relationship types
|
53
59
|
#
|
54
60
|
def relationship_types
|
55
|
-
result =
|
61
|
+
result = _query("CALL db.relationshipTypes()")
|
56
62
|
result.resultset.map(&:values).flatten
|
57
63
|
end
|
58
64
|
|
59
|
-
#
|
60
|
-
|
61
|
-
|
62
|
-
result = query("CREATE (n:`#{node.label}` #{quote_hash(node.properties)}) RETURN ID(n)")
|
63
|
-
return false if result.stats[:nodes_created] != 1
|
64
|
-
id = result.resultset.first["ID(n)"]
|
65
|
-
node.id = id
|
66
|
-
node
|
67
|
-
end
|
68
|
-
|
69
|
-
def find_node_by_id(id)
|
70
|
-
result = query("MATCH (node) WHERE ID(node) = #{id} RETURN node")
|
71
|
-
return nil if result.resultset.empty?
|
72
|
-
(node_id, labels, properties) = result.resultset.first["node"]
|
73
|
-
attrs = {}
|
74
|
-
|
75
|
-
properties.each do |(index, type, value)|
|
76
|
-
attrs[get_property(index)] = value
|
77
|
-
end
|
78
|
-
Node.new(label: get_label(labels.first), properties: attrs).tap do |node|
|
79
|
-
node.id = node_id
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
# Returns nodes. Options:
|
84
|
-
#
|
85
|
-
# - label: filter by label
|
86
|
-
# - properties: filter by properties
|
87
|
-
# - limit: number of items
|
88
|
-
# - skip: items offset (useful for pagination)
|
89
|
-
#
|
90
|
-
def nodes(label: nil, properties: nil, limit: nil, skip: nil)
|
91
|
-
_label = ":`#{label}`" if label
|
92
|
-
_props = quote_hash(properties) if properties
|
93
|
-
_limit = "LIMIT #{limit}" if limit
|
94
|
-
_skip = "SKIP #{skip}" if skip
|
95
|
-
|
96
|
-
cmd = "MATCH (node#{_label} #{_props}) RETURN node #{_skip} #{_limit}"
|
97
|
-
result = query(cmd)
|
98
|
-
|
99
|
-
result.resultset.map do |item|
|
100
|
-
node_from_resultset_item(item["node"])
|
101
|
-
end
|
65
|
+
# You can run custom cypher queries
|
66
|
+
def query(cmd)
|
67
|
+
_query(cmd).rows
|
102
68
|
end
|
103
69
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
# - properties: filter by properties
|
108
|
-
#
|
109
|
-
def count_nodes(label: nil, properties: nil)
|
110
|
-
_label = ":`#{label}`" if label
|
111
|
-
_props = quote_hash(properties) if properties
|
112
|
-
|
113
|
-
cmd = "MATCH (node#{_label} #{_props}) RETURN COUNT(node)"
|
114
|
-
result = query(cmd)
|
115
|
-
|
116
|
-
result.resultset.first["COUNT(node)"]
|
70
|
+
def get_label(id)
|
71
|
+
@labels ||= labels
|
72
|
+
@labels[id] || (@labels = labels)[id]
|
117
73
|
end
|
118
74
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
result = query("MATCH (src), (dest)
|
123
|
-
WHERE ID(src) = #{edge.src.id} AND ID(dest) = #{edge.dest.id}
|
124
|
-
CREATE (src)-[e:`#{edge.type}` #{quote_hash(edge.properties)}]->(dest) RETURN ID(e)")
|
125
|
-
return false if result.stats[:relationships_created] != 1
|
126
|
-
id = result.resultset.first["ID(e)"]
|
127
|
-
edge.id = id
|
128
|
-
edge
|
75
|
+
def get_property(id)
|
76
|
+
@properties ||= properties
|
77
|
+
@properties[id] || (@properties = properties)[id]
|
129
78
|
end
|
130
79
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
# - src
|
135
|
-
# - dest
|
136
|
-
# - properties
|
137
|
-
# - limit
|
138
|
-
# - skip
|
139
|
-
#
|
140
|
-
def edges(type: nil, src: nil, dest: nil, properties: nil, limit: nil, skip: nil)
|
141
|
-
_type = ":`#{type}`" if type
|
142
|
-
_props = quote_hash(properties) if properties
|
143
|
-
_limit = "LIMIT #{limit}" if limit
|
144
|
-
_skip = "SKIP #{skip}" if skip
|
145
|
-
|
146
|
-
_where = if src || dest
|
147
|
-
clauses = [
|
148
|
-
("ID(src) = #{src.id}" if src),
|
149
|
-
("ID(dest) = #{dest.id}" if dest)
|
150
|
-
].compact.join(" AND ")
|
151
|
-
"WHERE #{clauses}"
|
152
|
-
end
|
153
|
-
|
154
|
-
cmd = "MATCH (src)-[edge#{_type} #{_props}]->(dest) #{_where}
|
155
|
-
RETURN src, edge, dest #{_skip} #{_limit}"
|
156
|
-
result = query(cmd)
|
157
|
-
|
158
|
-
result.resultset.map do |item|
|
159
|
-
src = node_from_resultset_item(item["src"])
|
160
|
-
dest = node_from_resultset_item(item["dest"])
|
161
|
-
edge = edge_from_resultset_item(item["edge"])
|
162
|
-
|
163
|
-
edge.src = src
|
164
|
-
edge.dest = dest
|
165
|
-
|
166
|
-
edge
|
167
|
-
end
|
80
|
+
def get_relationship_type(id)
|
81
|
+
@relationship_types ||= relationship_types
|
82
|
+
@relationship_types[id] || (@relationship_types = relationship_types)[id]
|
168
83
|
end
|
169
84
|
|
170
85
|
private
|
171
86
|
|
172
|
-
def
|
87
|
+
def _query(cmd)
|
173
88
|
data = @connection.call("GRAPH.QUERY", graph_name, cmd, "--compact")
|
174
|
-
QueryResponse.new(data)
|
89
|
+
QueryResponse.new(data, self)
|
175
90
|
end
|
176
91
|
|
177
92
|
def quote_hash(hash)
|
@@ -189,48 +104,5 @@ module Redgraph
|
|
189
104
|
end
|
190
105
|
end
|
191
106
|
|
192
|
-
def get_label(id)
|
193
|
-
@labels ||= labels
|
194
|
-
@labels[id] || (@labels = labels)[id]
|
195
|
-
end
|
196
|
-
|
197
|
-
def get_property(id)
|
198
|
-
@properties ||= properties
|
199
|
-
@properties[id] || (@properties = properties)[id]
|
200
|
-
end
|
201
|
-
|
202
|
-
def get_relationship_type(id)
|
203
|
-
@relationship_types ||= relationship_types
|
204
|
-
@relationship_types[id] || (@relationship_types = relationship_types)[id]
|
205
|
-
end
|
206
|
-
|
207
|
-
# Builds a Node object from the raw data
|
208
|
-
#
|
209
|
-
def node_from_resultset_item(item)
|
210
|
-
(node_id, labels, props) = item
|
211
|
-
attrs = {}
|
212
|
-
|
213
|
-
props.each do |(index, type, value)|
|
214
|
-
attrs[get_property(index)] = value
|
215
|
-
end
|
216
|
-
Node.new(label: get_label(labels.first), properties: attrs).tap do |node|
|
217
|
-
node.id = node_id
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
|
-
def edge_from_resultset_item(item)
|
222
|
-
(edge_id, type_id, _src_id, _dest_id, props) = item
|
223
|
-
attrs = {}
|
224
|
-
|
225
|
-
props.each do |(index, type, value)|
|
226
|
-
attrs[get_property(index)] = value
|
227
|
-
end
|
228
|
-
|
229
|
-
Edge.new.tap do |edge|
|
230
|
-
edge.id = edge_id
|
231
|
-
edge.type = get_relationship_type(type_id)
|
232
|
-
edge.properties = attrs
|
233
|
-
end
|
234
|
-
end
|
235
107
|
end
|
236
108
|
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Redgraph
|
2
|
+
class Graph
|
3
|
+
module EdgeMethods
|
4
|
+
# Adds an edge. If successul it returns the created object, otherwise false
|
5
|
+
#
|
6
|
+
def add_edge(edge)
|
7
|
+
result = _query("MATCH (src), (dest)
|
8
|
+
WHERE ID(src) = #{edge.src.id} AND ID(dest) = #{edge.dest.id}
|
9
|
+
CREATE (src)-[e:`#{edge.type}` #{quote_hash(edge.properties)}]->(dest) RETURN ID(e)")
|
10
|
+
return false if result.stats[:relationships_created] != 1
|
11
|
+
id = result.resultset.first["ID(e)"]
|
12
|
+
edge.id = id
|
13
|
+
edge
|
14
|
+
end
|
15
|
+
|
16
|
+
# Finds edges. Options:
|
17
|
+
#
|
18
|
+
# - type
|
19
|
+
# - src
|
20
|
+
# - dest
|
21
|
+
# - properties
|
22
|
+
# - order
|
23
|
+
# - limit
|
24
|
+
# - skip
|
25
|
+
#
|
26
|
+
def edges(type: nil, src: nil, dest: nil, properties: nil, order: nil, limit: nil, skip: nil)
|
27
|
+
_type = ":`#{type}`" if type
|
28
|
+
_props = quote_hash(properties) if properties
|
29
|
+
_order = if order
|
30
|
+
raise MissingAliasPrefixError unless order.include?("edge.")
|
31
|
+
"ORDER BY #{order}"
|
32
|
+
end
|
33
|
+
_limit = "LIMIT #{limit}" if limit
|
34
|
+
_skip = "SKIP #{skip}" if skip
|
35
|
+
|
36
|
+
_where = if src || dest
|
37
|
+
clauses = [
|
38
|
+
("ID(src) = #{src.id}" if src),
|
39
|
+
("ID(dest) = #{dest.id}" if dest)
|
40
|
+
].compact.join(" AND ")
|
41
|
+
"WHERE #{clauses}"
|
42
|
+
end
|
43
|
+
|
44
|
+
cmd = "MATCH (src)-[edge#{_type} #{_props}]->(dest) #{_where}
|
45
|
+
RETURN src, edge, dest #{_order} #{_skip} #{_limit}"
|
46
|
+
result = _query(cmd)
|
47
|
+
|
48
|
+
result.resultset.map do |item|
|
49
|
+
src = node_from_resultset_item(item["src"])
|
50
|
+
dest = node_from_resultset_item(item["dest"])
|
51
|
+
edge = edge_from_resultset_item(item["edge"])
|
52
|
+
|
53
|
+
edge.src = src
|
54
|
+
edge.dest = dest
|
55
|
+
|
56
|
+
edge
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def edge_from_resultset_item(item)
|
63
|
+
(edge_id, type_id, _src_id, _dest_id, props) = item
|
64
|
+
attrs = {}
|
65
|
+
|
66
|
+
props.each do |(index, type, value)|
|
67
|
+
attrs[get_property(index)] = value
|
68
|
+
end
|
69
|
+
|
70
|
+
Edge.new.tap do |edge|
|
71
|
+
edge.id = edge_id
|
72
|
+
edge.type = get_relationship_type(type_id)
|
73
|
+
edge.properties = attrs
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module Redgraph
|
2
|
+
class Graph
|
3
|
+
module NodeMethods
|
4
|
+
# Adds a node. If successul it returns the created object, otherwise false
|
5
|
+
#
|
6
|
+
def add_node(node)
|
7
|
+
result = _query("CREATE (n:`#{node.label}` #{quote_hash(node.properties)}) RETURN ID(n)")
|
8
|
+
return false if result.stats[:nodes_created] != 1
|
9
|
+
id = result.resultset.first["ID(n)"]
|
10
|
+
node.id = id
|
11
|
+
node
|
12
|
+
end
|
13
|
+
|
14
|
+
def find_node_by_id(id)
|
15
|
+
result = _query("MATCH (node) WHERE ID(node) = #{id} RETURN node")
|
16
|
+
return nil if result.resultset.empty?
|
17
|
+
(node_id, labels, properties) = result.resultset.first["node"]
|
18
|
+
attrs = {}
|
19
|
+
|
20
|
+
properties.each do |(index, type, value)|
|
21
|
+
attrs[get_property(index)] = value
|
22
|
+
end
|
23
|
+
Node.new(label: get_label(labels.first), properties: attrs).tap do |node|
|
24
|
+
node.id = node_id
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
# Returns nodes. Options:
|
30
|
+
#
|
31
|
+
# - label: filter by label
|
32
|
+
# - properties: filter by properties
|
33
|
+
# - order: node.name ASC, node.year DESC
|
34
|
+
# - limit: number of items
|
35
|
+
# - skip: items offset (useful for pagination)
|
36
|
+
#
|
37
|
+
def nodes(label: nil, properties: nil, order: nil, limit: nil, skip: nil)
|
38
|
+
_label = ":`#{label}`" if label
|
39
|
+
_props = quote_hash(properties) if properties
|
40
|
+
_order = if order
|
41
|
+
raise MissingAliasPrefixError unless order.include?("node.")
|
42
|
+
"ORDER BY #{order}"
|
43
|
+
end
|
44
|
+
_limit = "LIMIT #{limit}" if limit
|
45
|
+
_skip = "SKIP #{skip}" if skip
|
46
|
+
|
47
|
+
cmd = "MATCH (node#{_label} #{_props}) RETURN node #{_order} #{_skip} #{_limit}"
|
48
|
+
|
49
|
+
result = _query(cmd)
|
50
|
+
|
51
|
+
result.resultset.map do |item|
|
52
|
+
node_from_resultset_item(item["node"])
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Counts nodes. Options:
|
57
|
+
#
|
58
|
+
# - label: filter by label
|
59
|
+
# - properties: filter by properties
|
60
|
+
#
|
61
|
+
def count_nodes(label: nil, properties: nil)
|
62
|
+
_label = ":`#{label}`" if label
|
63
|
+
_props = quote_hash(properties) if properties
|
64
|
+
|
65
|
+
cmd = "MATCH (node#{_label} #{_props}) RETURN COUNT(node)"
|
66
|
+
query(cmd).flatten[0]
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
# Builds a Node object from the raw data
|
72
|
+
#
|
73
|
+
def node_from_resultset_item(item)
|
74
|
+
(node_id, labels, props) = item
|
75
|
+
attrs = {}
|
76
|
+
|
77
|
+
props.each do |(index, type, value)|
|
78
|
+
attrs[get_property(index)] = value
|
79
|
+
end
|
80
|
+
Node.new(label: get_label(labels.first), properties: attrs).tap do |node|
|
81
|
+
node.id = node_id
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -10,8 +10,28 @@ module Redgraph
|
|
10
10
|
# - query stats
|
11
11
|
#
|
12
12
|
class QueryResponse
|
13
|
-
|
13
|
+
TYPES = [
|
14
|
+
UNKNOWN = 0,
|
15
|
+
NULL = 1,
|
16
|
+
STRING = 2,
|
17
|
+
INTEGER = 3,
|
18
|
+
BOOLEAN = 4,
|
19
|
+
DOUBLE = 5,
|
20
|
+
ARRAY = 6,
|
21
|
+
EDGE = 7,
|
22
|
+
NODE = 8,
|
23
|
+
PATH = 9,
|
24
|
+
MAP = 10,
|
25
|
+
POINT = 11
|
26
|
+
].freeze
|
27
|
+
|
28
|
+
def initialize(response, graph)
|
14
29
|
@response = response
|
30
|
+
@graph = graph
|
31
|
+
|
32
|
+
@header_row = @response[0]
|
33
|
+
@result_rows = @response[1]
|
34
|
+
@query_statistics = @response[2]
|
15
35
|
end
|
16
36
|
|
17
37
|
def stats
|
@@ -26,19 +46,76 @@ module Redgraph
|
|
26
46
|
@resultset ||= parse_resultset
|
27
47
|
end
|
28
48
|
|
49
|
+
# Wraps in custom datatypes if needed
|
50
|
+
#
|
51
|
+
def rows
|
52
|
+
@result_rows.map do |column|
|
53
|
+
column.map do |data|
|
54
|
+
reify_column_item(data)
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
29
60
|
private
|
30
61
|
|
62
|
+
def reify_column_item(data)
|
63
|
+
value_type, value = data
|
64
|
+
|
65
|
+
case value_type
|
66
|
+
when STRING, INTEGER, BOOLEAN, DOUBLE then value
|
67
|
+
when NODE then reify_node_item(value)
|
68
|
+
when EDGE then reify_edge_item(value)
|
69
|
+
else
|
70
|
+
"other"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def reify_node_item(data)
|
75
|
+
(node_id, labels, props) = data
|
76
|
+
|
77
|
+
label = @graph.get_label(labels[0]) # Only one label is currently supported
|
78
|
+
|
79
|
+
node = Node.new(label: label)
|
80
|
+
node.id = node_id
|
81
|
+
|
82
|
+
props.each do |(prop_id, prop_type, prop_value)|
|
83
|
+
prop_name = @graph.get_property(prop_id)
|
84
|
+
node.properties[prop_name] = prop_value
|
85
|
+
end
|
86
|
+
|
87
|
+
node
|
88
|
+
end
|
89
|
+
|
90
|
+
def reify_edge_item(data)
|
91
|
+
(edge_id, type_id, src_id, dest_id, props) = data
|
92
|
+
|
93
|
+
type = @graph.get_relationship_type(type_id)
|
94
|
+
|
95
|
+
edge = Edge.new(type: type)
|
96
|
+
edge.id = edge_id
|
97
|
+
edge.src_id = src_id
|
98
|
+
edge.dest_id = dest_id
|
99
|
+
|
100
|
+
props.each do |(prop_id, prop_type, prop_value)|
|
101
|
+
prop_name = @graph.get_property(prop_id)
|
102
|
+
edge.properties[prop_name] = prop_value
|
103
|
+
end
|
104
|
+
|
105
|
+
edge
|
106
|
+
end
|
107
|
+
|
31
108
|
# The header lists the entities described in the RETURN clause. It is an
|
32
109
|
# array of [ColumnType (enum), name (string)] elements. We can ignore the
|
33
110
|
# enum, it is always 1 (COLUMN_SCALAR).
|
34
111
|
def parse_header
|
35
|
-
@
|
112
|
+
@header_row.map{|item| item[1]}
|
36
113
|
end
|
37
114
|
|
38
115
|
def parse_stats
|
39
116
|
stats = {}
|
40
117
|
|
41
|
-
@
|
118
|
+
@query_statistics.each do |item|
|
42
119
|
label, value = item.split(":")
|
43
120
|
|
44
121
|
case label
|
@@ -58,7 +135,7 @@ module Redgraph
|
|
58
135
|
|
59
136
|
# The resultset has one element per entity (as described by the header)
|
60
137
|
def parse_resultset
|
61
|
-
@
|
138
|
+
@result_rows.map do |item|
|
62
139
|
out = {}
|
63
140
|
|
64
141
|
item.each.with_index do |(type, value), i|
|
data/lib/redgraph/version.rb
CHANGED
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "test_helper"
|
4
|
+
|
5
|
+
class GraphEdgeMethodsTest < Minitest::Test
|
6
|
+
def setup
|
7
|
+
@graph = Redgraph::Graph.new("movies", url: $REDIS_URL)
|
8
|
+
|
9
|
+
@al = quick_add_node(label: 'actor', properties: {name: "Al Pacino"})
|
10
|
+
@john = quick_add_node(label: 'actor', properties: {name: "John Travolta"})
|
11
|
+
end
|
12
|
+
|
13
|
+
def teardown
|
14
|
+
@graph.delete
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_find_edge
|
18
|
+
quick_add_edge(type: 'FRIEND_OF', src: @al, dest: @john, properties: {since: 1980})
|
19
|
+
edge = @graph.edges.first
|
20
|
+
|
21
|
+
assert_equal('FRIEND_OF', edge.type)
|
22
|
+
assert_equal(1980, edge.properties["since"])
|
23
|
+
assert_equal(@al, edge.src)
|
24
|
+
assert_equal(@john, edge.dest)
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_find_all_edges
|
29
|
+
marlon = quick_add_node(label: 'actor', properties: {name: "Marlon Brando"})
|
30
|
+
film = quick_add_node(label: 'film', properties: {name: "The Godfather"})
|
31
|
+
quick_add_edge(type: 'ACTOR_IN', src: marlon, dest: film, properties: {role: 'Don Vito'})
|
32
|
+
quick_add_edge(type: 'ACTOR_IN', src: @al, dest: film, properties: {role: 'Michael'})
|
33
|
+
|
34
|
+
edges = @graph.edges
|
35
|
+
assert_equal(2, edges.size)
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_filter_edges
|
39
|
+
marlon = quick_add_node(label: 'actor', properties: {name: "Marlon Brando"})
|
40
|
+
film = quick_add_node(label: 'film', properties: {name: "The Godfather"})
|
41
|
+
other_film = quick_add_node(label: 'film', properties: {name: "Carlito's Way"})
|
42
|
+
e_donvito = quick_add_edge(type: 'ACTOR_IN', src: marlon, dest: film, properties: {role: 'Don Vito'})
|
43
|
+
e_michael = quick_add_edge(type: 'ACTOR_IN', src: @al, dest: film, properties: {role: 'Michael'})
|
44
|
+
e_carlito = quick_add_edge(type: 'ACTOR_IN', src: @al, dest: other_film, properties: {role: 'Carlito'})
|
45
|
+
quick_add_edge(type: 'FRIEND_OF', src: @al, dest: marlon, properties: {since: 1980})
|
46
|
+
|
47
|
+
edges = @graph.edges(type: "FRIEND_OF")
|
48
|
+
assert_equal(1, edges.size)
|
49
|
+
|
50
|
+
edges = @graph.edges(type: "ACTOR_IN")
|
51
|
+
assert_equal(3, edges.size)
|
52
|
+
|
53
|
+
edges = @graph.edges(type: "ACTOR_IN", limit: 2)
|
54
|
+
assert_equal(2, edges.size)
|
55
|
+
|
56
|
+
edges = @graph.edges(type: "ACTOR_IN", skip: 2, limit: 10)
|
57
|
+
assert_equal(1, edges.size)
|
58
|
+
|
59
|
+
edges = @graph.edges(properties: {role: "Carlito"})
|
60
|
+
assert_equal([e_carlito], edges)
|
61
|
+
|
62
|
+
edges = @graph.edges(src: marlon)
|
63
|
+
assert_equal([e_donvito], edges)
|
64
|
+
|
65
|
+
edges = @graph.edges(type: 'ACTOR_IN', dest: film)
|
66
|
+
assert_equal(2, edges.size)
|
67
|
+
assert_includes(edges, e_donvito)
|
68
|
+
assert_includes(edges, e_michael)
|
69
|
+
|
70
|
+
edges = @graph.edges(src: @al, dest: marlon)
|
71
|
+
assert_equal(1, edges.size)
|
72
|
+
edge = edges[0]
|
73
|
+
assert_equal('FRIEND_OF', edge.type)
|
74
|
+
assert_equal(1980, edge.properties["since"])
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_order_edges
|
78
|
+
marlon = quick_add_node(label: 'actor', properties: {name: "Marlon Brando"})
|
79
|
+
|
80
|
+
e1 = quick_add_edge(type: 'FRIEND_OF', src: @al, dest: marlon, properties: {since: 1980})
|
81
|
+
e2 = quick_add_edge(type: 'FRIEND_OF', src: @al, dest: @john, properties: {since: 2000})
|
82
|
+
e3 = quick_add_edge(type: 'FRIEND_OF', src: marlon, dest: @john, properties: {since: 1990})
|
83
|
+
|
84
|
+
edges = @graph.edges(type: 'FRIEND_OF', order: "edge.since ASC")
|
85
|
+
assert_equal([e1, e3, e2], edges)
|
86
|
+
|
87
|
+
edges = @graph.edges(type: 'FRIEND_OF', order: "edge.since DESC")
|
88
|
+
assert_equal([e2, e3, e1], edges)
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def quick_add_node(label:, properties:)
|
94
|
+
@graph.add_node(Redgraph::Node.new(label: label, properties: properties))
|
95
|
+
end
|
96
|
+
|
97
|
+
def quick_add_edge(type:, src:, dest:, properties:)
|
98
|
+
@graph.add_edge(Redgraph::Edge.new(type: type, src: src, dest: dest, properties: properties))
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "test_helper"
|
4
|
+
|
5
|
+
class GraphNodeMethodsTest < Minitest::Test
|
6
|
+
def setup
|
7
|
+
@graph = Redgraph::Graph.new("movies", url: $REDIS_URL)
|
8
|
+
|
9
|
+
@al = quick_add_node(label: 'actor', properties: {name: "Al Pacino"})
|
10
|
+
@john = quick_add_node(label: 'actor', properties: {name: "John Travolta"})
|
11
|
+
end
|
12
|
+
|
13
|
+
def teardown
|
14
|
+
@graph.delete
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_find_node_by_id
|
18
|
+
node = @graph.find_node_by_id(@al.id)
|
19
|
+
|
20
|
+
refute_nil(node)
|
21
|
+
assert_equal("actor", node.label)
|
22
|
+
assert_equal("Al Pacino", node.properties["name"])
|
23
|
+
assert_equal(@al.id, node.id)
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_find_node_by_wrong_id
|
27
|
+
node = @graph.find_node_by_id("-1")
|
28
|
+
|
29
|
+
assert_nil(node)
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_find_all_nodes
|
33
|
+
actors = @graph.nodes
|
34
|
+
|
35
|
+
assert_equal(2, actors.size)
|
36
|
+
assert_includes(actors, @al)
|
37
|
+
assert_includes(actors, @john)
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_find_all_nodes_by_label
|
41
|
+
film = quick_add_node(label: 'film', properties: {name: "Scarface"})
|
42
|
+
|
43
|
+
actors = @graph.nodes(label: 'actor')
|
44
|
+
assert_equal(2, actors.size)
|
45
|
+
assert_includes(actors, @al)
|
46
|
+
assert_includes(actors, @john)
|
47
|
+
|
48
|
+
films = @graph.nodes(label: 'film')
|
49
|
+
assert_equal(1, films.size)
|
50
|
+
assert_includes(films, film)
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_find_all_nodes_by_property
|
54
|
+
scarface = quick_add_node(label: 'film', properties: {name: "Scarface", genre: "drama"})
|
55
|
+
casino = quick_add_node(label: 'film', properties: {name: "Casino", genre: "drama"})
|
56
|
+
_mamma_mia = quick_add_node(label: 'film', properties: {name: "Mamma Mia", genre: "musical"})
|
57
|
+
|
58
|
+
dramas = @graph.nodes(properties: {genre: "drama"})
|
59
|
+
|
60
|
+
assert_equal(2, dramas.size)
|
61
|
+
assert_includes(dramas, scarface)
|
62
|
+
assert_includes(dramas, casino)
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_order_nodes_by_property
|
66
|
+
scarface = quick_add_node(label: 'film', properties: {name: "Scarface", genre: "drama"})
|
67
|
+
casino = quick_add_node(label: 'film', properties: {name: "Casino", genre: "drama"})
|
68
|
+
mamma_mia = quick_add_node(label: 'film', properties: {name: "Mamma Mia", genre: "musical"})
|
69
|
+
|
70
|
+
items = @graph.nodes(label: 'film', order: "node.name")
|
71
|
+
assert_equal([casino, mamma_mia, scarface], items)
|
72
|
+
|
73
|
+
items = @graph.nodes(label: 'film', order: "node.name ASC")
|
74
|
+
assert_equal([casino, mamma_mia, scarface], items)
|
75
|
+
|
76
|
+
items = @graph.nodes(label: 'film', order: "node.name DESC")
|
77
|
+
assert_equal([scarface, mamma_mia, casino], items)
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_count_nodes
|
81
|
+
quick_add_node(label: 'film', properties: {name: "Scarface", genre: "drama"})
|
82
|
+
quick_add_node(label: 'film', properties: {name: "Casino", genre: "drama"})
|
83
|
+
quick_add_node(label: 'film', properties: {name: "Mamma Mia", genre: "musical"})
|
84
|
+
|
85
|
+
|
86
|
+
assert_equal(5, @graph.count_nodes)
|
87
|
+
assert_equal(3, @graph.count_nodes(label: 'film'))
|
88
|
+
assert_equal(2, @graph.count_nodes(properties: {genre: "drama"}))
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_limit_nodes
|
92
|
+
10.times do |i|
|
93
|
+
quick_add_node(label: 'token', properties: {number: i})
|
94
|
+
end
|
95
|
+
|
96
|
+
items = @graph.nodes(label: 'token', limit: 5)
|
97
|
+
assert_equal(5, items.size)
|
98
|
+
assert_equal([0,1,2,3,4], items.map{|item| item.properties["number"]})
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_skip_nodes
|
102
|
+
10.times do |i|
|
103
|
+
quick_add_node(label: 'token', properties: {number: i})
|
104
|
+
end
|
105
|
+
|
106
|
+
items = @graph.nodes(label: 'token', limit: 3, skip: 3)
|
107
|
+
assert_equal(3, items.size)
|
108
|
+
assert_equal([3,4,5], items.map{|item| item.properties["number"]})
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def quick_add_node(label:, properties:)
|
114
|
+
@graph.add_node(Redgraph::Node.new(label: label, properties: properties))
|
115
|
+
end
|
116
|
+
end
|
data/test/graph_queries_test.rb
CHANGED
@@ -6,151 +6,39 @@ class GraphQueriesTest < Minitest::Test
|
|
6
6
|
def setup
|
7
7
|
@graph = Redgraph::Graph.new("movies", url: $REDIS_URL)
|
8
8
|
|
9
|
-
@al = quick_add_node(label: 'actor', properties: {name: "Al Pacino"})
|
10
|
-
@john = quick_add_node(label: 'actor', properties: {name: "John Travolta"})
|
9
|
+
@al = quick_add_node(label: 'actor', properties: {name: "Al Pacino", born: 1940})
|
10
|
+
@john = quick_add_node(label: 'actor', properties: {name: "John Travolta", born: 1954})
|
11
11
|
end
|
12
12
|
|
13
13
|
def teardown
|
14
14
|
@graph.delete
|
15
15
|
end
|
16
16
|
|
17
|
-
def
|
18
|
-
|
19
|
-
|
20
|
-
refute_nil(node)
|
21
|
-
assert_equal("actor", node.label)
|
22
|
-
assert_equal("Al Pacino", node.properties["name"])
|
23
|
-
assert_equal(@al.id, node.id)
|
24
|
-
end
|
25
|
-
|
26
|
-
def test_find_node_by_wrong_id
|
27
|
-
node = @graph.find_node_by_id("-1")
|
28
|
-
|
29
|
-
assert_nil(node)
|
30
|
-
end
|
31
|
-
|
32
|
-
def test_find_all_nodes
|
33
|
-
actors = @graph.nodes
|
34
|
-
|
35
|
-
assert_equal(2, actors.size)
|
36
|
-
assert_includes(actors, @al)
|
37
|
-
assert_includes(actors, @john)
|
38
|
-
end
|
39
|
-
|
40
|
-
def test_find_all_nodes_by_label
|
41
|
-
film = quick_add_node(label: 'film', properties: {name: "Scarface"})
|
42
|
-
|
43
|
-
actors = @graph.nodes(label: 'actor')
|
44
|
-
assert_equal(2, actors.size)
|
45
|
-
assert_includes(actors, @al)
|
46
|
-
assert_includes(actors, @john)
|
47
|
-
|
48
|
-
films = @graph.nodes(label: 'film')
|
49
|
-
assert_equal(1, films.size)
|
50
|
-
assert_includes(films, film)
|
51
|
-
end
|
52
|
-
|
53
|
-
def test_find_all_nodes_by_property
|
54
|
-
scarface = quick_add_node(label: 'film', properties: {name: "Scarface", genre: "drama"})
|
55
|
-
casino = quick_add_node(label: 'film', properties: {name: "Casino", genre: "drama"})
|
56
|
-
mamma_mia = quick_add_node(label: 'film', properties: {name: "Mamma Mia", genre: "musical"})
|
57
|
-
|
58
|
-
dramas = @graph.nodes(properties: {genre: "drama"})
|
59
|
-
|
60
|
-
assert_equal(2, dramas.size)
|
61
|
-
assert_includes(dramas, scarface)
|
62
|
-
assert_includes(dramas, casino)
|
63
|
-
end
|
64
|
-
|
65
|
-
def test_count_nodes
|
66
|
-
quick_add_node(label: 'film', properties: {name: "Scarface", genre: "drama"})
|
67
|
-
quick_add_node(label: 'film', properties: {name: "Casino", genre: "drama"})
|
68
|
-
quick_add_node(label: 'film', properties: {name: "Mamma Mia", genre: "musical"})
|
69
|
-
|
70
|
-
|
71
|
-
assert_equal(5, @graph.count_nodes)
|
72
|
-
assert_equal(3, @graph.count_nodes(label: 'film'))
|
73
|
-
assert_equal(2, @graph.count_nodes(properties: {genre: "drama"}))
|
17
|
+
def test_query_string_attribute
|
18
|
+
result = @graph.query("MATCH (n) RETURN n.name ORDER BY n.name")
|
19
|
+
assert_equal([["Al Pacino"], ["John Travolta"]], result)
|
74
20
|
end
|
75
21
|
|
76
|
-
def
|
77
|
-
|
78
|
-
|
79
|
-
end
|
80
|
-
|
81
|
-
items = @graph.nodes(label: 'token', limit: 5)
|
82
|
-
assert_equal(5, items.size)
|
83
|
-
assert_equal([0,1,2,3,4], items.map{|item| item.properties["number"]})
|
22
|
+
def test_query_string_and_number_attributes
|
23
|
+
result = @graph.query("MATCH (n) RETURN n.name, n.born ORDER BY n.born")
|
24
|
+
assert_equal([["Al Pacino", 1940], ["John Travolta", 1954]], result)
|
84
25
|
end
|
85
26
|
|
86
|
-
def
|
87
|
-
|
88
|
-
|
89
|
-
end
|
90
|
-
|
91
|
-
items = @graph.nodes(label: 'token', limit: 3, skip: 3)
|
92
|
-
assert_equal(3, items.size)
|
93
|
-
assert_equal([3,4,5], items.map{|item| item.properties["number"]})
|
27
|
+
def test_query_nodes
|
28
|
+
result = @graph.query("MATCH (n) RETURN n ORDER BY n.born")
|
29
|
+
assert_equal([[@al], [@john]], result)
|
94
30
|
end
|
95
31
|
|
96
|
-
def
|
97
|
-
quick_add_edge(type: 'FRIEND_OF', src: @al, dest: @john, properties: {since: 1980})
|
98
|
-
|
99
|
-
|
100
|
-
assert_equal('FRIEND_OF', edge.type)
|
101
|
-
assert_equal(1980, edge.properties["since"])
|
102
|
-
assert_equal(@al, edge.src)
|
103
|
-
assert_equal(@john, edge.dest)
|
104
|
-
|
105
|
-
end
|
106
|
-
|
107
|
-
def test_find_all_edges
|
108
|
-
marlon = quick_add_node(label: 'actor', properties: {name: "Marlon Brando"})
|
109
|
-
film = quick_add_node(label: 'film', properties: {name: "The Godfather"})
|
110
|
-
quick_add_edge(type: 'ACTOR_IN', src: marlon, dest: film, properties: {role: 'Don Vito'})
|
111
|
-
quick_add_edge(type: 'ACTOR_IN', src: @al, dest: film, properties: {role: 'Michael'})
|
112
|
-
|
113
|
-
edges = @graph.edges
|
114
|
-
assert_equal(2, edges.size)
|
32
|
+
def test_query_edge
|
33
|
+
edge = quick_add_edge(type: 'FRIEND_OF', src: @al, dest: @john, properties: {since: 1980})
|
34
|
+
result = @graph.query("MATCH (src)-[edge]->(dest) RETURN edge")
|
35
|
+
assert_equal([[edge]], result)
|
115
36
|
end
|
116
37
|
|
117
|
-
def
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
e_donvito = quick_add_edge(type: 'ACTOR_IN', src: marlon, dest: film, properties: {role: 'Don Vito'})
|
122
|
-
e_michael = quick_add_edge(type: 'ACTOR_IN', src: @al, dest: film, properties: {role: 'Michael'})
|
123
|
-
e_carlito = quick_add_edge(type: 'ACTOR_IN', src: @al, dest: other_film, properties: {role: 'Carlito'})
|
124
|
-
quick_add_edge(type: 'FRIEND_OF', src: @al, dest: marlon, properties: {since: 1980})
|
125
|
-
|
126
|
-
edges = @graph.edges(type: "FRIEND_OF")
|
127
|
-
assert_equal(1, edges.size)
|
128
|
-
|
129
|
-
edges = @graph.edges(type: "ACTOR_IN")
|
130
|
-
assert_equal(3, edges.size)
|
131
|
-
|
132
|
-
edges = @graph.edges(type: "ACTOR_IN", limit: 2)
|
133
|
-
assert_equal(2, edges.size)
|
134
|
-
|
135
|
-
edges = @graph.edges(type: "ACTOR_IN", skip: 2, limit: 10)
|
136
|
-
assert_equal(1, edges.size)
|
137
|
-
|
138
|
-
edges = @graph.edges(properties: {role: "Carlito"})
|
139
|
-
assert_equal([e_carlito], edges)
|
140
|
-
|
141
|
-
edges = @graph.edges(src: marlon)
|
142
|
-
assert_equal([e_donvito], edges)
|
143
|
-
|
144
|
-
edges = @graph.edges(type: 'ACTOR_IN', dest: film)
|
145
|
-
assert_equal(2, edges.size)
|
146
|
-
assert_includes(edges, e_donvito)
|
147
|
-
assert_includes(edges, e_michael)
|
148
|
-
|
149
|
-
edges = @graph.edges(src: @al, dest: marlon)
|
150
|
-
assert_equal(1, edges.size)
|
151
|
-
edge = edges[0]
|
152
|
-
assert_equal('FRIEND_OF', edge.type)
|
153
|
-
assert_equal(1980, edge.properties["since"])
|
38
|
+
def test_query_node_and_edge
|
39
|
+
edge = quick_add_edge(type: 'FRIEND_OF', src: @al, dest: @john, properties: {since: 1980})
|
40
|
+
result = @graph.query("MATCH (src)-[edge:FRIEND_OF]->(dest) RETURN src, edge")
|
41
|
+
assert_equal([[@al, edge]], result)
|
154
42
|
end
|
155
43
|
|
156
44
|
private
|
data/test/graph_test.rb
CHANGED
@@ -12,11 +12,13 @@ class GraphTest < Minitest::Test
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def test_list
|
15
|
+
skip unless graph_list_supported?
|
15
16
|
list = @graph.list
|
16
17
|
assert_includes(list, "foobar")
|
17
18
|
end
|
18
19
|
|
19
20
|
def test_delete
|
21
|
+
skip unless graph_list_supported?
|
20
22
|
assert_includes(@graph.list, "foobar")
|
21
23
|
|
22
24
|
@graph.delete
|
@@ -66,4 +68,15 @@ class GraphTest < Minitest::Test
|
|
66
68
|
)
|
67
69
|
graph
|
68
70
|
end
|
71
|
+
|
72
|
+
# This command is only supported in the latest version
|
73
|
+
def graph_list_supported?
|
74
|
+
@graph.list
|
75
|
+
rescue Redis::CommandError => e
|
76
|
+
if e.message =~ /ERR unknown command `GRAPH.LIST`/
|
77
|
+
false
|
78
|
+
else
|
79
|
+
true
|
80
|
+
end
|
81
|
+
end
|
69
82
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redgraph
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paolo Zaccagnini
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-04-
|
11
|
+
date: 2021-04-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis
|
@@ -44,12 +44,16 @@ files:
|
|
44
44
|
- lib/redgraph.rb
|
45
45
|
- lib/redgraph/edge.rb
|
46
46
|
- lib/redgraph/graph.rb
|
47
|
+
- lib/redgraph/graph/edge_methods.rb
|
48
|
+
- lib/redgraph/graph/node_methods.rb
|
47
49
|
- lib/redgraph/node.rb
|
48
50
|
- lib/redgraph/query_response.rb
|
49
51
|
- lib/redgraph/version.rb
|
50
52
|
- redgraph.gemspec
|
51
53
|
- test/graph_connection_test.rb
|
54
|
+
- test/graph_edge_methods_test.rb
|
52
55
|
- test/graph_manipulation_test.rb
|
56
|
+
- test/graph_node_methods_test.rb
|
53
57
|
- test/graph_queries_test.rb
|
54
58
|
- test/graph_test.rb
|
55
59
|
- test/redgraph_test.rb
|