/
cursor.go
128 lines (113 loc) · 3.08 KB
/
cursor.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package couchdb
// A Cursor holds a reference to a page in a couchdb View
type Cursor interface {
HasMore() bool
ApplyTo(req *ViewRequest)
UpdateFrom(res *ViewResponse)
}
// NewKeyCursor returns a new key based Cursor pointing to
// the given start_key & startkey_docid
func NewKeyCursor(limit int, key interface{}, id string) Cursor {
return &StartKeyCursor{
baseCursor: &baseCursor{Limit: limit},
NextKey: key,
NextDocID: id,
}
}
// NewSkipCursor returns a new skip based Cursor pointing to
// the page after skip items
func NewSkipCursor(limit, skip int) Cursor {
return &SkipCursor{
baseCursor: &baseCursor{Limit: limit},
Skip: skip,
}
}
type baseCursor struct {
// Done will be true if there is no more result after last fetch
Done bool
// Limit is maximum number of items retrieved from a request
Limit int
}
// HasMore returns true if there is more document after the current batch.
// This value is meaning full only after UpdateFrom
func (c *baseCursor) HasMore() bool { return !c.Done }
func (c *baseCursor) updateFrom(res *ViewResponse) {
lrows := len(res.Rows)
if lrows <= c.Limit {
c.Done = true
} else {
res.Rows = res.Rows[:lrows-1]
c.Done = false
}
}
// SkipCursor is a Cursor using Skip to know how deep in the request it is.
type SkipCursor struct {
*baseCursor
// Skip is the number of elements to start from
Skip int
}
// ApplyTo applies the cursor to a ViewRequest
// the transformed ViewRequest will retrieve elements from Cursor to
// Limit or EndKey whichever comes first
// /!\ Mutates req
func (c *SkipCursor) ApplyTo(req *ViewRequest) {
if c.Skip != 0 {
req.Skip = c.Skip
}
if c.Limit != 0 {
req.Limit = c.Limit + 1
}
}
// UpdateFrom change the cursor status depending on information from
// the view's response
func (c *SkipCursor) UpdateFrom(res *ViewResponse) {
c.baseCursor.updateFrom(res)
c.Skip += c.Limit
}
// StartKeyCursor is a Cursor using start_key, ie a reference to the
// last fetched item to keep pagination
type StartKeyCursor struct {
*baseCursor
// NextKey & NextDocID contains a reference to the document
// right after the last fetched one
NextKey interface{}
NextDocID string
}
// ApplyTo applies the cursor to a ViewRequest
// the transformed ViewRequest will retrieve elements from Cursor to
// Limit or EndKey whichever comes first
// /!\ Mutates req
func (c *StartKeyCursor) ApplyTo(req *ViewRequest) {
if c.NextKey != "" && c.NextKey != nil {
if req.Key != nil && req.StartKey == nil {
req.StartKey = req.Key
req.EndKey = req.Key
req.InclusiveEnd = true
req.Key = nil
}
req.StartKey = c.NextKey
if c.NextDocID != "" {
req.StartKeyDocID = c.NextDocID
}
}
if c.Limit != 0 {
req.Limit = c.Limit + 1
}
}
// UpdateFrom change the cursor status depending on information from
// the view's response
func (c *StartKeyCursor) UpdateFrom(res *ViewResponse) {
var next *ViewResponseRow
lrows := len(res.Rows)
if lrows > 0 {
next = res.Rows[lrows-1]
}
c.baseCursor.updateFrom(res)
if !c.Done {
c.NextKey = next.Key
c.NextDocID = next.ID
} else {
c.NextKey = nil
c.NextDocID = ""
}
}