Skip to main content

Collection

The Collection class provides a type-safe, high-level API for working with documents in a specific collection. Collections automatically handle transactions and provide rich querying capabilities.

Getting a collection

Collections are obtained using db.getCollection() with a type parameter:
import { Database, Document } from '@sohzm/jasonisnthappy';

interface User extends Document {
  _id: string;
  name: string;
  email: string;
  age: number;
  active: boolean;
}

const db = Database.open('./my-database.db');
const users = db.getCollection<User>('users');

Collection info

name()

Returns the collection name.
name(): string
name
string
The collection name
Example
const users = db.getCollection<User>('users');
console.log(users.name()); // 'users'

Basic CRUD operations

insert()

Inserts a document into the collection.
insert(doc: Omit<T, '_id'>): string
doc
Omit<T, '_id'>
required
Document to insert (without _id field)
id
string
The auto-generated _id of the inserted document
Example
const userId = users.insert({
  name: 'Alice',
  email: '[email protected]',
  age: 30,
  active: true
});

console.log(`User created with ID: ${userId}`);

findById()

Finds a document by its ID.
findById(id: string): T | null
id
string
required
Document ID to find
document
T | null
The document if found, null otherwise
Example
const user = users.findById('user123');

if (user) {
  console.log(`Found: ${user.name} (${user.email})`);
} else {
  console.log('User not found');
}

updateById()

Updates a document by its ID.
updateById(id: string, updates: Partial<T>): void
id
string
required
Document ID to update
updates
Partial<T>
required
Partial document with fields to update
Example
users.updateById('user123', {
  age: 31,
  email: '[email protected]'
});

deleteById()

Deletes a document by its ID.
deleteById(id: string): void
id
string
required
Document ID to delete
Example
users.deleteById('user123');
console.log('User deleted');

findAll()

Retrieves all documents in the collection.
findAll(): T[]
documents
T[]
Array of all documents
Example
const allUsers = users.findAll();
console.log(`Total users: ${allUsers.length}`);

count()

Counts all documents in the collection.
count(): number
count
number
Number of documents
Example
const total = users.count();
console.log(`Total users: ${total}`);

Query operations

find()

Finds all documents matching a JMESPath filter.
find(filter: string): T[]
filter
string
required
JMESPath query expression
documents
T[]
Array of matching documents
Example
// Find active users over 25
const activeAdults = users.find('[?active == `true` && age > `25`]');

// Find users with specific email domain
const companyUsers = users.find('[?ends_with(email, `@company.com`)]');

findOne()

Finds the first document matching a filter.
findOne(filter: string): T | null
filter
string
required
JMESPath query expression
document
T | null
The first matching document, or null if none found
Example
const user = users.findOne('[?email == `[email protected]`]');

if (user) {
  console.log(`Found user: ${user.name}`);
}

update()

Updates all documents matching a filter.
update(filter: string, updates: Partial<T>): number
filter
string
required
JMESPath query expression
updates
Partial<T>
required
Fields to update
count
number
Number of documents updated
Example
// Deactivate all users over 65
const updated = users.update('[?age > `65`]', { active: false });
console.log(`Deactivated ${updated} users`);

updateOne()

Updates the first document matching a filter.
updateOne(filter: string, updates: Partial<T>): boolean
filter
string
required
JMESPath query expression
updates
Partial<T>
required
Fields to update
updated
boolean
true if a document was updated, false otherwise
Example
const updated = users.updateOne(
  '[?email == `[email protected]`]',
  { active: false }
);

if (updated) {
  console.log('User updated');
}

delete()

Deletes all documents matching a filter.
delete(filter: string): number
filter
string
required
JMESPath query expression
count
number
Number of documents deleted
Example
// Delete inactive users
const deleted = users.delete('[?active == `false`]');
console.log(`Deleted ${deleted} inactive users`);

deleteOne()

Deletes the first document matching a filter.
deleteOne(filter: string): boolean
filter
string
required
JMESPath query expression
deleted
boolean
true if a document was deleted, false otherwise
Example
const deleted = users.deleteOne('[?email == `[email protected]`]');

Upsert operations

upsertById()

Inserts or updates a document by ID.
upsertById(id: string, doc: Omit<T, '_id'>): UpsertResult
id
string
required
Document ID
doc
Omit<T, '_id'>
required
Document data (without _id)
result
UpsertResult
Result indicating whether document was inserted or updated
Example
const result = users.upsertById('user123', {
  name: 'Alice',
  email: '[email protected]',
  age: 30,
  active: true
});

if (result.inserted) {
  console.log('New user created');
} else {
  console.log('Existing user updated');
}

upsert()

Inserts or updates the first document matching a filter.
upsert(filter: string, doc: Omit<T, '_id'>): UpsertResult
filter
string
required
JMESPath query expression
doc
Omit<T, '_id'>
required
Document data (without _id)
result
UpsertResult
Result indicating whether document was inserted or updated
Example
const result = users.upsert(
  '[?email == `[email protected]`]',
  {
    name: 'Bob',
    email: '[email protected]',
    age: 25,
    active: true
  }
);

Bulk operations

insertMany()

Inserts multiple documents in a single operation.
insertMany(docs: Omit<T, '_id'>[]): string[]
docs
Omit<T, '_id'>[]
required
Array of documents to insert
ids
string[]
Array of generated document IDs
Example
const ids = users.insertMany([
  { name: 'Alice', email: '[email protected]', age: 30, active: true },
  { name: 'Bob', email: '[email protected]', age: 25, active: true },
  { name: 'Carol', email: '[email protected]', age: 35, active: false }
]);

console.log(`Inserted ${ids.length} users`);

bulkWrite()

Executes multiple mixed operations (insert, update, delete) in one call.
bulkWrite(
  operations: BulkOperation<Omit<T, '_id'>>[],
  ordered?: boolean
): BulkWriteResult
operations
BulkOperation<Omit<T, '_id'>>[]
required
Array of bulk operations to execute
ordered
boolean
If true, stops on first error. If false, continues on errors. Default: true
result
BulkWriteResult
Statistics about the bulk operation
Example
import { BulkOperation } from '@sohzm/jasonisnthappy';

const operations: BulkOperation<Omit<User, '_id'>>[] = [
  {
    op: 'insert',
    doc: { name: 'Alice', email: '[email protected]', age: 30, active: true }
  },
  {
    op: 'update_one',
    filter: '[?email == `[email protected]`]',
    update: { age: 26 }
  },
  {
    op: 'delete_many',
    filter: '[?active == `false`]'
  }
];

const result = users.bulkWrite(operations);

console.log(`Inserted: ${result.inserted_count}`);
console.log(`Updated: ${result.updated_count}`);
console.log(`Deleted: ${result.deleted_count}`);

if (result.errors.length > 0) {
  console.log('Errors:', result.errors);
}

Advanced queries

queryWithOptions()

Performs a query with sorting, pagination, and field projection.
queryWithOptions(
  filter?: string,
  sortField?: keyof T & string,
  sortAsc?: boolean,
  limit?: number,
  skip?: number,
  projectFields?: (keyof T & string)[],
  excludeFields?: (keyof T & string)[]
): T[]
filter
string
JMESPath query expression (optional)
sortField
keyof T & string
Field to sort by
sortAsc
boolean
Sort ascending if true, descending if false
limit
number
Maximum number of documents to return
skip
number
Number of documents to skip
projectFields
(keyof T & string)[]
Fields to include in results
excludeFields
(keyof T & string)[]
Fields to exclude from results
documents
T[]
Array of matching documents
Example
// Get top 10 active users sorted by age (descending), skip first 20
const topUsers = users.queryWithOptions(
  '[?active == `true`]',
  'age',
  false,
  10,
  20
);

// Get users with only name and email fields
const basicInfo = users.queryWithOptions(
  undefined,
  undefined,
  undefined,
  undefined,
  undefined,
  ['name', 'email']
);

queryCount()

Counts documents matching a query with pagination.
queryCount(filter?: string, skip?: number, limit?: number): number
filter
string
JMESPath query expression (optional)
skip
number
Number of documents to skip
limit
number
Maximum number of documents to count
count
number
Number of matching documents

queryFirst()

Returns the first document matching a query with optional sorting.
queryFirst(
  filter?: string,
  sortField?: keyof T & string,
  sortAsc?: boolean
): T | null
filter
string
JMESPath query expression (optional)
sortField
keyof T & string
Field to sort by
sortAsc
boolean
Sort ascending if true, descending if false
document
T | null
The first matching document, or null if none found
Example
// Get oldest active user
const oldest = users.queryFirst(
  '[?active == `true`]',
  'age',
  false
);

distinct()

Returns unique values for a field.
distinct<K extends keyof T>(field: K): T[K][]
field
K
required
Field name to get distinct values for
values
T[K][]
Array of unique values
Example
// Get all unique ages
const ages = users.distinct('age');
console.log('Unique ages:', ages);

// Get all unique email domains (assuming processing)
const emails = users.distinct('email');

countDistinct()

Counts unique values for a field.
countDistinct(field: keyof T): number
field
keyof T
required
Field name
count
number
Number of distinct values
Example
const uniqueAges = users.countDistinct('age');
console.log(`${uniqueAges} different ages`);

countWithQuery()

Counts documents matching a filter.
countWithQuery(filter?: string): number
filter
string
JMESPath query expression (optional)
count
number
Number of matching documents
Example
const activeCount = users.countWithQuery('[?active == `true`]');
console.log(`Active users: ${activeCount}`);
Performs full-text search on indexed fields.
search(query: string): SearchResult[]
query
string
required
Search query string
results
SearchResult[]
Array of search results with scores
Example
// First, create a text index
db.createTextIndex('users', 'name_text_idx', 'name');

// Then search
const results = users.search('alice');

for (const result of results) {
  const user = users.findById(result.doc_id);
  console.log(`${user.name} (score: ${result.score})`);
}

Aggregation

aggregate()

Runs an aggregation pipeline on the collection.
aggregate<R>(pipeline: AggregationStage[]): R[]
pipeline
AggregationStage[]
required
Array of aggregation stages
results
R[]
Array of aggregation results (shape depends on pipeline)
Example
// Count users by age group
const ageGroups = users.aggregate<{ age: number; count: number }>([
  { match: '[?active == `true`]' },
  { group_by: 'age' },
  { count: 'count' },
  { sort: { field: 'age', asc: true } }
]);

console.log(ageGroups);
// [{ age: 25, count: 5 }, { age: 30, count: 3 }, ...]

// Calculate average age by activity status
const avgAges = users.aggregate<{ active: boolean; avg_age: number }>([
  { group_by: 'active' },
  { avg: { field: 'age', output: 'avg_age' } }
]);
See the Types reference for all available aggregation stages.

Change tracking

watch()

Watches for changes to documents in the collection.
watch(filter: string | undefined, callback: WatchCallback<T>): WatchHandle
filter
string | undefined
required
JMESPath filter to watch specific documents, or undefined to watch all
callback
WatchCallback<T>
required
Function called when changes occurCallback signature:
(operation: 'insert' | 'update' | 'delete', docId: string, document: T | null) => void
handle
WatchHandle
Handle to stop watching
Example
// Watch all user changes
const handle = users.watch(undefined, (op, docId, doc) => {
  console.log(`${op}: ${docId}`);
  if (doc) {
    console.log('New data:', doc);
  }
});

// Watch only active users
const activeHandle = users.watch(
  '[?active == `true`]',
  (op, docId, doc) => {
    console.log(`Active user ${op}: ${docId}`);
  }
);

// Stop watching later
handle.stop();
activeHandle.stop();

Build docs developers (and LLMs) love