redgraph 0.1.1 → 0.1.2
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/CHANGELOG.md +6 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +9 -1
- data/README.md +15 -4
- data/lib/redgraph/edge.rb +5 -1
- data/lib/redgraph/graph.rb +98 -10
- data/lib/redgraph/version.rb +1 -1
- data/redgraph.gemspec +1 -1
- data/test/graph_manipulation_test.rb +29 -0
- data/test/graph_queries_test.rb +75 -0
- data/test/graph_test.rb +13 -0
- data/test/test_helper.rb +9 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba5feef26cde1b47f2da53277c18c3c234ce12f85f315e761c865135b60857b4
|
4
|
+
data.tar.gz: 1cb82a481b616ee4347a52347405155da600c176696de4ccb3efea97076b196d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 60ee0fdcb153873a42acfd40534c76db63dae469e680ed949544f15301f51ed3027a87d2fa7c1583a781c7d8077ec3fec4baf62746e86f5937f56bb9355f6941
|
7
|
+
data.tar.gz: cfa1a6bdc454adb49bb6537d7e1b4662de0e5e68f71611045f9d11b3719531f37c883df6f7fd4aa41061bea464d1d78e4f7b559bc19bfa7aa14031d97d8f0bb2
|
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
redgraph (0.1.
|
4
|
+
redgraph (0.1.2)
|
5
5
|
redis (~> 4)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
10
|
coderay (1.1.3)
|
11
|
+
docile (1.3.5)
|
11
12
|
method_source (1.0.0)
|
12
13
|
minitest (5.14.4)
|
13
14
|
pry (0.14.0)
|
@@ -15,6 +16,12 @@ GEM
|
|
15
16
|
method_source (~> 1.0)
|
16
17
|
rake (13.0.3)
|
17
18
|
redis (4.2.5)
|
19
|
+
simplecov (0.21.2)
|
20
|
+
docile (~> 1.1)
|
21
|
+
simplecov-html (~> 0.11)
|
22
|
+
simplecov_json_formatter (~> 0.1)
|
23
|
+
simplecov-html (0.12.3)
|
24
|
+
simplecov_json_formatter (0.1.2)
|
18
25
|
|
19
26
|
PLATFORMS
|
20
27
|
x86_64-darwin-20
|
@@ -24,6 +31,7 @@ DEPENDENCIES
|
|
24
31
|
pry (~> 0.14.0)
|
25
32
|
rake (~> 13.0)
|
26
33
|
redgraph!
|
34
|
+
simplecov (~> 0.21.2)
|
27
35
|
|
28
36
|
BUNDLED WITH
|
29
37
|
2.2.15
|
data/README.md
CHANGED
@@ -20,6 +20,8 @@ Or install it yourself as:
|
|
20
20
|
|
21
21
|
## Usage
|
22
22
|
|
23
|
+
The gem assumes you have a recent version of [RedisGraph](https://oss.redislabs.com/redisgraph/) up and running.
|
24
|
+
|
23
25
|
Basic usage:
|
24
26
|
|
25
27
|
graph = Redgraph::Graph.new('movies', url: "redis://localhost:6379/1")
|
@@ -44,10 +46,21 @@ To get all nodes:
|
|
44
46
|
|
45
47
|
@graph.nodes
|
46
48
|
|
47
|
-
Optional filters:
|
49
|
+
Optional filters that can be combined:
|
48
50
|
|
49
51
|
@graph.nodes(label: 'actor')
|
50
52
|
@graph.nodes(properties: {name: "Al Pacino"})
|
53
|
+
@graph.nodes(limit: 10, skip: 20)
|
54
|
+
|
55
|
+
Counting nodes
|
56
|
+
|
57
|
+
@graph.count_nodes(label: 'actor')
|
58
|
+
|
59
|
+
Getting edges:
|
60
|
+
|
61
|
+
@graph.edges
|
62
|
+
@graph.edges(src: actor, dest: film)
|
63
|
+
@graph.edges(kind: 'FRIEND_OF', limit: 10, skip: 20)
|
51
64
|
|
52
65
|
## Development
|
53
66
|
|
@@ -55,7 +68,7 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
|
55
68
|
|
56
69
|
TEST_REDIS_URL=YOUR-REDIS-URL rake test
|
57
70
|
|
58
|
-
to run the tests.
|
71
|
+
to run the tests. Test coverage will be enabled if you set the `COVERAGE` environment variable to any value.
|
59
72
|
|
60
73
|
You can use a `TEST_REDIS_URL` such as `redis://localhost:6379/1`. Make sure you're not overwriting important databases.
|
61
74
|
|
@@ -63,8 +76,6 @@ You can also run `bin/console` for an interactive prompt that will allow you to
|
|
63
76
|
|
64
77
|
To install this gem onto your local machine, run `bundle exec rake install`.
|
65
78
|
|
66
|
-
Run `bin/console` for an interactive prompt.
|
67
|
-
|
68
79
|
## Contributing
|
69
80
|
|
70
81
|
Bug reports and pull requests are welcome on GitHub at https://github.com/pzac/redgraph.
|
data/lib/redgraph/edge.rb
CHANGED
@@ -4,7 +4,7 @@ module Redgraph
|
|
4
4
|
class Edge
|
5
5
|
attr_accessor :id, :src, :dest, :type, :properties
|
6
6
|
|
7
|
-
def initialize(src
|
7
|
+
def initialize(src: nil, dest: nil, type: nil, properties: {})
|
8
8
|
@src = src
|
9
9
|
@dest = dest
|
10
10
|
@type = type
|
@@ -14,5 +14,9 @@ module Redgraph
|
|
14
14
|
def persisted?
|
15
15
|
!id.nil?
|
16
16
|
end
|
17
|
+
|
18
|
+
def ==(other)
|
19
|
+
super || other.instance_of?(self.class) && !id.nil? && other.id == id
|
20
|
+
end
|
17
21
|
end
|
18
22
|
end
|
data/lib/redgraph/graph.rb
CHANGED
@@ -49,6 +49,13 @@ module Redgraph
|
|
49
49
|
result.resultset.map(&:values).flatten
|
50
50
|
end
|
51
51
|
|
52
|
+
# Returns an array of existing relationship types
|
53
|
+
#
|
54
|
+
def relationship_types
|
55
|
+
result = query("CALL db.relationshipTypes()")
|
56
|
+
result.resultset.map(&:values).flatten
|
57
|
+
end
|
58
|
+
|
52
59
|
# Adds a node. If successul it returns the created object, otherwise false
|
53
60
|
#
|
54
61
|
def add_node(node)
|
@@ -90,18 +97,25 @@ module Redgraph
|
|
90
97
|
result = query(cmd)
|
91
98
|
|
92
99
|
result.resultset.map do |item|
|
93
|
-
(
|
94
|
-
attrs = {}
|
95
|
-
|
96
|
-
props.each do |(index, type, value)|
|
97
|
-
attrs[get_property(index)] = value
|
98
|
-
end
|
99
|
-
Node.new(label: get_label(labels.first), properties: attrs).tap do |node|
|
100
|
-
node.id = node_id
|
101
|
-
end
|
100
|
+
node_from_resultset_item(item["node"])
|
102
101
|
end
|
103
102
|
end
|
104
103
|
|
104
|
+
# Counts nodes. Options:
|
105
|
+
#
|
106
|
+
# - label: filter by label
|
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)"]
|
117
|
+
end
|
118
|
+
|
105
119
|
# Adds an edge. If successul it returns the created object, otherwise false
|
106
120
|
#
|
107
121
|
def add_edge(edge)
|
@@ -114,6 +128,45 @@ module Redgraph
|
|
114
128
|
edge
|
115
129
|
end
|
116
130
|
|
131
|
+
# Finds edges. Options:
|
132
|
+
#
|
133
|
+
# - type
|
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
|
168
|
+
end
|
169
|
+
|
117
170
|
private
|
118
171
|
|
119
172
|
def query(cmd)
|
@@ -130,8 +183,9 @@ module Redgraph
|
|
130
183
|
def escape_value(x)
|
131
184
|
case x
|
132
185
|
when Integer then x
|
186
|
+
when NilClass then "''"
|
133
187
|
else
|
134
|
-
"'
|
188
|
+
'"' + x.gsub('"', '\"') + '"'
|
135
189
|
end
|
136
190
|
end
|
137
191
|
|
@@ -144,5 +198,39 @@ module Redgraph
|
|
144
198
|
@properties ||= properties
|
145
199
|
@properties[id] || (@properties = properties)[id]
|
146
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
|
147
235
|
end
|
148
236
|
end
|
data/lib/redgraph/version.rb
CHANGED
data/redgraph.gemspec
CHANGED
@@ -15,7 +15,7 @@ Gem::Specification.new do |spec|
|
|
15
15
|
|
16
16
|
spec.metadata["homepage_uri"] = spec.homepage
|
17
17
|
spec.metadata["source_code_uri"] = spec.homepage
|
18
|
-
spec.metadata["changelog_uri"] = "#{spec.homepage}/CHANGELOG.md"
|
18
|
+
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/master/CHANGELOG.md"
|
19
19
|
|
20
20
|
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
21
21
|
`git ls-files -z`.split("\x0")
|
@@ -17,6 +17,35 @@ class GraphManipulationTest < Minitest::Test
|
|
17
17
|
assert_predicate result, :persisted?
|
18
18
|
end
|
19
19
|
|
20
|
+
def test_add_node_with_special_chars
|
21
|
+
[
|
22
|
+
"apo'str",
|
23
|
+
"two''apos",
|
24
|
+
"Foø'bÆ®",
|
25
|
+
"aa\nbb",
|
26
|
+
'aaa "bbb" ccc'
|
27
|
+
].each do |name|
|
28
|
+
|
29
|
+
node = Redgraph::Node.new(label: 'actor', properties: {name: name})
|
30
|
+
result = @graph.add_node(node)
|
31
|
+
assert_predicate result, :persisted?
|
32
|
+
|
33
|
+
item = @graph.find_node_by_id(node.id)
|
34
|
+
|
35
|
+
assert_equal(name, item.properties["name"])
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_add_node_with_nil_value
|
40
|
+
node = Redgraph::Node.new(label: 'actor', properties: {name: nil})
|
41
|
+
result = @graph.add_node(node)
|
42
|
+
assert_predicate result, :persisted?
|
43
|
+
|
44
|
+
item = @graph.find_node_by_id(node.id)
|
45
|
+
|
46
|
+
assert_equal("", item.properties["name"])
|
47
|
+
end
|
48
|
+
|
20
49
|
def test_add_edge
|
21
50
|
actor = Redgraph::Node.new(label: 'actor', properties: {name: "Al Pacino"})
|
22
51
|
@graph.add_node(actor)
|
data/test/graph_queries_test.rb
CHANGED
@@ -62,6 +62,17 @@ class GraphQueriesTest < Minitest::Test
|
|
62
62
|
assert_includes(dramas, casino)
|
63
63
|
end
|
64
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"}))
|
74
|
+
end
|
75
|
+
|
65
76
|
def test_limit_nodes
|
66
77
|
10.times do |i|
|
67
78
|
quick_add_node(label: 'token', properties: {number: i})
|
@@ -82,9 +93,73 @@ class GraphQueriesTest < Minitest::Test
|
|
82
93
|
assert_equal([3,4,5], items.map{|item| item.properties["number"]})
|
83
94
|
end
|
84
95
|
|
96
|
+
def test_find_edge
|
97
|
+
quick_add_edge(type: 'FRIEND_OF', src: @al, dest: @john, properties: {since: 1980})
|
98
|
+
edge = @graph.edges.first
|
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)
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_filter_edges
|
118
|
+
marlon = quick_add_node(label: 'actor', properties: {name: "Marlon Brando"})
|
119
|
+
film = quick_add_node(label: 'film', properties: {name: "The Godfather"})
|
120
|
+
other_film = quick_add_node(label: 'film', properties: {name: "Carlito's Way"})
|
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"])
|
154
|
+
end
|
155
|
+
|
85
156
|
private
|
86
157
|
|
87
158
|
def quick_add_node(label:, properties:)
|
88
159
|
@graph.add_node(Redgraph::Node.new(label: label, properties: properties))
|
89
160
|
end
|
161
|
+
|
162
|
+
def quick_add_edge(type:, src:, dest:, properties:)
|
163
|
+
@graph.add_edge(Redgraph::Edge.new(type: type, src: src, dest: dest, properties: properties))
|
164
|
+
end
|
90
165
|
end
|
data/test/graph_test.rb
CHANGED
@@ -42,6 +42,19 @@ class GraphTest < Minitest::Test
|
|
42
42
|
assert_equal(["name", "age"], @graph.properties)
|
43
43
|
end
|
44
44
|
|
45
|
+
def test_relationship_types
|
46
|
+
@graph = create_sample_graph("foobar")
|
47
|
+
|
48
|
+
actor = Redgraph::Node.new(label: "actor", properties: {"name": "Harrison Ford"})
|
49
|
+
@graph.add_node(actor)
|
50
|
+
film = Redgraph::Node.new(label: "film", properties: {"name": "Star Wars"})
|
51
|
+
@graph.add_node(film)
|
52
|
+
edge = Redgraph::Edge.new(type: "ACTED_IN", src: actor, dest: film)
|
53
|
+
@graph.add_edge(edge)
|
54
|
+
|
55
|
+
assert_equal(["ACTED_IN"], @graph.relationship_types)
|
56
|
+
end
|
57
|
+
|
45
58
|
private
|
46
59
|
|
47
60
|
def create_sample_graph(name)
|
data/test/test_helper.rb
CHANGED
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.2
|
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-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis
|
@@ -60,7 +60,7 @@ licenses:
|
|
60
60
|
metadata:
|
61
61
|
homepage_uri: https://github.com/pzac/redgraph
|
62
62
|
source_code_uri: https://github.com/pzac/redgraph
|
63
|
-
changelog_uri: https://github.com/pzac/redgraph/CHANGELOG.md
|
63
|
+
changelog_uri: https://github.com/pzac/redgraph/blob/master/CHANGELOG.md
|
64
64
|
post_install_message:
|
65
65
|
rdoc_options: []
|
66
66
|
require_paths:
|