channeldb/invoices: extend invoice queries to allow backwards pagination
This commit is contained in:
1 file changed
+70
-46
+70
-46
@@ -402,15 +402,19 @@ func (d *DB) FetchAllInvoices(pendingOnly bool) ([]Invoice, error) {
|
|||||||
type InvoiceQuery struct {
|
type InvoiceQuery struct {
|
||||||
// IndexOffset is the offset within the add indices to start at. This
|
// IndexOffset is the offset within the add indices to start at. This
|
||||||
// can be used to start the response at a particular invoice.
|
// can be used to start the response at a particular invoice.
|
||||||
IndexOffset uint32
|
IndexOffset uint64
|
||||||
|
|
||||||
// NumMaxInvoices is the maximum number of invoices that should be
|
// NumMaxInvoices is the maximum number of invoices that should be
|
||||||
// starting from the add index.
|
// starting from the add index.
|
||||||
NumMaxInvoices uint32
|
NumMaxInvoices uint64
|
||||||
|
|
||||||
// PendingOnly, if set, returns unsettled invoices starting from the
|
// PendingOnly, if set, returns unsettled invoices starting from the
|
||||||
// add index.
|
// add index.
|
||||||
PendingOnly bool
|
PendingOnly bool
|
||||||
|
|
||||||
|
// Reversed, if set, indicates that the invoices returned should start
|
||||||
|
// from the IndexOffset and go backwards.
|
||||||
|
Reversed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// InvoiceSlice is the response to a invoice query. It includes the original
|
// InvoiceSlice is the response to a invoice query. It includes the original
|
||||||
@@ -423,13 +427,19 @@ type InvoiceSlice struct {
|
|||||||
InvoiceQuery
|
InvoiceQuery
|
||||||
|
|
||||||
// Invoices is the set of invoices that matched the query above.
|
// Invoices is the set of invoices that matched the query above.
|
||||||
Invoices []*Invoice
|
Invoices []Invoice
|
||||||
|
|
||||||
|
// FirstIndexOffset is the index of the first element in the set of
|
||||||
|
// returned Invoices above. Callers can use this to resume their query
|
||||||
|
// in the event that the slice has too many events to fit into a single
|
||||||
|
// response.
|
||||||
|
FirstIndexOffset uint64
|
||||||
|
|
||||||
// LastIndexOffset is the index of the last element in the set of
|
// LastIndexOffset is the index of the last element in the set of
|
||||||
// returned Invoices above. Callers can use this to resume their query
|
// returned Invoices above. Callers can use this to resume their query
|
||||||
// in the event that the time slice has too many events to fit into a
|
// in the event that the slice has too many events to fit into a single
|
||||||
// single response.
|
// response.
|
||||||
LastIndexOffset uint32
|
LastIndexOffset uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryInvoices allows a caller to query the invoice database for invoices
|
// QueryInvoices allows a caller to query the invoice database for invoices
|
||||||
@@ -439,12 +449,6 @@ func (d *DB) QueryInvoices(q InvoiceQuery) (InvoiceSlice, error) {
|
|||||||
InvoiceQuery: q,
|
InvoiceQuery: q,
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the caller provided an index offset, then we'll not know how many
|
|
||||||
// records we need to skip. We'll also keep track of the record offset
|
|
||||||
// as that's part of the final return value.
|
|
||||||
invoicesToSkip := q.IndexOffset
|
|
||||||
invoiceOffset := q.IndexOffset
|
|
||||||
|
|
||||||
err := d.View(func(tx *bolt.Tx) error {
|
err := d.View(func(tx *bolt.Tx) error {
|
||||||
// If the bucket wasn't found, then there aren't any invoices
|
// If the bucket wasn't found, then there aren't any invoices
|
||||||
// within the database yet, so we can simply exit.
|
// within the database yet, so we can simply exit.
|
||||||
@@ -452,38 +456,50 @@ func (d *DB) QueryInvoices(q InvoiceQuery) (InvoiceSlice, error) {
|
|||||||
if invoices == nil {
|
if invoices == nil {
|
||||||
return ErrNoInvoicesCreated
|
return ErrNoInvoicesCreated
|
||||||
}
|
}
|
||||||
invoiceAddedIndex := invoices.Bucket(addIndexBucket)
|
invoiceAddIndex := invoices.Bucket(addIndexBucket)
|
||||||
if invoiceAddedIndex == nil {
|
if invoiceAddIndex == nil {
|
||||||
return ErrNoInvoicesCreated
|
return ErrNoInvoicesCreated
|
||||||
}
|
}
|
||||||
|
|
||||||
// We'll be using a cursor to seek into the database, so we'll
|
// keyForIndex is a helper closure that retrieves the invoice
|
||||||
// populate byte slices that represent the start of the key
|
// key for the given add index of an invoice.
|
||||||
// space we're interested in.
|
keyForIndex := func(c *bolt.Cursor, index uint64) []byte {
|
||||||
var startIndex [8]byte
|
var keyIndex [8]byte
|
||||||
switch q.PendingOnly {
|
byteOrder.PutUint64(keyIndex[:], index)
|
||||||
case true:
|
_, invoiceKey := c.Seek(keyIndex[:])
|
||||||
// We have to start from the beginning so we know
|
return invoiceKey
|
||||||
// how many pending invoices we're skipping.
|
}
|
||||||
byteOrder.PutUint64(startIndex[:], uint64(1))
|
|
||||||
default:
|
// nextKey is a helper closure to determine what the next
|
||||||
// We can seek right to the invoice offset we want
|
// invoice key is when iterating over the invoice add index.
|
||||||
// to start with.
|
nextKey := func(c *bolt.Cursor) ([]byte, []byte) {
|
||||||
invoicesToSkip = 0
|
if q.Reversed {
|
||||||
byteOrder.PutUint64(startIndex[:], uint64(invoiceOffset+1))
|
return c.Prev()
|
||||||
|
}
|
||||||
|
return c.Next()
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll be using a cursor to seek into the database and return
|
||||||
|
// a slice of invoices. We'll need to determine where to start
|
||||||
|
// our cursor depending on the parameters set within the query.
|
||||||
|
c := invoiceAddIndex.Cursor()
|
||||||
|
invoiceKey := keyForIndex(c, q.IndexOffset+1)
|
||||||
|
if q.Reversed {
|
||||||
|
_, invoiceKey = c.Last()
|
||||||
|
if q.IndexOffset != 0 {
|
||||||
|
invoiceKey = keyForIndex(c, q.IndexOffset-1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we know that a set of invoices exists, then we'll begin
|
// If we know that a set of invoices exists, then we'll begin
|
||||||
// our seek through the bucket in order to satisfy the query.
|
// our seek through the bucket in order to satisfy the query.
|
||||||
// We'll continue until either we reach the end of the range,
|
// We'll continue until either we reach the end of the range, or
|
||||||
// or reach our max number of events.
|
// reach our max number of invoices.
|
||||||
cursor := invoiceAddedIndex.Cursor()
|
for ; invoiceKey != nil; _, invoiceKey = nextKey(c) {
|
||||||
_, invoiceKey := cursor.Seek(startIndex[:])
|
|
||||||
for ; invoiceKey != nil; _, invoiceKey = cursor.Next() {
|
|
||||||
// If our current return payload exceeds the max number
|
// If our current return payload exceeds the max number
|
||||||
// of invoices, then we'll exit now.
|
// of invoices, then we'll exit now.
|
||||||
if uint32(len(resp.Invoices)) >= q.NumMaxInvoices {
|
if uint64(len(resp.Invoices)) >= q.NumMaxInvoices {
|
||||||
return nil
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
invoice, err := fetchInvoice(invoiceKey, invoices)
|
invoice, err := fetchInvoice(invoiceKey, invoices)
|
||||||
@@ -496,17 +512,22 @@ func (d *DB) QueryInvoices(q InvoiceQuery) (InvoiceSlice, error) {
|
|||||||
if q.PendingOnly && invoice.Terms.Settled {
|
if q.PendingOnly && invoice.Terms.Settled {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// If we're not yet past the user defined offset, then
|
|
||||||
// we'll continue to seek forward.
|
|
||||||
if invoicesToSkip > 0 {
|
|
||||||
invoicesToSkip--
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// At this point, we've exhausted the offset, so we'll
|
// At this point, we've exhausted the offset, so we'll
|
||||||
// begin collecting invoices found within the range.
|
// begin collecting invoices found within the range.
|
||||||
resp.Invoices = append(resp.Invoices, &invoice)
|
resp.Invoices = append(resp.Invoices, invoice)
|
||||||
invoiceOffset++
|
}
|
||||||
|
|
||||||
|
// If we iterated through the add index in reverse order, then
|
||||||
|
// we'll need to reverse the slice of invoices to return them in
|
||||||
|
// forward order.
|
||||||
|
if q.Reversed {
|
||||||
|
numInvoices := len(resp.Invoices)
|
||||||
|
for i := 0; i < numInvoices/2; i++ {
|
||||||
|
opposite := numInvoices - i - 1
|
||||||
|
resp.Invoices[i], resp.Invoices[opposite] =
|
||||||
|
resp.Invoices[opposite], resp.Invoices[i]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -515,9 +536,12 @@ func (d *DB) QueryInvoices(q InvoiceQuery) (InvoiceSlice, error) {
|
|||||||
return resp, err
|
return resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally, record the index of the last invoice added so that the
|
// Finally, record the indexes of the first and last invoices returned
|
||||||
// caller can resume from this point later on.
|
// so that the caller can resume from this point later on.
|
||||||
resp.LastIndexOffset = invoiceOffset
|
if len(resp.Invoices) > 0 {
|
||||||
|
resp.FirstIndexOffset = resp.Invoices[0].AddIndex
|
||||||
|
resp.LastIndexOffset = resp.Invoices[len(resp.Invoices)-1].AddIndex
|
||||||
|
}
|
||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user