ELIPS uses five nouns: database, collection, record, document, and lineage. They map directly to types in both the C++ and Python surfaces.
Database
Database (created via elips.Client / elips.PersistentClient) is the database handle. It owns the configured text embedder, the collection registry, the WAL, and any optional GPU device state. Create one with elips.Client(dimension=384) for an in-memory database or elips.PersistentClient(path, dimension=384) on disk; the handle is non-copyable.
Collection
A collection is a named partition. Inside a collection, ids are unique, and records share the database's dimension and metric. A collection owns exactly one index instance (a FAISS-style Index) plus its metadata index and planner.
docs = client.get_or_create_collection("documents")
faces = client.get_or_create_collection("faces")Record
The record is the unit of storage. Every field except key and vector is optional:
struct Record {
RecordID key;
Vector vector;
Payload meta; // typed metadata
std::optional<DocumentAttachment> document; // raw text + uri + mime
std::optional<ChunkInfo> chunk; // document key + position
std::optional<EmbeddingLineage> lineage; // provider · model · revision
};Documents & lineage
DocumentAttachment stores the original text and optional URI/MIME. ChunkInfo records where the record sits inside a larger document. EmbeddingLineage records which embedder produced the vector, so future migrations or audits can reason about provenance. All three are persisted by WAL::insert_ex and replayed on recovery.
Filters
Metadata filters are expressed as a where dict with Chroma-style operators. The same predicate tree is built from this dict and by the EQL parser; the executor cannot tell which produced it. Equality and set-membership predicates accelerate through MetadataIndex; other comparators evaluate during the scan.
where = {"kind": "design", "year": {"$gt": 2023}}
rows = docs.get(where=where)Query plan
Every vector or hybrid query first goes through the collection planner (Collection::plan_query()). The result is a QueryPlan that names the strategy (ann_index, exact_candidates, full_scan, text_probe, or hybrid_fusion), whether MetadataIndex was used, and the candidate count when relevant. The plan is exposed in both surfaces via explain_query / Collection.explain() — see Advanced patterns.