Skip to content

Commit db9249e

Browse files
authored
Introducing the QueryNode interface to handle ordered query results (#100)
1 parent 6365187 commit db9249e

File tree

3 files changed

+337
-253
lines changed

3 files changed

+337
-253
lines changed

db/query.go

Lines changed: 47 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import (
1818
"encoding/json"
1919
"fmt"
2020
"net/http"
21-
"reflect"
2221
"sort"
2322
"strconv"
2423
"strings"
@@ -28,6 +27,12 @@ import (
2827
"golang.org/x/net/context"
2928
)
3029

30+
// QueryNode represents a data node retrieved from an ordered query.
31+
type QueryNode interface {
32+
Key() string
33+
Unmarshal(v interface{}) error
34+
}
35+
3136
// Query represents a complex query that can be executed on a Ref.
3237
//
3338
// Complex queries can consist of up to 2 components: a required ordering constraint, and an
@@ -112,40 +117,23 @@ func (q *Query) Get(ctx context.Context, v interface{}) error {
112117
return resp.Unmarshal(http.StatusOK, v)
113118
}
114119

115-
// GetOrdered executes the Query and provides the results as an ordered list.
116-
//
117-
// v must be a pointer to an array or a slice. Only the child values returned by the query are
118-
// unmarshalled into v. Top-level keys are not returned. Although if the Query was created using
119-
// OrderByKey(), the returned values will still be ordered based on their keys.
120-
func (q *Query) GetOrdered(ctx context.Context, v interface{}) error {
121-
rv := reflect.ValueOf(v)
122-
if rv.Kind() != reflect.Ptr || rv.IsNil() {
123-
return fmt.Errorf("nil or not a pointer")
124-
}
125-
if rv.Elem().Kind() != reflect.Slice && rv.Elem().Kind() != reflect.Array {
126-
return fmt.Errorf("non-array non-slice pointer")
127-
}
128-
120+
// GetOrdered executes the Query and returns the results as an ordered slice.
121+
func (q *Query) GetOrdered(ctx context.Context) ([]QueryNode, error) {
129122
var temp interface{}
130123
if err := q.Get(ctx, &temp); err != nil {
131-
return err
124+
return nil, err
132125
}
133-
134126
if temp == nil {
135-
return nil
127+
return nil, nil
136128
}
137-
sr := newSortableQueryResult(temp, q.order)
138-
sort.Sort(sr)
139129

140-
var values []interface{}
141-
for _, val := range sr {
142-
values = append(values, val.Value)
143-
}
144-
b, err := json.Marshal(values)
145-
if err != nil {
146-
return err
130+
sn := newSortableNodes(temp, q.order)
131+
sort.Sort(sn)
132+
result := make([]QueryNode, len(sn))
133+
for i, v := range sn {
134+
result[i] = v
147135
}
148-
return json.Unmarshal(b, v)
136+
return result, nil
149137
}
150138

151139
// OrderByChild returns a Query that orders data by child values before applying filters.
@@ -307,14 +295,30 @@ func newComparableKey(v interface{}) *comparableKey {
307295
return &comparableKey{Num: &f}
308296
}
309297

310-
type queryResult struct {
311-
Key *comparableKey
298+
type queryNodeImpl struct {
299+
CompKey *comparableKey
312300
Value interface{}
313301
Index interface{}
314302
IndexType int
315303
}
316304

317-
func newQueryResult(key, val interface{}, order orderBy) *queryResult {
305+
func (q *queryNodeImpl) Key() string {
306+
if q.CompKey.Str != nil {
307+
return *q.CompKey.Str
308+
}
309+
// Numeric keys in queryNodeImpl are always array indices, and can be safely coverted into int.
310+
return strconv.Itoa(int(*q.CompKey.Num))
311+
}
312+
313+
func (q *queryNodeImpl) Unmarshal(v interface{}) error {
314+
b, err := json.Marshal(q.Value)
315+
if err != nil {
316+
return err
317+
}
318+
return json.Unmarshal(b, v)
319+
}
320+
321+
func newQueryNode(key, val interface{}, order orderBy) *queryNodeImpl {
318322
var index interface{}
319323
if prop, ok := order.(orderByProperty); ok {
320324
if prop == "$value" {
@@ -326,25 +330,25 @@ func newQueryResult(key, val interface{}, order orderBy) *queryResult {
326330
path := order.(orderByChild)
327331
index = extractChildValue(val, string(path))
328332
}
329-
return &queryResult{
330-
Key: newComparableKey(key),
333+
return &queryNodeImpl{
334+
CompKey: newComparableKey(key),
331335
Value: val,
332336
Index: index,
333337
IndexType: getIndexType(index),
334338
}
335339
}
336340

337-
type sortableQueryResult []*queryResult
341+
type sortableNodes []*queryNodeImpl
338342

339-
func (s sortableQueryResult) Len() int {
343+
func (s sortableNodes) Len() int {
340344
return len(s)
341345
}
342346

343-
func (s sortableQueryResult) Swap(i, j int) {
347+
func (s sortableNodes) Swap(i, j int) {
344348
s[i], s[j] = s[j], s[i]
345349
}
346350

347-
func (s sortableQueryResult) Less(i, j int) bool {
351+
func (s sortableNodes) Less(i, j int) bool {
348352
a, b := s[i], s[j]
349353
var aKey, bKey *comparableKey
350354
if a.IndexType == b.IndexType {
@@ -353,7 +357,7 @@ func (s sortableQueryResult) Less(i, j int) bool {
353357
if (a.IndexType == typeNumeric || a.IndexType == typeString) && a.Index != b.Index {
354358
aKey, bKey = newComparableKey(a.Index), newComparableKey(b.Index)
355359
} else {
356-
aKey, bKey = a.Key, b.Key
360+
aKey, bKey = a.CompKey, b.CompKey
357361
}
358362
} else {
359363
// If the indices are of different types, use the type ordering of Firebase.
@@ -363,18 +367,18 @@ func (s sortableQueryResult) Less(i, j int) bool {
363367
return aKey.Compare(bKey) < 0
364368
}
365369

366-
func newSortableQueryResult(values interface{}, order orderBy) sortableQueryResult {
367-
var entries sortableQueryResult
370+
func newSortableNodes(values interface{}, order orderBy) sortableNodes {
371+
var entries sortableNodes
368372
if m, ok := values.(map[string]interface{}); ok {
369373
for key, val := range m {
370-
entries = append(entries, newQueryResult(key, val, order))
374+
entries = append(entries, newQueryNode(key, val, order))
371375
}
372376
} else if l, ok := values.([]interface{}); ok {
373377
for key, val := range l {
374-
entries = append(entries, newQueryResult(key, val, order))
378+
entries = append(entries, newQueryNode(key, val, order))
375379
}
376380
} else {
377-
entries = append(entries, newQueryResult(0, values, order))
381+
entries = append(entries, newQueryNode(0, values, order))
378382
}
379383
return entries
380384
}

0 commit comments

Comments
 (0)