Skip to content

Commit 9fb9b98

Browse files
committed
protect graph internal state update
1 parent f2edc9e commit 9fb9b98

File tree

5 files changed

+192
-54
lines changed

5 files changed

+192
-54
lines changed

client_test.go

Lines changed: 88 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,54 +2,115 @@ package redisgraph
22

33
import (
44
"testing"
5-
5+
"os"
6+
"time"
7+
"github.com/stretchr/testify/assert"
68
"github.com/gomodule/redigo/redis"
79
)
810

9-
func TestGraphCreation(t *testing.T) {
10-
// Setup.
11-
conn, _ := redis.Dial("tcp", "0.0.0.0:6379")
12-
defer conn.Close()
11+
var graph Graph
1312

13+
func createGraph() {
14+
conn, _ := redis.Dial("tcp", "0.0.0.0:6379")
1415
conn.Do("FLUSHALL")
15-
rg := GraphNew("social", conn)
16+
graph = GraphNew("social", conn)
1617

1718
// Create 2 nodes connect via a single edge.
18-
japan := NodeNew(0, "country", "j", nil)
19-
john := NodeNew(0, "person", "p", nil)
20-
edge := EdgeNew(0, "visited", john, japan, nil)
19+
japan := NodeNew("Country", "j", nil)
20+
john := NodeNew("Person", "p", nil)
21+
edge := EdgeNew("Visited", john, japan, nil)
2122

2223
// Set node properties.
2324
john.SetProperty("name", "John Doe")
2425
john.SetProperty("age", 33)
2526
john.SetProperty("gender", "male")
2627
john.SetProperty("status", "single")
28+
29+
japan.SetProperty("name", "Japan")
30+
japan.SetProperty("population", 126800000)
31+
32+
edge.SetProperty("year", 2017)
2733

2834
// Introduce entities to graph.
29-
rg.AddNode(john)
30-
rg.AddNode(japan)
31-
rg.AddEdge(edge)
35+
graph.AddNode(john)
36+
graph.AddNode(japan)
37+
graph.AddEdge(edge)
3238

3339
// Flush graph to DB.
34-
resp, err := rg.Commit()
40+
_, err := graph.Commit()
41+
if err != nil {
42+
panic(err)
43+
}
44+
}
45+
46+
func setup() {
47+
createGraph()
48+
}
49+
50+
func shutdown() {
51+
graph.Conn.Close()
52+
}
53+
54+
func TestMain(m *testing.M) {
55+
setup()
56+
code := m.Run()
57+
shutdown()
58+
os.Exit(code)
59+
}
60+
61+
func TestMatchQuery(t *testing.T) {
62+
q := "MATCH (s)-[e]->(d) RETURN s,e,d"
63+
res, err := graph.Query(q)
3564
if err != nil {
3665
t.Error(err)
3766
}
67+
68+
assert.Equal(t, len(res.results), 1, "expecting 1 result record")
69+
70+
s, ok := (res.results[0][0]).(*Node)
71+
assert.True(t, ok, "First column should contain nodes.")
72+
e, ok := (res.results[0][1]).(*Edge)
73+
assert.True(t, ok, "Second column should contain edges.")
74+
d, ok := (res.results[0][2]).(*Node)
75+
assert.True(t, ok, "Third column should contain nodes.")
76+
77+
assert.Equal(t, s.Label, "Person", "Node should be of type 'Person'")
78+
assert.Equal(t, e.Relation, "Visited", "Edge should be of relation type 'Visited'")
79+
assert.Equal(t, d.Label, "Country", "Node should be of type 'Country'")
80+
81+
assert.Equal(t, len(s.Properties), 4, "Person node should have 4 properties")
82+
83+
assert.Equal(t, s.GetProperty("name"), "John Doe", "Unexpected property value.")
84+
assert.Equal(t, s.GetProperty("age"), 33, "Unexpected property value.")
85+
assert.Equal(t, s.GetProperty("gender"), "male", "Unexpected property value.")
86+
assert.Equal(t, s.GetProperty("status"), "single", "Unexpected property value.")
87+
88+
assert.Equal(t, e.GetProperty("year"), 2017, "Unexpected property value.")
89+
90+
assert.Equal(t, d.GetProperty("name"), "Japan", "Unexpected property value.")
91+
assert.Equal(t, d.GetProperty("population"), 126800000, "Unexpected property value.")
92+
}
3893

39-
// Validate response.
40-
if(resp.results != nil) {
41-
t.FailNow()
94+
func TestCreateQuery(t *testing.T) {
95+
q := "CREATE (w:WorkPlace {name:'RedisLabs'})"
96+
res, err := graph.Query(q)
97+
if err != nil {
98+
t.Error(err)
4299
}
43-
if(resp.statistics["Labels added"] != 2) {
44-
t.FailNow()
100+
101+
assert.True(t, res.Empty(), "Expecting empty result-set")
102+
103+
// Validate statistics.
104+
assert.Equal(t, res.NodesCreated(), 1, "Expecting a single node to be created.")
105+
assert.Equal(t, res.PropertiesSet(), 1, "Expecting a songle property to be added.")
106+
107+
q = "MATCH (w:WorkPlace) RETURN w"
108+
res, err = graph.Query(q)
109+
if err != nil {
110+
t.Error(err)
45111
}
46-
if(resp.statistics["Nodes created"] != 2) {
47-
t.FailNow()
48-
}
49-
if(resp.statistics["Properties set"] != 4) {
50-
t.FailNow()
51-
}
52-
if(resp.statistics["Relationships created"] != 1) {
53-
t.FailNow()
54-
}
112+
113+
assert.False(t, res.Empty(), "Expecting resultset to include a single node.")
114+
w := (res.results[0][0]).(*Node)
115+
assert.Equal(t, w.Label, "WorkPlace", "Unexpected node label.")
55116
}

edge.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,30 @@ type Edge struct {
1717
graph *Graph
1818
}
1919

20-
func EdgeNew(id uint64, relation string, srcNode *Node, destNode *Node, properties map[string]interface{}) *Edge {
20+
func EdgeNew(relation string, srcNode *Node, destNode *Node, properties map[string]interface{}) *Edge {
21+
p := properties
22+
if p == nil {
23+
p = make(map[string]interface{})
24+
}
25+
2126
return &Edge{
22-
ID:id,
2327
Relation: relation,
2428
Source: srcNode,
2529
Destination: destNode,
26-
Properties: properties,
30+
Properties: p,
2731
graph: nil,
2832
}
2933
}
3034

35+
func (e *Edge) SetProperty(key string, value interface{}) {
36+
e.Properties[key] = value
37+
}
38+
39+
func (e *Edge) GetProperty(key string) interface{} {
40+
v,_ := e.Properties[key]
41+
return v
42+
}
43+
3144
func (e Edge) SourceNodeID() uint64 {
3245
if(e.Source != nil) {
3346
return e.Source.ID

graph.go

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package redisgraph
33
import (
44
"fmt"
55
"strings"
6+
"sync"
67

78
"github.com/gomodule/redigo/redis"
89
)
@@ -16,6 +17,7 @@ type Graph struct {
1617
labels []string // List of node labels.
1718
relationshipTypes []string // List of relation types.
1819
properties []string // List of properties.
20+
mutex sync.Mutex // Lock, used for updating internal state.
1921
}
2022

2123
// New creates a new graph.
@@ -114,47 +116,60 @@ func (g *Graph) Merge(p string) (*QueryResult, error) {
114116

115117
func (g *Graph) getLabel(lblIdx int) string {
116118
if lblIdx >= len(g.labels) {
117-
// Missing label
118-
// refresh label mapping table.
119-
g.labels = g.Labels()
119+
// Missing label, refresh label mapping table.
120+
g.mutex.Lock()
120121

121-
// Retry.
122+
// Recheck now that we've got the lock.
122123
if lblIdx >= len(g.labels) {
123-
// Error!
124-
panic("Unknow label index.")
124+
g.labels = g.Labels()
125+
// Retry.
126+
if lblIdx >= len(g.labels) {
127+
// Error!
128+
panic("Unknow label index.")
129+
}
125130
}
131+
g.mutex.Unlock()
126132
}
127133

128134
return g.labels[lblIdx]
129135
}
130136

131137
func (g *Graph) getRelation(relIdx int) string {
132138
if relIdx >= len(g.relationshipTypes) {
133-
// Missing relation type
134-
// refresh relation type mapping table.
135-
g.relationshipTypes = g.RelationshipTypes()
139+
// Missing relation type, refresh relation type mapping table.
140+
g.mutex.Lock()
136141

137-
// Retry.
142+
// Recheck now that we've got the lock.
138143
if relIdx >= len(g.relationshipTypes) {
139-
// Error!
140-
panic("Unknow relation type index.")
144+
g.relationshipTypes = g.RelationshipTypes()
145+
// Retry.
146+
if relIdx >= len(g.relationshipTypes) {
147+
// Error!
148+
panic("Unknow relation type index.")
149+
}
141150
}
151+
g.mutex.Unlock()
142152
}
143153

144154
return g.relationshipTypes[relIdx]
145155
}
146156

147157
func (g *Graph) getProperty(propIdx int) string {
148158
if propIdx >= len(g.properties) {
149-
// Missing property
150-
// refresh property mapping table.
151-
g.properties = g.PropertyKeys()
159+
// Missing property, refresh property mapping table.
160+
g.mutex.Lock()
152161

153-
// Retry.
162+
// Recheck now that we've got the lock.
154163
if propIdx >= len(g.properties) {
155-
// Error!
156-
panic("Unknow property index.")
164+
g.properties = g.PropertyKeys()
165+
166+
// Retry.
167+
if propIdx >= len(g.properties) {
168+
// Error!
169+
panic("Unknow property index.")
170+
}
157171
}
172+
g.mutex.Unlock()
158173
}
159174

160175
return g.properties[propIdx]

node.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,14 @@ type Node struct {
1414
graph *Graph
1515
}
1616

17-
func NodeNew(id uint64, label string, alias string, properties map[string]interface{}) *Node {
17+
func NodeNew(label string, alias string, properties map[string]interface{}) *Node {
1818

1919
p := properties
2020
if p == nil {
2121
p = make(map[string]interface{})
2222
}
2323

24-
return &Node{
25-
ID: id,
24+
return &Node{
2625
Label: label,
2726
Alias: alias,
2827
Properties: p,

query_result.go

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@ import (
1010
"github.com/olekukonko/tablewriter"
1111
)
1212

13+
const (
14+
LABELS_ADDED string = "Labels added"
15+
NODES_CREATED string = "Nodes created"
16+
NODES_DELETED string = "Nodes deleted"
17+
RELATIONSHIPS_DELETED string = "Relationships deleted"
18+
PROPERTIES_SET string = "Properties set"
19+
RELATIONSHIPS_CREATED string = "Relationships created"
20+
INTERNAL_EXECUTION_TIME string = "internal execution time"
21+
)
22+
1323
type ResultSetColumnTypes int
1424
const (
1525
COLUMN_UNKNOWN ResultSetColumnTypes = iota
@@ -158,7 +168,9 @@ func (qr *QueryResult) parseNode(cell interface{}) *Node {
158168
rawProps, _ := redis.Values(c[2], nil)
159169
properties := qr.parseProperties(rawProps)
160170

161-
return NodeNew(id, label, "", properties)
171+
n := NodeNew(label, "", properties)
172+
n.ID = id
173+
return n
162174
}
163175

164176
func (qr *QueryResult) parseEdge(cell interface{}) *Edge {
@@ -177,8 +189,9 @@ func (qr *QueryResult) parseEdge(cell interface{}) *Edge {
177189
dest_node_id, _ := redis.Uint64(c[3], nil)
178190
rawProps,_ := redis.Values(c[4], nil)
179191
properties := qr.parseProperties(rawProps)
180-
e := EdgeNew(id, relation, nil, nil, properties)
181-
192+
e := EdgeNew(relation, nil, nil, properties)
193+
194+
e.ID = id
182195
e.srcNodeID = src_node_id
183196
e.destNodeID = dest_node_id
184197
return e
@@ -242,3 +255,40 @@ func (qr *QueryResult) PrettyPrint() {
242255

243256
fmt.Fprintf(os.Stdout, "\n")
244257
}
258+
259+
260+
func (qr *QueryResult) getStat(stat string) int {
261+
if val, ok := qr.statistics[stat]; ok {
262+
return int(val)
263+
} else {
264+
return 0
265+
}
266+
}
267+
268+
func (qr *QueryResult) LabelsAdded() int {
269+
return qr.getStat(LABELS_ADDED)
270+
}
271+
272+
func (qr *QueryResult) NodesCreated() int {
273+
return qr.getStat(NODES_CREATED)
274+
}
275+
276+
func (qr *QueryResult) NodesDeleted() int {
277+
return qr.getStat(NODES_DELETED)
278+
}
279+
280+
func (qr *QueryResult) PropertiesSet() int {
281+
return qr.getStat(PROPERTIES_SET)
282+
}
283+
284+
func (qr *QueryResult) RelationshipsCreated() int {
285+
return qr.getStat(RELATIONSHIPS_CREATED)
286+
}
287+
288+
func (qr *QueryResult) RelationshipsDeleted() int {
289+
return qr.getStat(RELATIONSHIPS_DELETED)
290+
}
291+
292+
func (qr *QueryResult) RunTime() int {
293+
return qr.getStat(INTERNAL_EXECUTION_TIME)
294+
}

0 commit comments

Comments
 (0)