src/server/pkg/collection/collection.go
func (c *readonlyCollection) ListPrefix(prefix string, val proto.Message, opts *Options, f func(string) error) error {
	queryPrefix := c.prefix
	if prefix != "" {
		// If we always call join, we'll get rid of the trailing slash we need
		// on the root c.prefix
		queryPrefix = filepath.Join(c.prefix, prefix)
	}
	return c.list(queryPrefix, &c.limit, opts, func(kv *mvccpb.KeyValue) error {
		if err := proto.Unmarshal(kv.Value, val); err != nil {
			return err
		}
		return f(strings.TrimPrefix(string(kv.Key), queryPrefix))
	})
}
func (c *readonlyCollection) ListPrefix(prefix string, val proto.Message, opts *Options, f func(string) error) error {
	queryPrefix := c.prefix
	if prefix != "" {
		// If we always call join, we'll get rid of the trailing slash we need
		// on the root c.prefix
		queryPrefix = filepath.Join(c.prefix, prefix)
	}
	return c.list(queryPrefix, &c.limit, opts, func(kv *mvccpb.KeyValue) error {
		if err := proto.Unmarshal(kv.Value, val); err != nil {
			return err
		}
		return f(strings.TrimPrefix(string(kv.Key), queryPrefix))
	})
}
// ListPrefix returns keys (and values) that begin with prefix, f will be
// called with each key, val will contain the value for the key.
// You can break out of iteration by returning errutil.ErrBreak.