query_impl.go 2.66 KB
Newer Older
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
1 2
package query

3 4
import "sort"

5 6 7 8 9 10 11 12 13 14 15
func DerivedResults(qr Results, ch <-chan Result) Results {
	return &results{
		query: qr.Query(),
		proc:  qr.Process(),
		res:   ch,
	}
}

// NaiveFilter applies a filter to the results.
func NaiveFilter(qr Results, filter Filter) Results {
	ch := make(chan Result)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
16 17
	go func() {
		defer close(ch)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
18
		defer qr.Close()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
19

20 21
		for e := range qr.Next() {
			if e.Error != nil || filter.Filter(e.Entry) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
22 23 24 25
				ch <- e
			}
		}
	}()
26 27

	return DerivedResults(qr, ch)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
28 29 30
}

// NaiveLimit truncates the results to a given int limit
31 32
func NaiveLimit(qr Results, limit int) Results {
	ch := make(chan Result)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
33 34
	go func() {
		defer close(ch)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
35
		defer qr.Close()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
36

37 38
		l := 0
		for e := range qr.Next() {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
39
			if e.Error != nil {
40
				ch <- e
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
41
				continue
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
42
			}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
43
			ch <- e
44
			l++
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
45 46 47
			if limit > 0 && l >= limit {
				break
			}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
48 49
		}
	}()
50 51

	return DerivedResults(qr, ch)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
52 53 54
}

// NaiveOffset skips a given number of results
55 56
func NaiveOffset(qr Results, offset int) Results {
	ch := make(chan Result)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
57 58
	go func() {
		defer close(ch)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
59
		defer qr.Close()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
60

61 62 63 64 65
		sent := 0
		for e := range qr.Next() {
			if e.Error != nil {
				ch <- e
			}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
66

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
67
			if sent < offset {
68
				sent++
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
69
				continue
70
			}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
71
			ch <- e
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
72 73
		}
	}()
74 75

	return DerivedResults(qr, ch)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
76 77
}

78
// NaiveOrder reorders results according to given orders.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
79
// WARNING: this is the only non-stream friendly operation!
80 81 82 83 84 85
func NaiveOrder(qr Results, orders ...Order) Results {
	// Short circuit.
	if len(orders) == 0 {
		return qr
	}

86 87 88 89
	ch := make(chan Result)
	var entries []Entry
	go func() {
		defer close(ch)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
90
		defer qr.Close()
91 92 93 94 95 96 97 98

		for e := range qr.Next() {
			if e.Error != nil {
				ch <- e
			}

			entries = append(entries, e.Entry)
		}
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
		sort.Slice(entries, func(i int, j int) bool {
			a, b := entries[i], entries[j]

			for _, cmp := range orders {
				switch cmp.Compare(a, b) {
				case 0:
				case -1:
					return true
				case 1:
					return false
				}
			}

			// This gives us a *stable* sort for free. We don't care
			// preserving the order from the underlying datastore
			// because it's undefined.
			return a.Key < b.Key
		})
117 118 119 120 121 122 123

		for _, e := range entries {
			ch <- Result{Entry: e}
		}
	}()

	return DerivedResults(qr, ch)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
124 125
}

126
func NaiveQueryApply(q Query, qr Results) Results {
127 128 129
	if q.Prefix != "" {
		qr = NaiveFilter(qr, FilterKeyPrefix{q.Prefix})
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
130 131 132
	for _, f := range q.Filters {
		qr = NaiveFilter(qr, f)
	}
133 134
	if len(q.Orders) > 0 {
		qr = NaiveOrder(qr, q.Orders...)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
135
	}
136 137 138 139 140 141
	if q.Offset != 0 {
		qr = NaiveOffset(qr, q.Offset)
	}
	if q.Limit != 0 {
		qr = NaiveLimit(qr, q.Offset)
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
142 143 144
	return qr
}

145
func ResultEntriesFrom(keys []string, vals [][]byte) []Entry {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
146 147 148 149 150 151
	re := make([]Entry, len(keys))
	for i, k := range keys {
		re[i] = Entry{Key: k, Value: vals[i]}
	}
	return re
}