2019-01-24 20:15:10 +00:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
2018-09-05 17:10:35 +01:00
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package storage
|
|
|
|
|
2018-09-07 15:20:15 +01:00
|
|
|
import (
|
2019-06-05 15:23:10 +01:00
|
|
|
"context"
|
2018-09-07 15:20:15 +01:00
|
|
|
)
|
2018-09-05 17:10:35 +01:00
|
|
|
|
2020-02-17 07:56:13 +00:00
|
|
|
// ListOptions are items that are optional for the LIST method.
|
2018-09-05 17:10:35 +01:00
|
|
|
type ListOptions struct {
|
|
|
|
Prefix Key
|
|
|
|
StartAfter Key // StartAfter is relative to Prefix
|
|
|
|
Recursive bool
|
|
|
|
IncludeValue bool
|
2018-09-07 15:20:15 +01:00
|
|
|
Limit int
|
2018-09-05 17:10:35 +01:00
|
|
|
}
|
|
|
|
|
2020-02-17 07:56:13 +00:00
|
|
|
// ListV2 lists all keys corresponding to ListOptions.
|
|
|
|
// limit is capped to LookupLimit.
|
2018-09-07 15:20:15 +01:00
|
|
|
//
|
|
|
|
// more indicates if the result was truncated. If false
|
|
|
|
// then the result []ListItem includes all requested keys.
|
|
|
|
// If true then the caller must call List again to get more
|
2019-09-25 22:30:41 +01:00
|
|
|
// results by setting `StartAfter` appropriately.
|
2019-06-05 15:23:10 +01:00
|
|
|
func ListV2(ctx context.Context, store KeyValueStore, opts ListOptions) (result Items, more bool, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
2018-09-05 17:10:35 +01:00
|
|
|
|
|
|
|
limit := opts.Limit
|
2020-01-22 19:00:46 +00:00
|
|
|
if limit <= 0 || limit > store.LookupLimit() {
|
|
|
|
limit = store.LookupLimit()
|
2018-09-05 17:10:35 +01:00
|
|
|
}
|
|
|
|
|
2018-09-07 15:20:15 +01:00
|
|
|
more = true
|
2018-09-05 17:10:35 +01:00
|
|
|
|
2019-09-25 22:30:41 +01:00
|
|
|
first := opts.StartAfter
|
2019-06-05 15:23:10 +01:00
|
|
|
iterate := func(ctx context.Context, it Iterator) error {
|
2018-09-05 17:10:35 +01:00
|
|
|
var item ListItem
|
|
|
|
skipFirst := true
|
|
|
|
for ; limit > 0; limit-- {
|
2019-06-05 15:23:10 +01:00
|
|
|
if !it.Next(ctx, &item) {
|
2018-09-05 17:10:35 +01:00
|
|
|
more = false
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
relativeKey := item.Key[len(opts.Prefix):]
|
|
|
|
if skipFirst {
|
|
|
|
skipFirst = false
|
|
|
|
if relativeKey.Equal(first) {
|
|
|
|
// skip the first element in iteration
|
|
|
|
// if it matches the search key
|
|
|
|
limit++
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-28 01:39:49 +00:00
|
|
|
task := mon.TaskNamed("appending_to_results")(nil)
|
2018-09-05 17:10:35 +01:00
|
|
|
if opts.IncludeValue {
|
|
|
|
result = append(result, ListItem{
|
|
|
|
Key: CloneKey(relativeKey),
|
|
|
|
Value: CloneValue(item.Value),
|
|
|
|
IsPrefix: item.IsPrefix,
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
result = append(result, ListItem{
|
|
|
|
Key: CloneKey(relativeKey),
|
|
|
|
IsPrefix: item.IsPrefix,
|
|
|
|
})
|
|
|
|
}
|
2020-01-28 01:39:49 +00:00
|
|
|
task(nil)
|
2018-09-05 17:10:35 +01:00
|
|
|
}
|
2018-09-07 15:20:15 +01:00
|
|
|
|
|
|
|
// we still need to consume one item for the more flag
|
2019-06-05 15:23:10 +01:00
|
|
|
more = it.Next(ctx, &item)
|
2018-09-05 17:10:35 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var firstFull Key
|
2019-09-25 22:30:41 +01:00
|
|
|
if !opts.StartAfter.IsZero() {
|
2018-09-05 17:10:35 +01:00
|
|
|
firstFull = joinKey(opts.Prefix, opts.StartAfter)
|
|
|
|
}
|
2019-06-05 15:23:10 +01:00
|
|
|
err = store.Iterate(ctx, IterateOptions{
|
2018-09-05 17:10:35 +01:00
|
|
|
Prefix: opts.Prefix,
|
|
|
|
First: firstFull,
|
|
|
|
Recurse: opts.Recursive,
|
2020-01-19 19:56:51 +00:00
|
|
|
Limit: limit,
|
2018-09-05 17:10:35 +01:00
|
|
|
}, iterate)
|
|
|
|
|
|
|
|
return result, more, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func joinKey(a, b Key) Key {
|
|
|
|
return append(append(Key{}, a...), b...)
|
|
|
|
}
|