Core
Collections
A collection is a group of documents inside a database. Most of the driver's API lives on SinterCollection.
Get a collection
db.collection() returns a handle.
Give it your document type, and filters, updates and results are type-checked
against it.
import { type CustomId, SinterClient } from "sinterdb";
interface User {
_id: CustomId;
email: string;
name: string;
age: number;
tags: string[];
}
const client = new SinterClient("sinterdb://localhost/my-app");
await client.connect();
const users = client.db().collection<User>("users");No request is sent. Like databases, a collection is created by the first write: an insert, or createIndex. Until then, reads return no documents rather than an error.
The type parameter is checked by TypeScript only. The server does not validate documents against a schema, so a document written without types, or by another program, can have any shape.
Counting
const total = await users.estimatedDocumentCount();
const adults = await users.countDocuments({ age: { $gte: 18 } });| Method | What it counts |
|---|---|
countDocuments(filter) | Documents that match the filter, counted on the server. |
estimatedDocumentCount() | Every document, from the collection's size, without a scan. |
Neither transfers documents. Without a filter, countDocuments() counts the whole collection.
Distinct values
distinct(field, filter) returns each
value of a field once, among the documents that match the filter.
const ages = await users.distinct("age");
// [17, 25, 31]
const adultTags = await users.distinct("tags", { age: { $gte: 18 } });
// ["admin", "beta"]- A field that holds an array contributes its elements, so
tagsreturns tags, not arrays of tags. - Documents without the field contribute nothing. A field holding
nullcontributesnull. - Values come back in sort order, and are distinct by exact equality, so
1and1nare two values.
Indexes
An index lets the server find matching documents without reading the whole collection. Every collection has a unique index on _id. Index the fields you filter on often.
await users.createIndex({ field: "email", unique: true });
await users.createIndex({
keys: [
["name", 1],
["age", -1],
],
});
const indexes = await users.indexes();
await users.dropIndex("name_1_age_-1");| Option | Meaning |
|---|---|
field | One field to index. Dotted paths reach into nested documents. |
keys | One to eight [field, direction] pairs for a compound index. |
unique | At most one document may hold a given value, or combination of values. |
sparse | Documents without the field are left out of the index. |
name | Defaults to the fields and directions, such as email_1. |
Give either field or keys, not both. Creating an identical index again succeeds and reports created: false. A unique index fails with a DuplicateKey server error if existing documents already break it.
An index can serve equality, $in, and range filters. It never changes what a query returns, only how fast. Sorting is not served by indexes. The full rules, including how compound indexes are chosen, are in indexes.md.
Names
Collection names must not be empty, must not contain null characters, and can be at most 255 bytes. Unlike database names, they may contain / and \.
You cannot drop or rename a collection yet. deleteMany({}) removes every document and leaves the empty collection, with its indexes.