Skip to content

Commit a2bd290

Browse files
authored
Merge pull request #17 from RedisGraph/consume-results
result-set consume interface
2 parents cecfb4c + 025ea6f commit a2bd290

File tree

5 files changed

+130
-41
lines changed

5 files changed

+130
-41
lines changed

Diff for: README.md

+19-6
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ $ go get github.com/redislabs/redisgraph-go
2525
package main
2626

2727
import (
28+
"fmt"
2829
"github.com/gomodule/redigo/redis"
2930
rg "github.com/redislabs/redisgraph-go"
3031
)
@@ -64,22 +65,34 @@ func main() {
6465
graph.Commit()
6566

6667
query := `MATCH (p:person)-[v:visited]->(c:country)
67-
RETURN p.name, p.age, v.purpose, c.name`
68+
RETURN p.name, p.age, c.name`
6869
rs, _ := graph.Query(query)
6970

7071
rs.PrettyPrint()
72+
73+
// Access individual result record.
74+
// As long as there are records to consume.
75+
for rs.Next() {
76+
// Get current record.
77+
r := rs.Record()
78+
79+
p_name := r.GetByIndex(0).(string)
80+
p_age := r.GetByIndex(1).(int)
81+
c_name := r.GetByIndex(2).(string)
82+
fmt.Printf("p_name: %s p_age: %d c_name: %s\n", p_name, p_age, c_name)
83+
}
7184
}
7285
```
7386

7487
Running the above should output:
7588

7689
```sh
7790
$ go run main.go
78-
+----------+-----------+-----------+--------+
79-
| p.name | p.age | v.purpose | c.name |
80-
+----------+-----------+-----------+--------+
81-
| John Doe | 33.000000 | NULL | Japan |
82-
+----------+-----------+-----------+--------+
91+
+----------+-----------+--------+
92+
| p.name | p.age | c.name |
93+
+----------+-----------+--------+
94+
| John Doe | 33.000000 | Japan |
95+
+----------+-----------+--------+
8396
```
8497

8598
## Running tests

Diff for: client_test.go

+20-9
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,14 @@ func TestMatchQuery(t *testing.T) {
6767

6868
assert.Equal(t, len(res.results), 1, "expecting 1 result record")
6969

70-
s, ok := (res.results[0][0]).(*Node)
70+
res.Next()
71+
r := res.Record()
72+
73+
s, ok := r.GetByIndex(0).(*Node)
7174
assert.True(t, ok, "First column should contain nodes.")
72-
e, ok := (res.results[0][1]).(*Edge)
75+
e, ok := r.GetByIndex(1).(*Edge)
7376
assert.True(t, ok, "Second column should contain edges.")
74-
d, ok := (res.results[0][2]).(*Node)
77+
d, ok := r.GetByIndex(2).(*Node)
7578
assert.True(t, ok, "Third column should contain nodes.")
7679

7780
assert.Equal(t, s.Label, "Person", "Node should be of type 'Person'")
@@ -111,7 +114,9 @@ func TestCreateQuery(t *testing.T) {
111114
}
112115

113116
assert.False(t, res.Empty(), "Expecting resultset to include a single node.")
114-
w := (res.results[0][0]).(*Node)
117+
res.Next()
118+
r := res.Record()
119+
w := r.GetByIndex(0).(*Node)
115120
assert.Equal(t, w.Label, "WorkPlace", "Unexpected node label.")
116121
}
117122

@@ -149,9 +154,10 @@ func TestArray(t *testing.T) {
149154
t.Error(err)
150155
}
151156

157+
res.Next()
158+
r := res.Record()
152159
assert.Equal(t, len(res.results), 1, "expecting 1 result record")
153-
154-
assert.Equal(t, []interface{}{0, 1, 2}, res.results[0][0])
160+
assert.Equal(t, []interface{}{0, 1, 2}, r.GetByIndex(0))
155161

156162
q = "unwind([0,1,2]) as x return x"
157163
res, err = graph.Query(q)
@@ -160,8 +166,11 @@ func TestArray(t *testing.T) {
160166
}
161167
assert.Equal(t, len(res.results), 3, "expecting 3 result record")
162168

163-
for i := 0; i < 3; i++ {
164-
assert.Equal(t, i, res.results[i][0])
169+
i := 0
170+
for res.Next() {
171+
r = res.Record()
172+
assert.Equal(t, i, r.GetByIndex(0))
173+
i++
165174
}
166175

167176
q = "MATCH(n) return collect(n) as x"
@@ -183,7 +192,9 @@ func TestArray(t *testing.T) {
183192

184193
assert.Equal(t, 1, len(res.results), "expecting 1 results record")
185194

186-
arr := res.results[0][0].([]interface{})
195+
res.Next()
196+
r = res.Record()
197+
arr := r.GetByIndex(0).([]interface{})
187198

188199
assert.Equal(t, 2, len(arr))
189200

Diff for: graph.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ func (g *Graph) Labels() []string {
200200
l := make([]string, len(qr.results))
201201

202202
for idx, r := range qr.results {
203-
l[idx] = r[0].(string)
203+
l[idx] = r.GetByIndex(0).(string)
204204
}
205205
return l
206206
}
@@ -212,7 +212,7 @@ func (g *Graph) RelationshipTypes() []string {
212212
rt := make([]string, len(qr.results))
213213

214214
for idx, r := range qr.results {
215-
rt[idx] = r[0].(string)
215+
rt[idx] = r.GetByIndex(0).(string)
216216
}
217217
return rt
218218
}
@@ -224,7 +224,7 @@ func (g *Graph) PropertyKeys() []string {
224224
p := make([]string, len(qr.results))
225225

226226
for idx, r := range qr.results {
227-
p[idx] = r[0].(string)
227+
p[idx] = r.GetByIndex(0).(string)
228228
}
229229
return p
230230
}

Diff for: query_result.go

+47-23
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,11 @@ type QueryResultHeader struct {
5050

5151
// QueryResult represents the results of a query.
5252
type QueryResult struct {
53-
results [][]interface{}
54-
statistics map[string]float64
55-
header QueryResultHeader
56-
graph *Graph
53+
graph *Graph
54+
header QueryResultHeader
55+
results []*Record
56+
statistics map[string]float64
57+
current_record_idx int
5758
}
5859

5960
func QueryResultNew(g *Graph, response interface{}) (*QueryResult, error) {
@@ -65,6 +66,7 @@ func QueryResultNew(g *Graph, response interface{}) (*QueryResult, error) {
6566
column_types: make([]ResultSetColumnTypes, 0),
6667
},
6768
graph: g,
69+
current_record_idx: -1,
6870
}
6971

7072
r, _ := redis.Values(response, nil)
@@ -120,31 +122,30 @@ func (qr *QueryResult) parseHeader(raw_header interface{}) {
120122

121123
func (qr *QueryResult) parseRecords(raw_result_set []interface{}) {
122124
records, _ := redis.Values(raw_result_set[1], nil)
123-
124-
qr.results = make([][]interface{}, len(records))
125+
qr.results = make([]*Record, len(records))
125126

126127
for i, r := range records {
127128
cells, _ := redis.Values(r, nil)
128-
record := make([]interface{}, len(cells))
129+
values := make([]interface{}, len(cells))
129130

130131
for idx, c := range cells {
131132
t := qr.header.column_types[idx]
132133
switch t {
133134
case COLUMN_SCALAR:
134135
s, _ := redis.Values(c, nil)
135-
record[idx] = qr.parseScalar(s)
136+
values[idx] = qr.parseScalar(s)
136137
break
137138
case COLUMN_NODE:
138-
record[idx] = qr.parseNode(c)
139+
values[idx] = qr.parseNode(c)
139140
break
140141
case COLUMN_RELATION:
141-
record[idx] = qr.parseEdge(c)
142+
values[idx] = qr.parseEdge(c)
142143
break
143144
default:
144145
panic("Unknown column type.")
145146
}
146147
}
147-
qr.results[i] = record
148+
qr.results[i] = recordNew(values, qr.header.column_names)
148149
}
149150
}
150151

@@ -252,6 +253,36 @@ func (qr *QueryResult) parseScalar(cell []interface{}) interface{} {
252253
return s
253254
}
254255

256+
func (qr *QueryResult) getStat(stat string) int {
257+
if val, ok := qr.statistics[stat]; ok {
258+
return int(val)
259+
} else {
260+
return 0
261+
}
262+
}
263+
264+
// Next returns true only if there is a record to be processed.
265+
func (qr *QueryResult) Next() bool {
266+
if qr.Empty() {
267+
return false
268+
}
269+
if qr.current_record_idx < len(qr.results)-1 {
270+
qr.current_record_idx++
271+
return true
272+
} else {
273+
return false
274+
}
275+
}
276+
277+
// Record returns the current record.
278+
func (qr *QueryResult) Record() *Record {
279+
if qr.current_record_idx >= 0 && qr.current_record_idx < len(qr.results) {
280+
return qr.results[qr.current_record_idx]
281+
} else {
282+
return nil
283+
}
284+
}
285+
255286
// PrettyPrint prints the QueryResult to stdout, pretty-like.
256287
func (qr *QueryResult) PrettyPrint() {
257288
if qr.Empty() {
@@ -261,13 +292,14 @@ func (qr *QueryResult) PrettyPrint() {
261292
table := tablewriter.NewWriter(os.Stdout)
262293
table.SetAutoFormatHeaders(false)
263294
table.SetHeader(qr.header.column_names)
264-
295+
row_count := len(qr.results)
296+
col_count := len(qr.header.column_names)
265297
if len(qr.results) > 0 {
266298
// Convert to [][]string.
267-
results := make([][]string, len(qr.results))
299+
results := make([][]string, row_count)
268300
for i, record := range qr.results {
269-
results[i] = make([]string, len(record))
270-
for j, elem := range record {
301+
results[i] = make([]string, col_count)
302+
for j, elem := range record.Values() {
271303
results[i][j] = fmt.Sprint(elem)
272304
}
273305
}
@@ -284,14 +316,6 @@ func (qr *QueryResult) PrettyPrint() {
284316
fmt.Fprintf(os.Stdout, "\n")
285317
}
286318

287-
func (qr *QueryResult) getStat(stat string) int {
288-
if val, ok := qr.statistics[stat]; ok {
289-
return int(val)
290-
} else {
291-
return 0
292-
}
293-
}
294-
295319
func (qr *QueryResult) LabelsAdded() int {
296320
return qr.getStat(LABELS_ADDED)
297321
}

Diff for: record.go

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package redisgraph
2+
3+
type Record struct {
4+
values []interface{}
5+
keys []string
6+
}
7+
8+
func recordNew(values []interface{}, keys []string) *Record {
9+
r := &Record {
10+
values: values,
11+
keys: keys,
12+
}
13+
14+
return r
15+
}
16+
17+
func (r *Record) Keys() []string {
18+
return r.keys
19+
}
20+
21+
func (r *Record) Values() []interface{} {
22+
return r.values
23+
}
24+
25+
func (r *Record) Get(key string) (interface{}, bool) {
26+
// TODO: switch from []string to map[string]int
27+
for i := range r.keys {
28+
if r.keys[i] == key {
29+
return r.values[i], true
30+
}
31+
}
32+
return nil, false
33+
}
34+
35+
func (r *Record) GetByIndex(index int) interface{} {
36+
if(index < len(r.values)) {
37+
return r.values[index]
38+
} else {
39+
return nil
40+
}
41+
}

0 commit comments

Comments
 (0)