redgraph 0.1.4 → 0.2.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 +14 -0
- data/Gemfile.lock +7 -8
- data/README.md +98 -41
- data/lib/redgraph/graph/node_methods.rb +22 -1
- data/lib/redgraph/graph.rb +2 -1
- data/lib/redgraph/node.rb +9 -4
- data/lib/redgraph/node_model/class_methods.rb +83 -0
- data/lib/redgraph/node_model/graph_manipulation.rb +24 -0
- data/lib/redgraph/node_model/persistence.rb +56 -0
- data/lib/redgraph/node_model.rb +19 -80
- data/lib/redgraph/query_response.rb +12 -3
- data/lib/redgraph/version.rb +1 -1
- data/lib/redgraph.rb +5 -0
- data/test/node_model_class_methods_test.rb +93 -0
- data/test/node_model_labels_test.rb +57 -0
- data/test/node_model_persistence_test.rb +70 -0
- data/test/node_model_test.rb +37 -61
- data/test/node_test.rb +23 -0
- data/test/test_helper.rb +2 -1
- metadata +10 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8b51ea5b1c1c11aab62f08cfe08508130f65def1a3b527ddcaf9419140c4e12a
|
4
|
+
data.tar.gz: 6518c572f6a4c14c733653acf1282c9b8473a8737b028a5695f93f953766f0bd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 634966fdde2e4567654b9a2d3e77eb77b26b8d1c8def562deeef8015d0b798ef8c7a9dd993dd15eabeeac4e2cc32013b748649c3f599dcc524742edd67d0b252
|
7
|
+
data.tar.gz: 9dba064e18ded1c51b80eca7d17cf487e1748b31fcbc18be656d57dcbe70954c563f3e7ee7bcd6ed847e4d9e170b40c0a498007980642349f1a4aa1ba5f0c706
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
## [0.2.2] - 2022-05-15
|
2
|
+
|
3
|
+
- Nodes allow multiple labels
|
4
|
+
- Friendlier exception message when the RedisGraph module is missing
|
5
|
+
|
6
|
+
## [0.2.1]
|
7
|
+
|
8
|
+
- Add NodeModel#destroy method
|
9
|
+
- Add NodeModel.create method
|
10
|
+
|
11
|
+
## [0.2.0]
|
12
|
+
|
13
|
+
- revamp the NodeModel mixin, the Node to model mapping is now handled by the `_type` property
|
14
|
+
|
1
15
|
## [0.1.4]
|
2
16
|
|
3
17
|
- add NodeModel mixin for a basic ActiveRecord-like syntax
|
data/Gemfile.lock
CHANGED
@@ -1,23 +1,22 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
redgraph (0.
|
4
|
+
redgraph (0.2.2)
|
5
5
|
activesupport (>= 3.0.0)
|
6
6
|
redis (~> 4)
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
|
-
activesupport (
|
11
|
+
activesupport (7.0.3)
|
12
12
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
13
13
|
i18n (>= 1.6, < 2)
|
14
14
|
minitest (>= 5.1)
|
15
15
|
tzinfo (~> 2.0)
|
16
|
-
zeitwerk (~> 2.3)
|
17
16
|
coderay (1.1.3)
|
18
|
-
concurrent-ruby (1.1.
|
17
|
+
concurrent-ruby (1.1.10)
|
19
18
|
docile (1.3.5)
|
20
|
-
i18n (1.
|
19
|
+
i18n (1.10.0)
|
21
20
|
concurrent-ruby (~> 1.0)
|
22
21
|
method_source (1.0.0)
|
23
22
|
minitest (5.14.4)
|
@@ -25,7 +24,7 @@ GEM
|
|
25
24
|
coderay (~> 1.1)
|
26
25
|
method_source (~> 1.0)
|
27
26
|
rake (13.0.3)
|
28
|
-
redis (4.
|
27
|
+
redis (4.6.0)
|
29
28
|
simplecov (0.21.2)
|
30
29
|
docile (~> 1.1)
|
31
30
|
simplecov-html (~> 0.11)
|
@@ -34,9 +33,9 @@ GEM
|
|
34
33
|
simplecov_json_formatter (0.1.2)
|
35
34
|
tzinfo (2.0.4)
|
36
35
|
concurrent-ruby (~> 1.0)
|
37
|
-
zeitwerk (2.4.2)
|
38
36
|
|
39
37
|
PLATFORMS
|
38
|
+
arm64-darwin-21
|
40
39
|
x86_64-darwin-20
|
41
40
|
|
42
41
|
DEPENDENCIES
|
@@ -47,4 +46,4 @@ DEPENDENCIES
|
|
47
46
|
simplecov (~> 0.21.2)
|
48
47
|
|
49
48
|
BUNDLED WITH
|
50
|
-
2.2.
|
49
|
+
2.2.22
|
data/README.md
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# Redgraph
|
2
2
|
|
3
|
+
[](https://badge.fury.io/rb/redgraph)
|
4
|
+
[](https://codeclimate.com/github/pzac/redgraph)
|
5
|
+
|
3
6
|
A simple RedisGraph library. This gem owes **a lot** to the existing [redisgraph-rb](https://github.com/RedisGraph/redisgraph-rb) gem, but tries to provide a friendlier interface, similar to the existing [Python](https://github.com/RedisGraph/redisgraph-py) and [Elixir](https://github.com/crflynn/redisgraph-ex) clients.
|
4
7
|
|
5
8
|
## Installation
|
@@ -24,85 +27,130 @@ The gem assumes you have a recent version of [RedisGraph](https://oss.redislabs.
|
|
24
27
|
|
25
28
|
Basic usage:
|
26
29
|
|
27
|
-
|
28
|
-
|
30
|
+
```ruby
|
31
|
+
graph = Redgraph::Graph.new('movies', url: "redis://localhost:6379/1")
|
32
|
+
=> #<Redgraph::Graph:0x00007f8d5c2b7e38 @connection=#<Redis client v4.2.5 for redis://localhost:6379/1>, @graph_name="movies", @module_version=999999>
|
33
|
+
```
|
29
34
|
|
30
35
|
Create a couple nodes:
|
31
36
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
37
|
+
```ruby
|
38
|
+
actor = Redgraph::Node.new(label: 'actor', properties: {name: "Al Pacino"})
|
39
|
+
=> #<Redgraph::Node:0x00007fce3baa0580 @id=nil, @labels=["actor"], @properties={"name"=>"Al Pacino"}>
|
40
|
+
graph.add_node(actor)
|
41
|
+
=> #<Redgraph::Node:0x00007fce3baa0580 @id=0, @labels=["actor"], @properties={"name"=>"Al Pacino"}>
|
42
|
+
film = Redgraph::Node.new(label: 'film', properties: {name: "Scarface"})
|
43
|
+
=> #<Redgraph::Node:0x00007fce3e8c6c48 @id=nil, @labels=["film"], @properties={"name"=>"Scarface"}>
|
44
|
+
graph.add_node(film)
|
45
|
+
=> #<Redgraph::Node:0x00007fce3e8c6c48 @id=1, @labels=["film"], @properties={"name"=>"Scarface"}>
|
46
|
+
```
|
47
|
+
|
48
|
+
Nodes might have multiple labels, although they're not supported by RedisGraph yet (you can track the feature progress [here](https://github.com/RedisGraph/RedisGraph/pull/1561)):
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
item = Redgraph::Node.new(labels: ['film', 'drama'], properties: {name: "Casino"})
|
52
|
+
=> #<Redgraph::Node:0x00007fce3bc73308 @id=nil, @labels=["film", "drama"], @properties={"name"=>"Casino"}>
|
53
|
+
```
|
40
54
|
|
41
55
|
Create an edge between those nodes:
|
42
56
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
57
|
+
```ruby
|
58
|
+
edge = Redgraph::Edge.new(src: actor, dest: film, type: 'ACTOR_IN', properties: {role: "Tony Montana"})
|
59
|
+
=> #<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">
|
60
|
+
graph.add_edge(edge)
|
61
|
+
=> #<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">
|
62
|
+
```
|
47
63
|
|
48
64
|
You can merge nodes - the node will be created only if there isn't another with the same label and properties:
|
49
65
|
|
50
|
-
|
51
|
-
|
66
|
+
```ruby
|
67
|
+
graph.merge_node(film)
|
68
|
+
=> #<Redgraph::Node:0x00007f8d5f85ccc8 @id=1, @label="film", @properties={:name=>"Scarface"}>
|
69
|
+
```
|
52
70
|
|
53
71
|
Same with edges:
|
54
72
|
|
55
|
-
|
56
|
-
|
73
|
+
```ruby
|
74
|
+
graph.merge_edge(edge)
|
75
|
+
=> #<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">
|
76
|
+
```
|
57
77
|
|
58
78
|
Find a node by id:
|
59
79
|
|
60
|
-
|
61
|
-
|
80
|
+
```ruby
|
81
|
+
graph.find_node_by_id(1)
|
82
|
+
=> #<Redgraph::Node:0x00007f8d5c2c6e88 @id=1, @label="film", @properties={"name"=>"Scarface"}>
|
83
|
+
```
|
62
84
|
|
63
85
|
To get all nodes:
|
64
86
|
|
65
|
-
|
66
|
-
|
87
|
+
```ruby
|
88
|
+
graph.nodes
|
89
|
+
=> [#<Redgraph::Node:0x00007f8d5c2ee0a0 @id=0, @label="actor", @properties={"name"=>"Al Pacino"}>, #<Redgraph::Node:0x00007f8d5c2edfd8 @id=1, @label="film", @properties={"name"=>"Scarface"}>]
|
90
|
+
```
|
67
91
|
|
68
92
|
Optional filters that can be combined:
|
69
93
|
|
70
|
-
|
71
|
-
|
72
|
-
|
94
|
+
```ruby
|
95
|
+
graph.nodes(label: 'actor')
|
96
|
+
graph.nodes(properties: {name: "Al Pacino"})
|
97
|
+
graph.nodes(limit: 10, skip: 20)
|
98
|
+
```
|
73
99
|
|
74
100
|
Counting nodes
|
75
101
|
|
76
|
-
|
77
|
-
|
102
|
+
```ruby
|
103
|
+
graph.count_nodes(label: 'actor')
|
104
|
+
=> 1
|
105
|
+
```
|
78
106
|
|
79
107
|
Getting edges:
|
80
108
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
109
|
+
```ruby
|
110
|
+
graph.edges
|
111
|
+
graph.edges(src: actor, dest: film)
|
112
|
+
graph.edges(kind: 'FRIEND_OF', limit: 10, skip: 20)
|
113
|
+
graph.count_edges
|
114
|
+
```
|
85
115
|
|
86
116
|
Running custom queries
|
87
117
|
|
88
|
-
|
118
|
+
```ruby
|
119
|
+
graph.query("MATCH (src)-[edge:FRIEND_OF]->(dest) RETURN src, edge")
|
120
|
+
```
|
89
121
|
|
90
122
|
### NodeModel
|
91
123
|
|
92
124
|
You can use the `NodeModel` mixin for a limited ActiveRecord-like interface:
|
93
125
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
126
|
+
```ruby
|
127
|
+
class Actor
|
128
|
+
include Redgraph::NodeModel
|
129
|
+
self.graph = Redgraph::Graph.new("movies", url: $REDIS_URL)
|
130
|
+
attribute :name
|
131
|
+
end
|
132
|
+
```
|
99
133
|
|
100
134
|
And this will give you stuff such as
|
101
135
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
136
|
+
```ruby
|
137
|
+
Actor.count
|
138
|
+
john = Actor.new(name: "John Travolta")
|
139
|
+
john.add_to_graph
|
140
|
+
john.add_relation(type: "ACTED_IN", node: film, properties: {role: "Tony Manero"})
|
141
|
+
john.reload
|
142
|
+
john.destroy
|
143
|
+
Actor.create(name: "Al Pacino")
|
144
|
+
```
|
145
|
+
|
146
|
+
`NodeModel` models will automatically set a `_type` property to keep track of the object class.
|
147
|
+
|
148
|
+
You will then be able to run custom queries such as:
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
Actor.query("MATCH (node) RETURN node ORDER BY node.name")
|
152
|
+
```
|
153
|
+
And the result rows object will be instances of the classes defined by the `_type` attribute.
|
106
154
|
|
107
155
|
## Development
|
108
156
|
|
@@ -118,6 +166,15 @@ You can also run `bin/console` for an interactive prompt that will allow you to
|
|
118
166
|
|
119
167
|
To install this gem onto your local machine, run `bundle exec rake install`.
|
120
168
|
|
169
|
+
### Installing RedisGraph
|
170
|
+
|
171
|
+
If you're using an Apple silicon mac you might want to use the docker image: I've had issues compiling the module (OpenMP problems). Just do a:
|
172
|
+
|
173
|
+
docker run -p 6380:6379 -it --rm redislabs/redisgraph
|
174
|
+
TEST_REDIS_URL=redis://localhost:6380/0 be rake test
|
175
|
+
|
176
|
+
I'm using port 6380 to not interphere with the other redis instance.
|
177
|
+
|
121
178
|
## Contributing
|
122
179
|
|
123
180
|
Bug reports and pull requests are welcome on GitHub at https://github.com/pzac/redgraph.
|
@@ -68,7 +68,27 @@ module Redgraph
|
|
68
68
|
node = Node.new(label: label, properties: properties)
|
69
69
|
|
70
70
|
cmd = "MATCH #{node.to_query_string} RETURN COUNT(node)"
|
71
|
-
|
71
|
+
# RedisGraph bug: if there are no matches COUNT returns zero rows
|
72
|
+
# https://github.com/RedisGraph/RedisGraph/issues/1455
|
73
|
+
query(cmd).flatten[0] || 0
|
74
|
+
end
|
75
|
+
|
76
|
+
def update_node(node)
|
77
|
+
return false unless node.persisted?
|
78
|
+
_set = node.properties.map do |(key, val)|
|
79
|
+
"node.#{key} = #{escape_value(val)}"
|
80
|
+
end.join(", ")
|
81
|
+
|
82
|
+
cmd = "MATCH (node) WHERE ID(node) = #{node.id} SET #{_set} RETURN node"
|
83
|
+
result = _query(cmd)
|
84
|
+
node_from_resultset_item(result.resultset.first["node"])
|
85
|
+
end
|
86
|
+
|
87
|
+
def destroy_node(node)
|
88
|
+
return false unless node.persisted?
|
89
|
+
cmd = "MATCH (node) WHERE ID(node) = #{node.id} DELETE node"
|
90
|
+
result = _query(cmd)
|
91
|
+
result.stats["nodes_deleted"] == 1
|
72
92
|
end
|
73
93
|
|
74
94
|
private
|
@@ -97,6 +117,7 @@ module Redgraph
|
|
97
117
|
node
|
98
118
|
end
|
99
119
|
|
120
|
+
|
100
121
|
end
|
101
122
|
end
|
102
123
|
end
|
data/lib/redgraph/graph.rb
CHANGED
@@ -7,6 +7,7 @@ module Redgraph
|
|
7
7
|
class Graph
|
8
8
|
include NodeMethods
|
9
9
|
include EdgeMethods
|
10
|
+
include Util
|
10
11
|
|
11
12
|
attr_accessor :connection, :graph_name
|
12
13
|
|
@@ -18,7 +19,7 @@ module Redgraph
|
|
18
19
|
@graph_name = graph_name
|
19
20
|
@connection = Redis.new(redis_options)
|
20
21
|
@module_version = module_version
|
21
|
-
raise ServerError unless @module_version
|
22
|
+
raise ServerError, "Can't find RedisGraph module" unless @module_version
|
22
23
|
end
|
23
24
|
|
24
25
|
# Returns the version of the RedisGraph module
|
data/lib/redgraph/node.rb
CHANGED
@@ -4,14 +4,19 @@ module Redgraph
|
|
4
4
|
class Node
|
5
5
|
include Util
|
6
6
|
|
7
|
-
attr_accessor :id, :
|
7
|
+
attr_accessor :id, :labels, :properties
|
8
8
|
|
9
|
-
def initialize(label: nil, properties: nil, id: nil)
|
9
|
+
def initialize(label: nil, properties: nil, id: nil, labels: nil)
|
10
10
|
@id = id
|
11
|
-
|
11
|
+
raise(Error, "You can either define a single label or a label array") if label && labels
|
12
|
+
@labels = labels || (label ? [label] : [])
|
12
13
|
@properties = (properties || {}).with_indifferent_access
|
13
14
|
end
|
14
15
|
|
16
|
+
def label
|
17
|
+
labels.first
|
18
|
+
end
|
19
|
+
|
15
20
|
def persisted?
|
16
21
|
id.present?
|
17
22
|
end
|
@@ -21,7 +26,7 @@ module Redgraph
|
|
21
26
|
end
|
22
27
|
|
23
28
|
def to_query_string(item_alias: 'node')
|
24
|
-
_label = "
|
29
|
+
_label = labels.map {|l| ":`#{l}`"}.join
|
25
30
|
"(#{item_alias}#{_label} #{properties_to_string(properties)})"
|
26
31
|
end
|
27
32
|
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Redgraph
|
2
|
+
module NodeModel
|
3
|
+
module ClassMethods
|
4
|
+
# Returns an array of nodes. Options:
|
5
|
+
#
|
6
|
+
# - label: filter by label
|
7
|
+
# - properties: filter by properties
|
8
|
+
# - order: node.name ASC, node.year DESC
|
9
|
+
# - limit: number of items
|
10
|
+
# - skip: items offset (useful for pagination)
|
11
|
+
#
|
12
|
+
def all(label: nil, properties: {}, limit: nil, skip: nil, order: nil)
|
13
|
+
graph.nodes(label: label, properties: properties_plus_type(properties),
|
14
|
+
limit: limit, skip: skip, order: nil).map do |node|
|
15
|
+
reify_from_node(node)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns the number of nodes with the current label. Options:
|
20
|
+
#
|
21
|
+
# - properties: filter by properties
|
22
|
+
#
|
23
|
+
def count(label: nil, properties: nil)
|
24
|
+
graph.count_nodes(label: label, properties: properties_plus_type(properties))
|
25
|
+
end
|
26
|
+
|
27
|
+
# Finds a node by id. Returns nil if not found
|
28
|
+
#
|
29
|
+
def find(id)
|
30
|
+
node = graph.find_node_by_id(id)
|
31
|
+
return unless node
|
32
|
+
reify_from_node(node)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Sets the label for this class of nodes. If missing it will be computed from the class name
|
36
|
+
def label=(x)
|
37
|
+
@label = x
|
38
|
+
end
|
39
|
+
|
40
|
+
# Current label
|
41
|
+
#
|
42
|
+
def label
|
43
|
+
@label ||= default_label
|
44
|
+
end
|
45
|
+
|
46
|
+
# Converts a Node object into NodeModel
|
47
|
+
#
|
48
|
+
def reify_from_node(node)
|
49
|
+
klass = node.properties[:_type].to_s.safe_constantize || self
|
50
|
+
klass.new(id: node.id, **node.properties)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Runs a query on the graph, but converts the nodes to the corresponding ActiveModel class
|
54
|
+
# if available - otherwise they stay NodeObjects.
|
55
|
+
#
|
56
|
+
# Returns an array of rows.
|
57
|
+
#
|
58
|
+
def query(cmd)
|
59
|
+
raise MissingGraphError unless graph
|
60
|
+
|
61
|
+
graph.query(cmd).map do |row|
|
62
|
+
row.map do |item|
|
63
|
+
item.is_a?(Node) ? reify_from_node(item) : item
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def create(properties)
|
69
|
+
new(**properties).add_to_graph
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def default_label
|
75
|
+
name.demodulize.underscore
|
76
|
+
end
|
77
|
+
|
78
|
+
def properties_plus_type(properties = {})
|
79
|
+
{_type: name}.merge(properties || {})
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Redgraph
|
2
|
+
module NodeModel
|
3
|
+
module GraphManipulation
|
4
|
+
# Adds a relation between the node and another node.
|
5
|
+
#
|
6
|
+
# - type: type of relation
|
7
|
+
# - node: the destination node
|
8
|
+
# - properties: optional properties hash
|
9
|
+
# - allow_duplicates: if false it will create a relation between two nodes with the same type
|
10
|
+
# and properties only if not present
|
11
|
+
#
|
12
|
+
def add_relation(type:, node:, properties: nil, allow_duplicates: true)
|
13
|
+
edge = Edge.new(type: type, src: to_node, dest: node.to_node, properties: properties)
|
14
|
+
allow_duplicates ? graph.add_edge(edge) : graph.merge_edge(edge)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Runs a custom query on the graph
|
18
|
+
#
|
19
|
+
def query(cmd)
|
20
|
+
self.class.query(cmd)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Redgraph
|
2
|
+
module NodeModel
|
3
|
+
module Persistence
|
4
|
+
# Adds the node to the graph
|
5
|
+
#
|
6
|
+
# - allow_duplicates: if false it will create a node with the same type and properties only if
|
7
|
+
# not present
|
8
|
+
#
|
9
|
+
def add_to_graph(allow_duplicates: true)
|
10
|
+
raise MissingGraphError unless graph
|
11
|
+
item = allow_duplicates ? graph.add_node(to_node) : graph.merge_node(to_node)
|
12
|
+
self.id = item.id
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
# Creates a new record or updates the existing
|
17
|
+
#
|
18
|
+
def save
|
19
|
+
if persisted?
|
20
|
+
item = graph.update_node(to_node)
|
21
|
+
self.class.reify_from_node(item)
|
22
|
+
else
|
23
|
+
add_to_graph
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def persisted?
|
28
|
+
id.present?
|
29
|
+
end
|
30
|
+
|
31
|
+
def reload
|
32
|
+
item = self.class.find(id)
|
33
|
+
@label = item.label
|
34
|
+
assign_attributes(item.attributes)
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
# Deletes the record from the graph
|
39
|
+
#
|
40
|
+
def destroy
|
41
|
+
@destroyed = true
|
42
|
+
if graph.destroy_node(self)
|
43
|
+
self
|
44
|
+
else
|
45
|
+
false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns true if this object has been destroyed, otherwise returns false.
|
50
|
+
#
|
51
|
+
def destroyed?
|
52
|
+
!!@destroyed
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/redgraph/node_model.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
require_relative 'node_model/class_methods'
|
3
|
+
require_relative 'node_model/graph_manipulation'
|
4
|
+
require_relative 'node_model/persistence'
|
2
5
|
|
3
6
|
module Redgraph
|
4
7
|
# This mixin allows you to use an interface similar to ActiveRecord
|
@@ -16,13 +19,19 @@ module Redgraph
|
|
16
19
|
# john = Actor.find(123)
|
17
20
|
# total = Actor.count
|
18
21
|
#
|
22
|
+
# When you create a record it will automatically set the _type property with the class name.
|
23
|
+
# This allows reifying the node into the corresponding NodeModel class.
|
24
|
+
#
|
19
25
|
module NodeModel
|
20
26
|
extend ActiveSupport::Concern
|
21
27
|
|
22
|
-
included do
|
28
|
+
included do |base|
|
29
|
+
include Persistence
|
30
|
+
include GraphManipulation
|
31
|
+
|
23
32
|
@attribute_names = [:id]
|
24
33
|
|
25
|
-
attr_accessor :id
|
34
|
+
attr_accessor :id, :_type
|
26
35
|
|
27
36
|
class << self
|
28
37
|
attr_reader :attribute_names
|
@@ -30,7 +39,7 @@ module Redgraph
|
|
30
39
|
|
31
40
|
def attribute(name)
|
32
41
|
@attribute_names << name
|
33
|
-
|
42
|
+
attr_accessor(name)
|
34
43
|
end
|
35
44
|
|
36
45
|
private
|
@@ -43,57 +52,8 @@ module Redgraph
|
|
43
52
|
end
|
44
53
|
end
|
45
54
|
|
46
|
-
class_methods do
|
47
|
-
# Returns an array of nodes. Options:
|
48
|
-
#
|
49
|
-
# - properties: filter by properties
|
50
|
-
# - order: node.name ASC, node.year DESC
|
51
|
-
# - limit: number of items
|
52
|
-
# - skip: items offset (useful for pagination)
|
53
|
-
#
|
54
|
-
def all(properties: nil, limit: nil, skip: nil, order: nil)
|
55
|
-
graph.nodes(label: label, properties: properties,
|
56
|
-
limit: limit, skip: skip, order: nil).map do |node|
|
57
|
-
new(id: node.id, **node.properties)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
# Returns the number of nodes with the current label. Options:
|
62
|
-
#
|
63
|
-
# - properties: filter by properties
|
64
|
-
#
|
65
|
-
def count(properties: nil)
|
66
|
-
graph.count_nodes(label: label, properties: properties)
|
67
|
-
end
|
68
|
-
|
69
|
-
# Finds a node by id. Returns nil if not found
|
70
|
-
#
|
71
|
-
def find(id)
|
72
|
-
node = graph.find_node_by_id(id)
|
73
|
-
return unless node
|
74
|
-
new(id: node.id, **node.properties)
|
75
|
-
end
|
76
|
-
|
77
|
-
# Sets the label for this class of nodes. If missing it will be computed from the class name
|
78
|
-
def label=(x)
|
79
|
-
@label = x
|
80
|
-
end
|
81
|
-
|
82
|
-
# Current label
|
83
|
-
#
|
84
|
-
def label
|
85
|
-
@label ||= default_label
|
86
|
-
end
|
87
|
-
|
88
|
-
private
|
89
|
-
|
90
|
-
def default_label
|
91
|
-
name.demodulize.underscore
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
55
|
def initialize(**args)
|
96
|
-
absent_attributes = args.keys.map(&:to_sym) - self.class.attribute_names
|
56
|
+
absent_attributes = args.keys.map(&:to_sym) - self.class.attribute_names - [:_type]
|
97
57
|
|
98
58
|
if absent_attributes.any?
|
99
59
|
raise ArgumentError, "Unknown attribute #{absent_attributes}"
|
@@ -120,36 +80,15 @@ module Redgraph
|
|
120
80
|
self.class.attribute_names.to_h { |name| [name, public_send(name)] }
|
121
81
|
end
|
122
82
|
|
123
|
-
def
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
# Adds the node to the graph
|
128
|
-
#
|
129
|
-
# - allow_duplicates: if false it will create a node with the same type and properties only if
|
130
|
-
# not present
|
131
|
-
#
|
132
|
-
def add_to_graph(allow_duplicates: true)
|
133
|
-
item = allow_duplicates ? graph.add_node(to_node) : graph.merge_node(to_node)
|
134
|
-
self.id = item.id
|
135
|
-
self
|
136
|
-
end
|
137
|
-
|
138
|
-
# Adds a relation between the node and another node.
|
139
|
-
#
|
140
|
-
# - type: type of relation
|
141
|
-
# - node: the destination node
|
142
|
-
# - properties: optional properties hash
|
143
|
-
# - allow_duplicates: if false it will create a relation between two nodes with the same type
|
144
|
-
# and properties only if not present
|
145
|
-
#
|
146
|
-
def add_relation(type:, node:, properties: nil, allow_duplicates: true)
|
147
|
-
edge = Edge.new(type: type, src: to_node, dest: node.to_node, properties: properties)
|
148
|
-
allow_duplicates ? graph.add_edge(edge) : graph.merge_edge(edge)
|
83
|
+
def assign_attributes(attrs = {})
|
84
|
+
attrs.each do |name, value|
|
85
|
+
instance_variable_set("@#{name}", value)
|
86
|
+
end
|
149
87
|
end
|
150
88
|
|
151
89
|
def to_node
|
152
|
-
|
90
|
+
props = attributes.except(:id).merge(_type: self.class.name)
|
91
|
+
Redgraph::Node.new(id: id, label: label, properties: props)
|
153
92
|
end
|
154
93
|
|
155
94
|
def ==(other)
|
@@ -29,9 +29,16 @@ module Redgraph
|
|
29
29
|
@response = response
|
30
30
|
@graph = graph
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
32
|
+
case @response.size
|
33
|
+
when 3
|
34
|
+
@header_row = @response[0]
|
35
|
+
@result_rows = @response[1]
|
36
|
+
@query_statistics = @response[2]
|
37
|
+
when 1 # queries with no RETURN clause
|
38
|
+
@header_row = []
|
39
|
+
@result_rows = []
|
40
|
+
@query_statistics = @response[0]
|
41
|
+
end
|
35
42
|
end
|
36
43
|
|
37
44
|
def stats
|
@@ -121,6 +128,8 @@ module Redgraph
|
|
121
128
|
case label
|
122
129
|
when /^Nodes created/
|
123
130
|
stats[:nodes_created] = value.to_i
|
131
|
+
when /^Nodes deleted/
|
132
|
+
stats[:nodes_deleted] = value.to_i
|
124
133
|
when /^Relationships created/
|
125
134
|
stats[:relationships_created] = value.to_i
|
126
135
|
when /^Properties set/
|
data/lib/redgraph/version.rb
CHANGED
data/lib/redgraph.rb
CHANGED
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "test_helper"
|
4
|
+
|
5
|
+
class NodeModelClassMethodsTest < Minitest::Test
|
6
|
+
include TestHelpers
|
7
|
+
|
8
|
+
GRAPH = Redgraph::Graph.new("movies", url: $REDIS_URL)
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@graph = GRAPH
|
12
|
+
end
|
13
|
+
|
14
|
+
def teardown
|
15
|
+
@graph.delete
|
16
|
+
end
|
17
|
+
|
18
|
+
# test classes
|
19
|
+
#
|
20
|
+
|
21
|
+
class Actor
|
22
|
+
include Redgraph::NodeModel
|
23
|
+
self.graph = GRAPH
|
24
|
+
attribute :name
|
25
|
+
end
|
26
|
+
|
27
|
+
class MissingGraphActor
|
28
|
+
include Redgraph::NodeModel
|
29
|
+
attribute :name
|
30
|
+
end
|
31
|
+
|
32
|
+
# tests
|
33
|
+
#
|
34
|
+
|
35
|
+
def test_count
|
36
|
+
quick_add_node(label: 'actor', properties: {_type: Actor.name, name: "Al Pacino"})
|
37
|
+
quick_add_node(label: 'actor', properties: {_type: Actor.name, name: "John Travolta"})
|
38
|
+
assert_equal(2, Actor.count)
|
39
|
+
assert_equal(1, Actor.count(properties: {name: "Al Pacino"}))
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_all
|
43
|
+
al = Actor.new(name: "Al Pacino").add_to_graph
|
44
|
+
john = Actor.new(name: "John Travolta").add_to_graph
|
45
|
+
|
46
|
+
items = Actor.all
|
47
|
+
assert_equal(2, items.size)
|
48
|
+
assert_includes(items, al)
|
49
|
+
assert_includes(items, john)
|
50
|
+
|
51
|
+
items = Actor.all(properties: {name: "Al Pacino"})
|
52
|
+
assert_equal(1, items.size)
|
53
|
+
assert_includes(items, al)
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_find
|
57
|
+
al = quick_add_node(label: 'actor', properties: {name: "Al Pacino"})
|
58
|
+
item = Actor.find(al.id)
|
59
|
+
|
60
|
+
assert_equal(Actor, item.class)
|
61
|
+
assert_predicate(item, :persisted?)
|
62
|
+
assert_equal(al.id, item.id)
|
63
|
+
assert_equal("Al Pacino", item.name)
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_find_bad_id
|
67
|
+
quick_add_node(label: 'actor', properties: {name: "Al Pacino"})
|
68
|
+
item = Actor.find("-1")
|
69
|
+
assert_nil(item)
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_create
|
73
|
+
assert_equal(0, Actor.count)
|
74
|
+
|
75
|
+
actor = Actor.create(name: "Harrison Ford")
|
76
|
+
|
77
|
+
assert_equal(1, Actor.count)
|
78
|
+
assert_predicate(actor, :persisted?)
|
79
|
+
assert_equal("Harrison Ford", actor.name)
|
80
|
+
assert_equal("actor", actor.label)
|
81
|
+
|
82
|
+
found = Actor.find(actor.id)
|
83
|
+
assert_equal("Harrison Ford", found.name)
|
84
|
+
assert_equal("actor", found.label)
|
85
|
+
assert_equal(Actor.name, found._type)
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_create_with_missing_graph
|
89
|
+
assert_raises(Redgraph::MissingGraphError) do
|
90
|
+
MissingGraphActor.create(name: "Harrison Ford")
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "test_helper"
|
4
|
+
|
5
|
+
class NodeModelLabelsTest < Minitest::Test
|
6
|
+
include TestHelpers
|
7
|
+
|
8
|
+
GRAPH = Redgraph::Graph.new("movies", url: $REDIS_URL)
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@graph = GRAPH
|
12
|
+
end
|
13
|
+
|
14
|
+
def teardown
|
15
|
+
@graph.delete
|
16
|
+
end
|
17
|
+
|
18
|
+
# test classes
|
19
|
+
#
|
20
|
+
|
21
|
+
class Actor
|
22
|
+
include Redgraph::NodeModel
|
23
|
+
self.graph = GRAPH
|
24
|
+
attribute :name
|
25
|
+
end
|
26
|
+
|
27
|
+
class Artist
|
28
|
+
include Redgraph::NodeModel
|
29
|
+
self.label = "_artist"
|
30
|
+
end
|
31
|
+
|
32
|
+
class Painter < Artist
|
33
|
+
end
|
34
|
+
|
35
|
+
class Pianist < Artist
|
36
|
+
self.label = "pianist"
|
37
|
+
end
|
38
|
+
|
39
|
+
# tests
|
40
|
+
#
|
41
|
+
|
42
|
+
def test_label
|
43
|
+
assert_equal("actor", Actor.label)
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_custom_label
|
47
|
+
assert_equal("_artist", Artist.label)
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_default_label_when_inherited
|
51
|
+
assert_equal("painter", Painter.label)
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_custom_label_when_inherited
|
55
|
+
assert_equal("pianist", Pianist.label)
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "test_helper"
|
4
|
+
|
5
|
+
class NodeModelPersistenceTest < Minitest::Test
|
6
|
+
include TestHelpers
|
7
|
+
|
8
|
+
GRAPH = Redgraph::Graph.new("movies", url: $REDIS_URL)
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@graph = GRAPH
|
12
|
+
end
|
13
|
+
|
14
|
+
def teardown
|
15
|
+
@graph.delete
|
16
|
+
end
|
17
|
+
|
18
|
+
# test classes
|
19
|
+
#
|
20
|
+
|
21
|
+
class Film
|
22
|
+
include Redgraph::NodeModel
|
23
|
+
self.graph = GRAPH
|
24
|
+
attribute :name
|
25
|
+
end
|
26
|
+
|
27
|
+
# tests
|
28
|
+
#
|
29
|
+
|
30
|
+
def test_save_new
|
31
|
+
assert_equal(0, Film.count)
|
32
|
+
|
33
|
+
film = Film.new(name: "Star Wars")
|
34
|
+
film.save
|
35
|
+
|
36
|
+
assert_predicate(film, :persisted?)
|
37
|
+
assert_equal(1, Film.count)
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_save_existing
|
41
|
+
film = Film.create(name: "Star Wars")
|
42
|
+
film.name = "Commando"
|
43
|
+
film.save
|
44
|
+
|
45
|
+
assert_equal(1, Film.count)
|
46
|
+
item = Film.find(film.id)
|
47
|
+
assert_equal("Commando", item.name)
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_reload
|
51
|
+
film = Film.create(name: "Star Wars")
|
52
|
+
copy = Film.find(film.id)
|
53
|
+
|
54
|
+
assert_equal("Star Wars", copy.name)
|
55
|
+
|
56
|
+
film.name = "Commando"
|
57
|
+
film.save
|
58
|
+
|
59
|
+
assert_equal("Star Wars", copy.name)
|
60
|
+
assert_equal("Commando", copy.reload.name)
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_destroy
|
64
|
+
film = Film.create(name: "Star Wars")
|
65
|
+
assert_equal(1, Film.count)
|
66
|
+
film.destroy
|
67
|
+
assert_predicate(film, :destroyed?)
|
68
|
+
assert_equal(0, Film.count)
|
69
|
+
end
|
70
|
+
end
|
data/test/node_model_test.rb
CHANGED
@@ -15,6 +15,9 @@ class NodeModelTest < Minitest::Test
|
|
15
15
|
@graph.delete
|
16
16
|
end
|
17
17
|
|
18
|
+
# test classes
|
19
|
+
#
|
20
|
+
|
18
21
|
class Animal
|
19
22
|
include Redgraph::NodeModel
|
20
23
|
attribute :name
|
@@ -24,73 +27,12 @@ class NodeModelTest < Minitest::Test
|
|
24
27
|
class Dog < Animal
|
25
28
|
end
|
26
29
|
|
27
|
-
def test_graph_accessor
|
28
|
-
assert_equal("pippo", Animal.graph)
|
29
|
-
assert_equal("pippo", Animal.new.graph)
|
30
|
-
end
|
31
|
-
|
32
|
-
def test_class_inheritance
|
33
|
-
assert_equal("pippo", Dog.graph)
|
34
|
-
assert_equal("dog", Dog.label)
|
35
|
-
assert_equal([:id, :name], Dog.attribute_names)
|
36
|
-
end
|
37
|
-
|
38
30
|
class Actor
|
39
31
|
include Redgraph::NodeModel
|
40
32
|
self.graph = GRAPH
|
41
33
|
attribute :name
|
42
34
|
end
|
43
35
|
|
44
|
-
def test_count
|
45
|
-
quick_add_node(label: 'actor', properties: {name: "Al Pacino"})
|
46
|
-
quick_add_node(label: 'actor', properties: {name: "John Travolta"})
|
47
|
-
assert_equal(2, Actor.count)
|
48
|
-
assert_equal(1, Actor.count(properties: {name: "Al Pacino"}))
|
49
|
-
end
|
50
|
-
|
51
|
-
def test_all
|
52
|
-
al = Actor.new(name: "Al Pacino").add_to_graph
|
53
|
-
john = Actor.new(name: "John Travolta").add_to_graph
|
54
|
-
|
55
|
-
items = Actor.all
|
56
|
-
assert_equal(2, items.size)
|
57
|
-
assert_includes(items, al)
|
58
|
-
assert_includes(items, john)
|
59
|
-
|
60
|
-
items = Actor.all(properties: {name: "Al Pacino"})
|
61
|
-
assert_equal(1, items.size)
|
62
|
-
assert_includes(items, al)
|
63
|
-
end
|
64
|
-
|
65
|
-
def test_find
|
66
|
-
al = quick_add_node(label: 'actor', properties: {name: "Al Pacino"})
|
67
|
-
item = Actor.find(al.id)
|
68
|
-
|
69
|
-
assert_equal(Actor, item.class)
|
70
|
-
assert_predicate(item, :persisted?)
|
71
|
-
assert_equal(al.id, item.id)
|
72
|
-
assert_equal("Al Pacino", item.name)
|
73
|
-
end
|
74
|
-
|
75
|
-
def test_find_bad_id
|
76
|
-
quick_add_node(label: 'actor', properties: {name: "Al Pacino"})
|
77
|
-
item = Actor.find("-1")
|
78
|
-
assert_nil(item)
|
79
|
-
end
|
80
|
-
|
81
|
-
def test_label
|
82
|
-
assert_equal("actor", Actor.label)
|
83
|
-
end
|
84
|
-
|
85
|
-
class Artist
|
86
|
-
include Redgraph::NodeModel
|
87
|
-
self.label = "person"
|
88
|
-
end
|
89
|
-
|
90
|
-
def test_custom_label
|
91
|
-
assert_equal("person", Artist.label)
|
92
|
-
end
|
93
|
-
|
94
36
|
class Film
|
95
37
|
include Redgraph::NodeModel
|
96
38
|
self.graph = GRAPH
|
@@ -98,6 +40,20 @@ class NodeModelTest < Minitest::Test
|
|
98
40
|
attribute :year
|
99
41
|
end
|
100
42
|
|
43
|
+
# tests
|
44
|
+
#
|
45
|
+
|
46
|
+
def test_graph_accessor
|
47
|
+
assert_equal("pippo", Animal.graph)
|
48
|
+
assert_equal("pippo", Animal.new.graph)
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_class_inheritance
|
52
|
+
assert_equal("pippo", Dog.graph)
|
53
|
+
assert_equal("dog", Dog.label)
|
54
|
+
assert_equal([:id, :name], Dog.attribute_names)
|
55
|
+
end
|
56
|
+
|
101
57
|
def test_attribute_names
|
102
58
|
assert_equal([:id, :name, :year], Film.attribute_names)
|
103
59
|
|
@@ -157,4 +113,24 @@ class NodeModelTest < Minitest::Test
|
|
157
113
|
assert_equal(2, @graph.count_edges)
|
158
114
|
end
|
159
115
|
|
116
|
+
def test_casting_query
|
117
|
+
Film.new(name: "Star Wars", year: 1977).add_to_graph
|
118
|
+
Actor.new(name: "Harrison Ford").add_to_graph
|
119
|
+
|
120
|
+
items = Film.query("MATCH (node) RETURN node ORDER BY node.name")
|
121
|
+
assert_equal(2, items.size)
|
122
|
+
assert_kind_of(Actor, items[0][0])
|
123
|
+
assert_kind_of(Film, items[1][0])
|
124
|
+
end
|
125
|
+
|
126
|
+
def test_casting_query_from_model
|
127
|
+
film = Film.create(name: "Star Wars", year: 1977)
|
128
|
+
Actor.create(name: "Harrison Ford")
|
129
|
+
|
130
|
+
items = film.query("MATCH (node) RETURN node ORDER BY node.name")
|
131
|
+
assert_equal(2, items.size)
|
132
|
+
assert_kind_of(Actor, items[0][0])
|
133
|
+
assert_kind_of(Film, items[1][0])
|
134
|
+
end
|
135
|
+
|
160
136
|
end
|
data/test/node_test.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "test_helper"
|
4
|
+
|
5
|
+
class NodeTest < Minitest::Test
|
6
|
+
def test_single_labels
|
7
|
+
node = Redgraph::Node.new(label: "film")
|
8
|
+
assert_equal(["film"], node.labels)
|
9
|
+
assert_equal("film", node.label)
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_multiple_labels
|
13
|
+
node = Redgraph::Node.new(labels: ["film", "drama"])
|
14
|
+
assert_equal(["film", "drama"], node.labels)
|
15
|
+
assert_equal("film", node.label)
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_conflicting_labels
|
19
|
+
assert_raises(Redgraph::Error) do
|
20
|
+
node = Redgraph::Node.new(labels: ["film", "drama"], label: "film")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -16,7 +16,8 @@ require "minitest/autorun"
|
|
16
16
|
require "pry"
|
17
17
|
|
18
18
|
unless $REDIS_URL = ENV['TEST_REDIS_URL']
|
19
|
-
puts "To run the tests you need to define the TEST_REDIS_URL environment variable"
|
19
|
+
puts "To run the tests you need to define the TEST_REDIS_URL environment variable. Ex:"
|
20
|
+
puts " TEST_REDIS_URL=redis://localhost:6379/0"
|
20
21
|
exit(1)
|
21
22
|
end
|
22
23
|
|
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.
|
4
|
+
version: 0.2.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:
|
11
|
+
date: 2022-05-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis
|
@@ -62,6 +62,9 @@ files:
|
|
62
62
|
- lib/redgraph/graph/node_methods.rb
|
63
63
|
- lib/redgraph/node.rb
|
64
64
|
- lib/redgraph/node_model.rb
|
65
|
+
- lib/redgraph/node_model/class_methods.rb
|
66
|
+
- lib/redgraph/node_model/graph_manipulation.rb
|
67
|
+
- lib/redgraph/node_model/persistence.rb
|
65
68
|
- lib/redgraph/query_response.rb
|
66
69
|
- lib/redgraph/util.rb
|
67
70
|
- lib/redgraph/version.rb
|
@@ -72,7 +75,11 @@ files:
|
|
72
75
|
- test/graph_node_methods_test.rb
|
73
76
|
- test/graph_queries_test.rb
|
74
77
|
- test/graph_test.rb
|
78
|
+
- test/node_model_class_methods_test.rb
|
79
|
+
- test/node_model_labels_test.rb
|
80
|
+
- test/node_model_persistence_test.rb
|
75
81
|
- test/node_model_test.rb
|
82
|
+
- test/node_test.rb
|
76
83
|
- test/redgraph_test.rb
|
77
84
|
- test/test_helper.rb
|
78
85
|
homepage: https://github.com/pzac/redgraph
|
@@ -97,7 +104,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
97
104
|
- !ruby/object:Gem::Version
|
98
105
|
version: '0'
|
99
106
|
requirements: []
|
100
|
-
rubygems_version: 3.2.
|
107
|
+
rubygems_version: 3.2.22
|
101
108
|
signing_key:
|
102
109
|
specification_version: 4
|
103
110
|
summary: A simple RedisGraph client
|