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

Steven Allen's avatar
Steven Allen committed
3 4
import (
	"sort"
5

Steven Allen's avatar
Steven Allen committed
6 7
	goprocess "github.com/jbenet/goprocess"
)
8 9 10

// NaiveFilter applies a filter to the results.
func NaiveFilter(qr Results, filter Filter) Results {
Steven Allen's avatar
Steven Allen committed
11 12 13 14 15 16 17 18 19 20
	return ResultsFromIterator(qr.Query(), Iterator{
		Next: func() (Result, bool) {
			for {
				e, ok := qr.NextSync()
				if !ok {
					return Result{}, false
				}
				if e.Error != nil || filter.Filter(e.Entry) {
					return e, true
				}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
21
			}
Steven Allen's avatar
Steven Allen committed
22 23 24 25 26
		},
		Close: func() error {
			return qr.Close()
		},
	})
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
27 28 29
}

// NaiveLimit truncates the results to a given int limit
30
func NaiveLimit(qr Results, limit int) Results {
Steven Allen's avatar
Steven Allen committed
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
	if limit == 0 {
		// 0 means no limit
		return qr
	}
	closed := false
	return ResultsFromIterator(qr.Query(), Iterator{
		Next: func() (Result, bool) {
			if limit == 0 {
				if !closed {
					closed = true
					err := qr.Close()
					if err != nil {
						return Result{Error: err}, true
					}
				}
				return Result{}, false
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
47
			}
Steven Allen's avatar
Steven Allen committed
48 49 50 51 52 53
			limit--
			return qr.NextSync()
		},
		Close: func() error {
			if closed {
				return nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
54
			}
Steven Allen's avatar
Steven Allen committed
55 56 57 58
			closed = true
			return qr.Close()
		},
	})
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
59 60 61
}

// NaiveOffset skips a given number of results
62
func NaiveOffset(qr Results, offset int) Results {
Steven Allen's avatar
Steven Allen committed
63 64 65 66 67 68 69
	return ResultsFromIterator(qr.Query(), Iterator{
		Next: func() (Result, bool) {
			for ; offset > 0; offset-- {
				res, ok := qr.NextSync()
				if !ok || res.Error != nil {
					return res, ok
				}
70
			}
Steven Allen's avatar
Steven Allen committed
71 72 73 74 75 76
			return qr.NextSync()
		},
		Close: func() error {
			return qr.Close()
		},
	})
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
77 78
}

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

Steven Allen's avatar
Steven Allen committed
87
	return ResultsWithProcess(qr.Query(), func(worker goprocess.Process, out chan<- Result) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
88
		defer qr.Close()
Steven Allen's avatar
Steven Allen committed
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
		var entries []Entry
	collect:
		for {
			select {
			case <-worker.Closing():
				return
			case e, ok := <-qr.Next():
				if !ok {
					break collect
				}
				if e.Error != nil {
					out <- e
					continue
				}
				entries = append(entries, e.Entry)
104 105
			}
		}
Steven Allen's avatar
Steven Allen committed
106

107
		sort.Slice(entries, func(i int, j int) bool {
Steven Allen's avatar
Steven Allen committed
108
			return Less(orders, entries[i], entries[j])
109
		})
110
		for _, e := range entries {
Steven Allen's avatar
Steven Allen committed
111 112 113 114 115
			select {
			case <-worker.Closing():
				return
			case out <- Result{Entry: e}:
			}
116
		}
Steven Allen's avatar
Steven Allen committed
117
	})
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
118 119
}

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

139
func ResultEntriesFrom(keys []string, vals [][]byte) []Entry {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
140 141 142 143 144 145
	re := make([]Entry, len(keys))
	for i, k := range keys {
		re[i] = Entry{Key: k, Value: vals[i]}
	}
	return re
}