Documentation
¶
Overview ¶
Package db provides a multi-layer database abstraction supporting: - User-level SQLite with sqlite-vec for personal data and vector search - Organization-level SQLite for shared tenant data - PostgreSQL with pgvector for scalable deployments - MongoDB/FerretDB for document storage - Hanzo Datastore (ClickHouse) for deep analytics
ZAP protocol driver for the ORM.
ZAP (Zero-Copy App Proto) uses binary encoding over RPC, communicating directly with ZAP-native backends (hanzo/sql, hanzo/kv, hanzo/datastore, hanzo/documentdb). Each backend speaks ZAP natively — no sidecar needed.
Transport is github.com/zap-proto/http: a fasthttp-style request/response exchange carried over ZAP length-prefixed frames (encoded by the pure-Go zap-proto/go runtime). This is the same ZAP-HTTP transport the gateway, ingress, and luxd use — one and only one internal transport. The driver speaks it as a client: each backend op is a POST to a path (/query, /get, /set, /find, …) with a JSON body; the response carries a status and a JSON body. Routing is by address (each backend on its own port; see DefaultPorts) and by path — there is no peer-discovery layer, so the ORM takes no mDNS dependency.
Index ¶
- Variables
- func GenerateID() string
- func LowercaseFirst(s string) string
- func NormalizeOp(op string) string
- func ParseFilterString(s string) (field, op string)
- func ToJSONFieldName(field string) string
- type AfterCreateHook
- type AfterDeleteHook
- type AfterUpdateHook
- type AnalyticsBatch
- type AnalyticsRows
- type AnalyticsStore
- type BeforeCreateHook
- type BeforeDeleteHook
- type BeforeUpdateHook
- type Config
- type Cursor
- type DB
- type DatastoreConfig
- type Entity
- type IsolationLevel
- type Iterator
- type Key
- type Kind
- type Layer
- type Manager
- func (m *Manager) Analytics() AnalyticsStore
- func (m *Manager) Close() error
- func (m *Manager) Org(orgID string) (DB, error)
- func (m *Manager) RegisterOrgDB(orgID string, db DB)
- func (m *Manager) RegisterUserDB(userID string, db DB)
- func (m *Manager) SetAnalyticsStore(store AnalyticsStore)
- func (m *Manager) User(userID string) (DB, error)
- type Model
- func (m *Model) Create(ctx context.Context) error
- func (m *Model) DB() DB
- func (m *Model) Delete(ctx context.Context) error
- func (m *Model) Entity() Kind
- func (m *Model) Exists(ctx context.Context) (bool, error)
- func (m *Model) Get(ctx context.Context) error
- func (m *Model) GetByID(ctx context.Context, id string) error
- func (m *Model) GetID() string
- func (m *Model) GetKind() string
- func (m *Model) GetNamespace() string
- func (m *Model) Init(database DB, entity Kind)
- func (m *Model) IsCreated() bool
- func (m *Model) IsLoaded() bool
- func (m *Model) JSON() ([]byte, error)
- func (m *Model) JSONString() string
- func (m *Model) Key() Key
- func (m *Model) MarkLoaded()
- func (m *Model) ModelQuery() Query
- func (m *Model) MustGet(ctx context.Context)
- func (m *Model) MustGetByID(ctx context.Context, id string)
- func (m *Model) MustPut(ctx context.Context)
- func (m *Model) Put(ctx context.Context) error
- func (m *Model) RunInTransaction(ctx context.Context, fn func(tx Transaction) error) error
- func (m *Model) SetDB(database DB)
- func (m *Model) SetEntity(entity Kind)
- func (m *Model) SetID(id string)
- func (m *Model) SetKey(key Key) error
- func (m *Model) SetKeyFromString(id string) error
- func (m *Model) SetNamespace(ns string)
- func (m *Model) SoftDelete(ctx context.Context) error
- func (m *Model) Update(ctx context.Context) error
- type Query
- type QueryFilter
- type QueryOrder
- type SQLiteConfig
- type SQLiteDB
- func (db *SQLiteDB) AllocateIDs(kind string, parent Key, n int) ([]Key, error)
- func (db *SQLiteDB) Close() error
- func (db *SQLiteDB) CreateIfAbsent(ctx context.Context, key Key, src interface{}) (bool, error)
- func (db *SQLiteDB) Delete(ctx context.Context, key Key) error
- func (db *SQLiteDB) DeleteMulti(ctx context.Context, keys []Key) error
- func (db *SQLiteDB) Get(ctx context.Context, key Key, dst interface{}) error
- func (db *SQLiteDB) GetMulti(ctx context.Context, keys []Key, dst interface{}) error
- func (db *SQLiteDB) NewIncompleteKey(kind string, parent Key) Key
- func (db *SQLiteDB) NewKey(kind string, stringID string, intID int64, parent Key) Key
- func (db *SQLiteDB) Put(ctx context.Context, key Key, src interface{}) (Key, error)
- func (db *SQLiteDB) PutMulti(ctx context.Context, keys []Key, src interface{}) ([]Key, error)
- func (db *SQLiteDB) PutVector(ctx context.Context, kind string, id string, vector []float32, ...) error
- func (db *SQLiteDB) Query(kind string) Query
- func (db *SQLiteDB) RunInTransaction(ctx context.Context, fn func(tx Transaction) error, opts *TransactionOptions) error
- func (db *SQLiteDB) TenantID() string
- func (db *SQLiteDB) TenantType() string
- func (db *SQLiteDB) VectorSearch(ctx context.Context, opts *VectorSearchOptions) ([]VectorResult, error)
- type SQLiteDBConfig
- type SimpleCursor
- type Syncable
- type Transaction
- type TransactionOptions
- type Validator
- type VectorResult
- type VectorSearchOptions
- type ZapBackend
- type ZapConfig
- type ZapDB
- func (z *ZapDB) AllocateIDs(kind string, parent Key, n int) ([]Key, error)
- func (z *ZapDB) Close() error
- func (z *ZapDB) CreateIfAbsent(ctx context.Context, key Key, src interface{}) (bool, error)
- func (z *ZapDB) Delete(ctx context.Context, key Key) error
- func (z *ZapDB) DeleteMulti(ctx context.Context, keys []Key) error
- func (z *ZapDB) Get(ctx context.Context, key Key, dst interface{}) error
- func (z *ZapDB) GetMulti(ctx context.Context, keys []Key, dst interface{}) error
- func (z *ZapDB) NewIncompleteKey(kind string, parent Key) Key
- func (z *ZapDB) NewKey(kind string, stringID string, intID int64, parent Key) Key
- func (z *ZapDB) Put(ctx context.Context, key Key, src interface{}) (Key, error)
- func (z *ZapDB) PutMulti(ctx context.Context, keys []Key, src interface{}) ([]Key, error)
- func (z *ZapDB) PutVector(ctx context.Context, kind string, id string, vector []float32, ...) error
- func (z *ZapDB) Query(kind string) Query
- func (z *ZapDB) RunInTransaction(ctx context.Context, fn func(tx Transaction) error, opts *TransactionOptions) error
- func (z *ZapDB) TenantID() string
- func (z *ZapDB) TenantType() string
- func (z *ZapDB) VectorSearch(ctx context.Context, opts *VectorSearchOptions) ([]VectorResult, error)
Constants ¶
This section is empty.
Variables ¶
var ( // ErrNoSuchEntity is returned when an entity is not found. ErrNoSuchEntity = errors.New("db: no such entity") // ErrInvalidKey is returned when a key is invalid. ErrInvalidKey = errors.New("db: invalid key") // ErrInvalidEntityType is returned when an entity type is invalid. ErrInvalidEntityType = errors.New("db: invalid entity type") // ErrConcurrentModification is returned when optimistic locking fails. ErrConcurrentModification = errors.New("db: concurrent modification") // ErrDatabaseClosed is returned when operating on a closed database. ErrDatabaseClosed = errors.New("db: database closed") // ErrValidationFailed is returned when entity validation fails. ErrValidationFailed = errors.New("db: validation failed") // ErrEntityNotFound aliases ErrNoSuchEntity. ErrEntityNotFound = ErrNoSuchEntity // ErrKindMismatch is returned by CreateIfAbsent when the id is already held // by a row of a DIFFERENT kind. Entity identity is (kind, id); on the SQL // backends the id column is a bare primary key, so two kinds cannot share an // id. That squatting row is invisible to Get (which filters by kind), so // reporting created=false would strand the caller — CreateIfAbsent surfaces // the collision loudly instead. Keep each kind in its own stringID keyspace. ErrKindMismatch = errors.New("db: id held by a different kind") )
var DefaultPorts = map[ZapBackend]int{ ZapSQL: 9651, ZapKV: 9653, ZapDocumentDB: 9654, ZapDatastore: 9655, }
DefaultPorts for each ZAP-native backend.
Functions ¶
func LowercaseFirst ¶
LowercaseFirst lowercases the first character of a string.
func ParseFilterString ¶
ParseFilterString parses "Field=" into field and operator.
func ToJSONFieldName ¶
ToJSONFieldName converts a Go struct field name (PascalCase) to its JSON equivalent (camelCase) by lowercasing the first letter of each path segment. Handles nested paths like "Account.TransactionHash" → "account.transactionHash".
Types ¶
type AfterCreateHook ¶
type AfterCreateHook interface {
AfterCreate() error
}
AfterCreateHook is called after entity creation.
type AfterDeleteHook ¶
type AfterDeleteHook interface {
AfterDelete() error
}
AfterDeleteHook is called after entity deletion.
type AfterUpdateHook ¶
type AfterUpdateHook interface {
AfterUpdate(prev interface{}) error
}
AfterUpdateHook is called after entity update.
type AnalyticsBatch ¶
type AnalyticsBatch interface {
Append(v ...interface{}) error
AppendStruct(v interface{}) error
Flush() error
Send() error
Abort() error
Rows() int
Close() error
}
AnalyticsBatch for bulk inserts into analytics store.
type AnalyticsRows ¶
type AnalyticsRows interface {
Next() bool
Scan(dest ...interface{}) error
ScanStruct(dest interface{}) error
Columns() []string
Close() error
Err() error
}
AnalyticsRows represents analytics query results.
type AnalyticsStore ¶
type AnalyticsStore interface {
Query(ctx context.Context, query string, args ...interface{}) (AnalyticsRows, error)
Select(ctx context.Context, dest interface{}, query string, args ...interface{}) error
Exec(ctx context.Context, query string, args ...interface{}) error
PrepareBatch(ctx context.Context, query string) (AnalyticsBatch, error)
AsyncInsert(ctx context.Context, query string, wait bool, args ...interface{}) error
Close() error
}
AnalyticsStore is the interface for analytics queries (e.g. ClickHouse).
type BeforeCreateHook ¶
type BeforeCreateHook interface {
BeforeCreate() error
}
BeforeCreateHook is called before entity creation.
type BeforeDeleteHook ¶
type BeforeDeleteHook interface {
BeforeDelete() error
}
BeforeDeleteHook is called before entity deletion.
type BeforeUpdateHook ¶
type BeforeUpdateHook interface {
BeforeUpdate(prev interface{}) error
}
BeforeUpdateHook is called before entity update.
type Config ¶
type Config struct {
DataDir string
UserDataDir string
OrgDataDir string
DatastoreDSN string
EnableDatastore bool
EnableVectorSearch bool
VectorDimensions int
SQLite SQLiteConfig
Datastore DatastoreConfig
IsDev bool
}
Config holds database configuration options.
type Cursor ¶
type Cursor interface {
String() string
}
Cursor represents a position in a result set.
func DecodeCursor ¶
DecodeCursor parses a cursor string.
type DB ¶
type DB interface {
// Core operations
Get(ctx context.Context, key Key, dst interface{}) error
Put(ctx context.Context, key Key, src interface{}) (Key, error)
// CreateIfAbsent conditionally inserts src under key with first-writer-wins
// semantics. It returns created=true iff this call inserted the row (key was
// absent); created=false iff a live row already existed under key, which is
// left untouched. Unlike Put — an unconditional upsert — CreateIfAbsent never
// overwrites a live row, so the winner's content is immutable: a caller that
// sees created=false can Get the existing row with no lost update and no
// TOCTOU window.
//
// "Absent" means no live row of the SAME kind. A soft-deleted row (see
// Delete) of the same kind is resurrected as the new content and reported
// created=true, so CreateIfAbsent and Get share one definition of existence.
// Resurrection never changes an existing row's kind.
//
// Existence is scoped to (kind, id). Because the id column is a bare primary
// key on the SQL backends, an id already held by a DIFFERENT kind is a
// keyspace collision: CreateIfAbsent returns ErrKindMismatch rather than a
// silent created=false that Get could not see. Callers must therefore keep
// each kind in its own stringID keyspace. CreateIfAbsent is also exact-match
// on the stringID: "Acme", "acme" and "acme " are distinct ids, so callers
// must normalize (case, trim, Unicode) BEFORE constructing the key.
//
// The write is atomic at the storage layer — SQLite serializes writers and
// the SQL backend applies INSERT ... ON CONFLICT at the row — so for N
// concurrent callers on the same absent key exactly one observes created=true.
// key must be complete with a non-empty id; otherwise ErrInvalidKey.
CreateIfAbsent(ctx context.Context, key Key, src interface{}) (created bool, err error)
Delete(ctx context.Context, key Key) error
// Batch operations
GetMulti(ctx context.Context, keys []Key, dst interface{}) error
PutMulti(ctx context.Context, keys []Key, src interface{}) ([]Key, error)
DeleteMulti(ctx context.Context, keys []Key) error
// Query
Query(kind string) Query
// Vector search
VectorSearch(ctx context.Context, opts *VectorSearchOptions) ([]VectorResult, error)
PutVector(ctx context.Context, kind string, id string, vector []float32, metadata map[string]interface{}) error
// Key management
NewKey(kind string, stringID string, intID int64, parent Key) Key
NewIncompleteKey(kind string, parent Key) Key
AllocateIDs(kind string, parent Key, n int) ([]Key, error)
// Transactions
RunInTransaction(ctx context.Context, fn func(tx Transaction) error, opts *TransactionOptions) error
// Lifecycle
Close() error
// Tenant info
TenantID() string
TenantType() string
}
DB is the main database interface for entity storage.
type DatastoreConfig ¶
type DatastoreConfig struct {
MaxOpenConns int
MaxIdleConns int
ConnMaxLifetime time.Duration
Compression string
QueryTimeout time.Duration
}
DatastoreConfig holds Hanzo Datastore (ClickHouse) configuration.
type Entity ¶
type Entity interface {
Kind() string
}
Entity is the interface that all model entities should implement.
type IsolationLevel ¶
type IsolationLevel int
IsolationLevel represents transaction isolation levels.
const ( IsolationDefault IsolationLevel = iota IsolationReadUncommitted IsolationReadCommitted IsolationRepeatableRead IsolationSerializable )
type Key ¶
type Key interface {
Kind() string
StringID() string
IntID() int64
Parent() Key
Namespace() string
Incomplete() bool
Encode() string
Equal(other Key) bool
}
Key represents a unique identifier for an entity.
type Kind ¶
type Kind interface {
Kind() string
}
Kind interface for entities with a kind/table name.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager is the main entry point for database operations. It manages multiple database layers and provides unified access.
func NewManager ¶
NewManager creates a new database manager.
func (*Manager) Analytics ¶
func (m *Manager) Analytics() AnalyticsStore
Analytics returns the analytics store.
func (*Manager) RegisterOrgDB ¶
RegisterOrgDB registers an organization database.
func (*Manager) RegisterUserDB ¶
RegisterUserDB registers a user database.
func (*Manager) SetAnalyticsStore ¶
func (m *Manager) SetAnalyticsStore(store AnalyticsStore)
SetAnalyticsStore sets the analytics store for the manager.
type Model ¶
type Model struct {
Parent Key `json:"-"`
ID string `json:"id,omitempty"`
CreatedAt time.Time `json:"createdAt,omitempty"`
UpdatedAt time.Time `json:"updatedAt,omitempty"`
Deleted bool `json:"deleted,omitempty"`
Version int64 `json:"version,omitempty"`
Namespace_ string `json:"-"`
Mock bool `json:"-"`
UseStringKey bool `json:"-"`
// contains filtered or unexported fields
}
Model is a base type that provides common functionality for entities. Embed this in your entity structs for non-generic model usage.
func (*Model) GetNamespace ¶
GetNamespace returns the namespace for this entity.
func (*Model) JSONString ¶
JSONString returns the JSON string representation.
func (*Model) ModelQuery ¶
ModelQuery returns a new query for this entity's kind.
func (*Model) MustGetByID ¶
MustGetByID retrieves by ID or panics.
func (*Model) RunInTransaction ¶
RunInTransaction executes a function within a transaction.
func (*Model) SetKeyFromString ¶
SetKeyFromString sets the key from a string ID.
func (*Model) SetNamespace ¶
SetNamespace sets the namespace.
func (*Model) SoftDelete ¶
SoftDelete marks the entity as deleted without removing it.
type Query ¶
type Query interface {
Filter(filterStr string, value interface{}) Query
FilterField(fieldPath string, op string, value interface{}) Query
Order(fieldPath string) Query
OrderDesc(fieldPath string) Query
Limit(limit int) Query
Offset(offset int) Query
Project(fieldNames ...string) Query
Distinct() Query
Ancestor(ancestor Key) Query
GetAll(ctx context.Context, dst interface{}) ([]Key, error)
First(ctx context.Context, dst interface{}) (Key, error)
Count(ctx context.Context) (int, error)
Keys(ctx context.Context) ([]Key, error)
Run(ctx context.Context) Iterator
Start(cursor Cursor) Query
End(cursor Cursor) Query
}
Query provides a fluent interface for querying entities.
type QueryFilter ¶
QueryFilter holds a filter condition.
type QueryOrder ¶
QueryOrder holds an order directive.
type SQLiteConfig ¶
type SQLiteConfig struct {
MaxOpenConns int
MaxIdleConns int
BusyTimeout int
JournalMode string
Synchronous string
CacheSize int
QueryTimeout time.Duration
}
SQLiteConfig holds SQLite-specific configuration.
type SQLiteDB ¶
type SQLiteDB struct {
// contains filtered or unexported fields
}
SQLiteDB implements the DB interface using SQLite.
func AdaptSQLDB ¶ added in v0.6.7
AdaptSQLDB layers the ORM's typed-record model (the `_entities` table) over an already-open *sql.DB the CALLER owns. Use it when a store's file is opened elsewhere — cloud's per-org SQLite, for instance, is opened through one seam (cek-encrypted at rest, single-writer, WAL pragmas) and handed to subsystems as a *sql.DB. AdaptSQLDB lets the ORM manage records IN that file without owning the file: the caller keeps the connection's pragmas, encryption, durability, and Close; the ORM only ensures its schema and marshals records.
The one connection serves both reads and writes (the caller's pool, typically MaxOpenConns(1) for a serialized single writer), serialized by writeMu exactly as NewSQLiteDB serializes against its dedicated write connection. initSchema runs so `_entities` exists; any table the caller created is untouched. Close is a no-op — the connection belongs to the caller, who closes it.
func NewSQLiteDB ¶
func NewSQLiteDB(cfg *SQLiteDBConfig) (*SQLiteDB, error)
NewSQLiteDB creates a new SQLite database connection.
func (*SQLiteDB) AllocateIDs ¶
func (*SQLiteDB) CreateIfAbsent ¶ added in v0.6.6
func (*SQLiteDB) DeleteMulti ¶
func (*SQLiteDB) NewIncompleteKey ¶
func (*SQLiteDB) RunInTransaction ¶
func (db *SQLiteDB) RunInTransaction(ctx context.Context, fn func(tx Transaction) error, opts *TransactionOptions) error
func (*SQLiteDB) TenantType ¶
func (*SQLiteDB) VectorSearch ¶
func (db *SQLiteDB) VectorSearch(ctx context.Context, opts *VectorSearchOptions) ([]VectorResult, error)
type SQLiteDBConfig ¶
type SQLiteDBConfig struct {
Path string
Config SQLiteConfig
EnableVectorSearch bool
VectorDimensions int
TenantID string
TenantType string
}
SQLiteDBConfig holds configuration for a SQLite database.
type SimpleCursor ¶
SimpleCursor is a basic cursor implementation.
func (*SimpleCursor) String ¶
func (c *SimpleCursor) String() string
type Transaction ¶
type Transaction interface {
Get(key Key, dst interface{}) error
Put(key Key, src interface{}) (Key, error)
// CreateIfAbsent is the transaction-scoped conditional insert: the same
// first-writer-wins semantics as DB.CreateIfAbsent, participating in the
// enclosing transaction.
CreateIfAbsent(key Key, src interface{}) (created bool, err error)
Delete(key Key) error
Query(kind string) Query
// GetForUpdate reads the row into dst AND acquires a row-level exclusive
// lock for the duration of the transaction. Concurrent txs that also call
// GetForUpdate on the same key block until this tx commits or rolls back.
// Required for compare-and-swap patterns against a shared row, where SSI
// alone is insufficient because ON CONFLICT DO UPDATE can miss the
// rw-dependency cycle. SQLite honors this via the write mutex it already
// holds; drivers without row-locking treat it as a regular Get.
GetForUpdate(key Key, dst interface{}) error
}
Transaction represents a database transaction.
type TransactionOptions ¶
type TransactionOptions struct {
ReadOnly bool
MaxAttempts int
Isolation IsolationLevel
}
TransactionOptions configures transaction behavior.
type Validator ¶
type Validator interface {
Validate() error
}
Validator interface for entities that support validation.
type VectorResult ¶
VectorResult represents a vector search result.
type VectorSearchOptions ¶
type VectorSearchOptions struct {
Kind string
Vector []float32
Limit int
MinScore float32
Filters map[string]interface{}
}
VectorSearchOptions configures vector similarity search.
type ZapBackend ¶ added in v0.2.0
type ZapBackend int
ZapBackend selects which ZAP-native backend to connect to.
const ( // ZapSQL connects to hanzo/sql (PostgreSQL fork) on port 9651. ZapSQL ZapBackend = iota // ZapDocumentDB connects to hanzo/documentdb (FerretDB fork) on port 9654. // Provides MongoDB-style document semantics over PostgreSQL storage. // Clients who "think mongo" use this; data lives in hanzo/sql. ZapDocumentDB // ZapKV connects to hanzo/kv (Valkey fork) on port 9653. ZapKV // ZapDatastore connects to hanzo/datastore (ClickHouse fork) on port 9655. ZapDatastore )
type ZapConfig ¶ added in v0.2.0
type ZapConfig struct {
// Addr is the backend address (e.g., "localhost:9651").
// If empty, uses DefaultPorts[Backend] on localhost.
Addr string
// Backend selects which ZAP-native backend to connect to.
Backend ZapBackend
// Database is the target database name (for SQL/DocumentDB backends).
Database string
// Collection is the default collection/table for entity storage.
// Defaults to "_entities" for SQL, "entities" for DocumentDB.
Collection string
// QueryTimeout is the per-query timeout (default 30s).
QueryTimeout time.Duration
}
ZapConfig configures a ZAP database connection.
type ZapDB ¶ added in v0.2.0
type ZapDB struct {
// contains filtered or unexported fields
}
ZapDB implements db.DB over the ZAP-HTTP binary protocol.
func NewZapDB ¶ added in v0.2.0
NewZapDB dials a ZAP-native backend and returns a DB implementation. The transport connects lazily on the first operation, so this does not fail when the backend is momentarily unreachable — the first Get/Put surfaces a clear dial error instead.
func (*ZapDB) AllocateIDs ¶ added in v0.2.0
func (*ZapDB) CreateIfAbsent ¶ added in v0.6.6
CreateIfAbsent conditionally inserts src under key, first-writer-wins. See db.DB.CreateIfAbsent for the contract. Dispatch mirrors Put: each ZAP-native backend uses its own conditional-insert primitive (SQL ON CONFLICT, Valkey SET NX, document unique _id). The reply decides created; an unrecognized or error reply returns an error rather than a guessed created value, so a caller never mistakes an upsert or a transport failure for a first-writer win.
Status: the hanzo ZAP backends do not yet expose a zap-proto/http listener (see LLM.md), so these paths are wire-complete but exercised only by the env-gated live integration test, not unit CI. The SQLite backend is the fully-tested reference implementation of the identical contract.
func (*ZapDB) DeleteMulti ¶ added in v0.2.0
func (*ZapDB) NewIncompleteKey ¶ added in v0.2.0
func (*ZapDB) RunInTransaction ¶ added in v0.2.0
func (z *ZapDB) RunInTransaction(ctx context.Context, fn func(tx Transaction) error, opts *TransactionOptions) error
func (*ZapDB) TenantType ¶ added in v0.2.0
func (*ZapDB) VectorSearch ¶ added in v0.2.0
func (z *ZapDB) VectorSearch(ctx context.Context, opts *VectorSearchOptions) ([]VectorResult, error)