Builder Pattern
Builder Pattern
Section titled “Builder Pattern”The OstrichDB SDK provides an intuitive builder pattern that makes working with the hierarchical data structure natural and chainable. Instead of passing multiple string parameters to methods, the builder pattern lets you construct operations step by step, following the data hierarchy.
Overview
Section titled “Overview”Traditional API vs Builder Pattern
Section titled “Traditional API vs Builder Pattern”Traditional Method-Based API:
// Verbose and error-proneawait db.create_project('ecommerce');await db.create_collection('ecommerce', 'products');await db.create_cluster('ecommerce', 'products', 'electronics');await db.create_record('ecommerce', 'products', 'electronics', 'laptop-name', 'STRING', 'MacBook Pro');
Builder Pattern API:
// Intuitive and chainableconst laptop = db.project('ecommerce') .collection('products') .cluster('electronics') .record('laptop-name', 'STRING', 'MacBook Pro');
await laptop.create('laptop-name', 'STRING', 'MacBook Pro');
Benefits of the Builder Pattern
Section titled “Benefits of the Builder Pattern”- 🔗 Chainable: Natural flow following the data hierarchy
- 📖 Readable: Code reads like the data structure
- 🛡️ Type Safe: Full TypeScript support with auto-completion
- 🚀 Reusable: Build once, use for multiple operations
- 🎯 Intuitive: Mirrors how you think about organizing data
Builder Hierarchy
Section titled “Builder Hierarchy”Understanding the Chain
Section titled “Understanding the Chain”The builder pattern follows OstrichDB’s hierarchy:
OstrichDB → ProjectBuilder → CollectionBuilder → ClusterBuilder → RecordBuilder
Each level provides methods relevant to that level of the hierarchy:
const db = new OstrichDB({ /* config */ });
const project = db.project('my-app'); // ProjectBuilderconst collection = project.collection('user-data'); // CollectionBuilderconst cluster = collection.cluster('active-users'); // ClusterBuilderconst record = cluster.record('user-name', 'STRING', 'John'); // RecordBuilder
Project Builder
Section titled “Project Builder”Creating Projects
Section titled “Creating Projects”const project = db.project('ecommerce-app');
// Create the projectawait project.create();
// Delete the projectawait project.delete();
Project Operations
Section titled “Project Operations”const project = db.project('blog-app');
// List collections in this projectconst collections = await project.listCollections();console.log('Collections:', collections);
// Access a specific collectionconst posts = project.collection('posts');
Project Builder Methods
Section titled “Project Builder Methods”Method | Description | Returns |
---|---|---|
create() | Create the project | Promise<void> |
delete() | Delete the project | Promise<void> |
listCollections() | List collections in project | Promise<Collection[]> |
collection(name) | Get collection builder | CollectionBuilder |
Collection Builder
Section titled “Collection Builder”Creating Collections
Section titled “Creating Collections”const posts = db.project('blog').collection('posts');
// Create the collectionawait posts.create();
// Get collection informationconst info = await posts.get();console.log('Collection info:', info);
// Delete the collectionawait posts.delete();
Collection Operations
Section titled “Collection Operations”const products = db.project('ecommerce').collection('products');
// List all clusters in this collectionconst clusters = await products.listClusters();
// Access specific clustersconst electronics = products.cluster('electronics');const clothing = products.cluster('clothing');const books = products.cluster('books');
Collection Builder Methods
Section titled “Collection Builder Methods”Method | Description | Returns |
---|---|---|
create() | Create the collection | Promise<void> |
delete() | Delete the collection | Promise<void> |
get() | Get collection data | Promise<CollectionData> |
listClusters() | List clusters in collection | Promise<Cluster[]> |
cluster(name) | Get cluster builder | ClusterBuilder |
Cluster Builder
Section titled “Cluster Builder”Creating Clusters
Section titled “Creating Clusters”const activeUsers = db.project('app') .collection('users') .cluster('active');
// Create the clusterawait activeUsers.create();
// Get cluster informationconst clusterData = await activeUsers.get();
// Delete the clusterawait activeUsers.delete();
Cluster Operations
Section titled “Cluster Operations”const electronics = db.project('store') .collection('products') .cluster('electronics');
// List all records in the clusterconst records = await electronics.listRecords();console.log('Records:', records);
// Search for specific recordsconst laptops = await electronics.searchRecords({ search: 'laptop', type: 'STRING', limit: 10});
// Access specific recordsconst laptop = electronics.record('macbook-pro', 'STRING', 'MacBook Pro 16"');
Cluster Builder Methods
Section titled “Cluster Builder Methods”Method | Description | Returns |
---|---|---|
create() | Create the cluster | Promise<void> |
delete() | Delete the cluster | Promise<void> |
get() | Get cluster data | Promise<ClusterData> |
listRecords() | List records in cluster | Promise<Record[]> |
searchRecords(options) | Search records | Promise<Record[]> |
record(name, type, value) | Get record builder | RecordBuilder |
Record Builder
Section titled “Record Builder”Creating Records
Section titled “Creating Records”const userName = db.project('app') .collection('users') .cluster('active') .record('user-123-name', 'STRING', 'John Doe');
// Create the recordawait userName.create('user-123-name', 'STRING', 'John Doe');
// Get the record valueconst record = await userName.get('user-123-name');console.log('User name:', record.value);
// Delete the recordawait userName.delete('user-123-name');
Record Builder Methods
Section titled “Record Builder Methods”Method | Description | Returns |
---|---|---|
create(name, type, value) | Create the record | Promise<void> |
get(identifier) | Get record by name/ID | Promise<Record> |
delete(name) | Delete the record | Promise<void> |
Practical Examples
Section titled “Practical Examples”E-commerce Product Management
Section titled “E-commerce Product Management”class ProductManager { private db: OstrichDB; private products: ClusterBuilder;
constructor(db: OstrichDB) { this.db = db; this.products = db.project('ecommerce') .collection('inventory') .cluster('products'); }
async addProduct(id: string, name: string, price: number, category: string) { // Create product records using builder pattern await this.products.record(`${id}-name`, 'STRING', name) .create(`${id}-name`, 'STRING', name);
await this.products.record(`${id}-price`, 'FLOAT', price.toString()) .create(`${id}-price`, 'FLOAT', price.toString());
await this.products.record(`${id}-category`, 'STRING', category) .create(`${id}-category`, 'STRING', category);
await this.products.record(`${id}-created`, 'DATETIME', new Date().toISOString()) .create(`${id}-created`, 'DATETIME', new Date().toISOString());
console.log(`Product ${name} added successfully`); }
async getProduct(id: string) { try { const name = await this.products.record(`${id}-name`).get(`${id}-name`); const price = await this.products.record(`${id}-price`).get(`${id}-price`); const category = await this.products.record(`${id}-category`).get(`${id}-category`); const created = await this.products.record(`${id}-created`).get(`${id}-created`);
return { id, name: name.value, price: parseFloat(price.value), category: category.value, created: created.value }; } catch (error) { throw new Error(`Product ${id} not found`); } }
async findProductsByCategory(category: string) { const categoryRecords = await this.products.searchRecords({ search: 'category', valueContains: category, type: 'STRING' });
// Group by product ID const productIds = new Set( categoryRecords.map(record => record.name.split('-')[0]) );
const products = []; for (const id of productIds) { try { const product = await this.getProduct(id); products.push(product); } catch (error) { // Skip incomplete products } }
return products; }
async updateProductPrice(id: string, newPrice: number) { await this.products.record(`${id}-price`, 'FLOAT', newPrice.toString()) .create(`${id}-price`, 'FLOAT', newPrice.toString()); }
async deleteProduct(id: string) { await this.products.record(`${id}-name`).delete(`${id}-name`); await this.products.record(`${id}-price`).delete(`${id}-price`); await this.products.record(`${id}-category`).delete(`${id}-category`); await this.products.record(`${id}-created`).delete(`${id}-created`); }}
// Usageconst productManager = new ProductManager(db);
await productManager.addProduct('prod-001', 'MacBook Pro', 2499.99, 'Electronics');await productManager.addProduct('prod-002', 'iPhone 15', 999.99, 'Electronics');
const laptop = await productManager.getProduct('prod-001');console.log('Laptop:', laptop);
const electronics = await productManager.findProductsByCategory('Electronics');console.log('Electronics:', electronics);
Blog Management System
Section titled “Blog Management System”class BlogManager { private posts: ClusterBuilder; private drafts: ClusterBuilder;
constructor(db: OstrichDB) { const blog = db.project('blog-platform'); this.posts = blog.collection('content').cluster('published'); this.drafts = blog.collection('content').cluster('drafts'); }
async createDraft(title: string, content: string, author: string) { const draftId = `draft-${Date.now()}`; const draft = this.drafts;
// Create draft using builder pattern await draft.record(`${draftId}-title`, 'STRING', title) .create(`${draftId}-title`, 'STRING', title);
await draft.record(`${draftId}-content`, 'STRING', content) .create(`${draftId}-content`, 'STRING', content);
await draft.record(`${draftId}-author`, 'STRING', author) .create(`${draftId}-author`, 'STRING', author);
await draft.record(`${draftId}-created`, 'DATETIME', new Date().toISOString()) .create(`${draftId}-created`, 'DATETIME', new Date().toISOString());
return draftId; }
async publishDraft(draftId: string) { // Get draft data const title = await this.drafts.record(`${draftId}-title`).get(`${draftId}-title`); const content = await this.drafts.record(`${draftId}-content`).get(`${draftId}-content`); const author = await this.drafts.record(`${draftId}-author`).get(`${draftId}-author`);
// Create published post const postId = `post-${Date.now()}`; await this.posts.record(`${postId}-title`, 'STRING', title.value) .create(`${postId}-title`, 'STRING', title.value);
await this.posts.record(`${postId}-content`, 'STRING', content.value) .create(`${postId}-content`, 'STRING', content.value);
await this.posts.record(`${postId}-author`, 'STRING', author.value) .create(`${postId}-author`, 'STRING', author.value);
await this.posts.record(`${postId}-published`, 'DATETIME', new Date().toISOString()) .create(`${postId}-published`, 'DATETIME', new Date().toISOString());
// Delete draft await this.deleteDraft(draftId);
return postId; }
async listPublishedPosts() { const records = await this.posts.listRecords();
// Group by post ID const posts = new Map(); records.forEach(record => { const parts = record.name.split('-'); const postId = parts.slice(0, 2).join('-'); // "post-timestamp" const field = parts.slice(2).join('-'); // remaining parts
if (!posts.has(postId)) { posts.set(postId, { id: postId }); }
posts.get(postId)[field] = record.value; });
return Array.from(posts.values()); }
async searchPosts(query: string) { const titleMatches = await this.posts.searchRecords({ search: 'title', valueContains: query, limit: 20 });
const contentMatches = await this.posts.searchRecords({ search: 'content', valueContains: query, limit: 20 });
// Combine and deduplicate results const allMatches = [...titleMatches, ...contentMatches]; const uniquePostIds = new Set( allMatches.map(record => record.name.split('-').slice(0, 2).join('-')) );
const results = []; for (const postId of uniquePostIds) { try { const post = await this.getPost(postId); results.push(post); } catch (error) { // Skip incomplete posts } }
return results; }
private async getPost(postId: string) { const title = await this.posts.record(`${postId}-title`).get(`${postId}-title`); const content = await this.posts.record(`${postId}-content`).get(`${postId}-content`); const author = await this.posts.record(`${postId}-author`).get(`${postId}-author`); const published = await this.posts.record(`${postId}-published`).get(`${postId}-published`);
return { id: postId, title: title.value, content: content.value, author: author.value, published: published.value }; }
private async deleteDraft(draftId: string) { await this.drafts.record(`${draftId}-title`).delete(`${draftId}-title`); await this.drafts.record(`${draftId}-content`).delete(`${draftId}-content`); await this.drafts.record(`${draftId}-author`).delete(`${draftId}-author`); await this.drafts.record(`${draftId}-created`).delete(`${draftId}-created`); }}
User Management with Builder Pattern
Section titled “User Management with Builder Pattern”class UserManager { private activeUsers: ClusterBuilder; private inactiveUsers: ClusterBuilder;
constructor(db: OstrichDB) { const userSystem = db.project('user-management'); const users = userSystem.collection('users');
this.activeUsers = users.cluster('active'); this.inactiveUsers = users.cluster('inactive'); }
async createUser(userData: { username: string; email: string; age: number; roles: string[]; }) { const userId = `user-${Date.now()}`; const users = this.activeUsers;
// Create user records with proper types await users.record(`${userId}-username`, 'STRING', userData.username) .create(`${userId}-username`, 'STRING', userData.username);
await users.record(`${userId}-email`, 'STRING', userData.email) .create(`${userId}-email`, 'STRING', userData.email);
await users.record(`${userId}-age`, 'INTEGER', userData.age.toString()) .create(`${userId}-age`, 'INTEGER', userData.age.toString());
await users.record(`${userId}-roles`, '[]STRING', JSON.stringify(userData.roles)) .create(`${userId}-roles`, '[]STRING', JSON.stringify(userData.roles));
await users.record(`${userId}-created`, 'DATETIME', new Date().toISOString()) .create(`${userId}-created`, 'DATETIME', new Date().toISOString());
await users.record(`${userId}-active`, 'BOOLEAN', 'true') .create(`${userId}-active`, 'BOOLEAN', 'true');
return userId; }
async getUser(userId: string) { try { const username = await this.activeUsers.record(`${userId}-username`).get(`${userId}-username`); const email = await this.activeUsers.record(`${userId}-email`).get(`${userId}-email`); const age = await this.activeUsers.record(`${userId}-age`).get(`${userId}-age`); const roles = await this.activeUsers.record(`${userId}-roles`).get(`${userId}-roles`); const created = await this.activeUsers.record(`${userId}-created`).get(`${userId}-created`);
return { id: userId, username: username.value, email: email.value, age: parseInt(age.value), roles: JSON.parse(roles.value), created: created.value, active: true }; } catch (error) { // Try inactive users try { const username = await this.inactiveUsers.record(`${userId}-username`).get(`${userId}-username`); const email = await this.inactiveUsers.record(`${userId}-email`).get(`${userId}-email`); const age = await this.inactiveUsers.record(`${userId}-age`).get(`${userId}-age`); const roles = await this.inactiveUsers.record(`${userId}-roles`).get(`${userId}-roles`); const created = await this.inactiveUsers.record(`${userId}-created`).get(`${userId}-created`);
return { id: userId, username: username.value, email: email.value, age: parseInt(age.value), roles: JSON.parse(roles.value), created: created.value, active: false }; } catch (inactiveError) { throw new Error(`User ${userId} not found`); } } }
async deactivateUser(userId: string) { // Get user data from active cluster const user = await this.getUser(userId);
if (!user.active) { throw new Error('User is already inactive'); }
// Move to inactive cluster await this.inactiveUsers.record(`${userId}-username`, 'STRING', user.username) .create(`${userId}-username`, 'STRING', user.username); await this.inactiveUsers.record(`${userId}-email`, 'STRING', user.email) .create(`${userId}-email`, 'STRING', user.email); await this.inactiveUsers.record(`${userId}-age`, 'INTEGER', user.age.toString()) .create(`${userId}-age`, 'INTEGER', user.age.toString()); await this.inactiveUsers.record(`${userId}-roles`, '[]STRING', JSON.stringify(user.roles)) .create(`${userId}-roles`, '[]STRING', JSON.stringify(user.roles)); await this.inactiveUsers.record(`${userId}-created`, 'DATETIME', user.created) .create(`${userId}-created`, 'DATETIME', user.created); await this.inactiveUsers.record(`${userId}-deactivated`, 'DATETIME', new Date().toISOString()) .create(`${userId}-deactivated`, 'DATETIME', new Date().toISOString());
// Remove from active cluster await this.activeUsers.record(`${userId}-username`).delete(`${userId}-username`); await this.activeUsers.record(`${userId}-email`).delete(`${userId}-email`); await this.activeUsers.record(`${userId}-age`).delete(`${userId}-age`); await this.activeUsers.record(`${userId}-roles`).delete(`${userId}-roles`); await this.activeUsers.record(`${userId}-created`).delete(`${userId}-created`); await this.activeUsers.record(`${userId}-active`).delete(`${userId}-active`); }
async findUsersByRole(role: string) { const roleRecords = await this.activeUsers.searchRecords({ search: 'roles', valueContains: role, type: '[]STRING' });
const userIds = new Set( roleRecords.map(record => record.name.split('-').slice(0, 2).join('-')) );
const users = []; for (const userId of userIds) { try { const user = await this.getUser(userId); users.push(user); } catch (error) { // Skip users that couldn't be loaded } }
return users; }}
Builder Pattern Best Practices
Section titled “Builder Pattern Best Practices”1. Reuse Builders
Section titled “1. Reuse Builders”Create builders once and reuse them for multiple operations:
// Good - reuse buildersconst users = db.project('app').collection('users').cluster('active');
await users.record('user1-name', 'STRING', 'Alice').create('user1-name', 'STRING', 'Alice');await users.record('user2-name', 'STRING', 'Bob').create('user2-name', 'STRING', 'Bob');await users.record('user3-name', 'STRING', 'Charlie').create('user3-name', 'STRING', 'Charlie');
// Bad - recreate builders each timeawait db.project('app').collection('users').cluster('active') .record('user1-name', 'STRING', 'Alice').create('user1-name', 'STRING', 'Alice');await db.project('app').collection('users').cluster('active') .record('user2-name', 'STRING', 'Bob').create('user2-name', 'STRING', 'Bob');
2. Use Descriptive Names
Section titled “2. Use Descriptive Names”Make your code self-documenting with clear names:
// Good - descriptive namesconst blogPosts = db.project('blog-platform') .collection('content') .cluster('published-posts');
const userProfiles = db.project('social-app') .collection('user-data') .cluster('active-profiles');
// Bad - unclear namesconst data = db.project('app').collection('stuff').cluster('things');
3. Group Related Operations
Section titled “3. Group Related Operations”Organize related operations together:
class OrderManager { private orders: ClusterBuilder;
constructor(db: OstrichDB) { this.orders = db.project('ecommerce') .collection('transactions') .cluster('orders'); }
async createOrder(orderData: any) { const orderId = `order-${Date.now()}`;
// Group all order-related operations await this.orders.record(`${orderId}-customer`, 'STRING', orderData.customer) .create(`${orderId}-customer`, 'STRING', orderData.customer); await this.orders.record(`${orderId}-total`, 'FLOAT', orderData.total.toString()) .create(`${orderId}-total`, 'FLOAT', orderData.total.toString()); await this.orders.record(`${orderId}-items`, '[]STRING', JSON.stringify(orderData.items)) .create(`${orderId}-items`, '[]STRING', JSON.stringify(orderData.items)); await this.orders.record(`${orderId}-status`, 'STRING', 'pending') .create(`${orderId}-status`, 'STRING', 'pending');
return orderId; }}
4. Handle Errors Gracefully
Section titled “4. Handle Errors Gracefully”Implement proper error handling in your builder operations:
async function safeCreateUser(db: OstrichDB, userData: any) { const users = db.project('app').collection('users').cluster('active');
try { const userId = `user-${Date.now()}`;
await users.record(`${userId}-username`, 'STRING', userData.username) .create(`${userId}-username`, 'STRING', userData.username); await users.record(`${userId}-email`, 'STRING', userData.email) .create(`${userId}-email`, 'STRING', userData.email);
return userId; } catch (error) { console.error('Failed to create user:', error); throw error; }}
Performance Optimization
Section titled “Performance Optimization”Connection Reuse
Section titled “Connection Reuse”The builder pattern inherently promotes connection reuse:
// Efficient - same underlying connectionconst app = db.project('my-app');const users = app.collection('users');const activeUsers = users.cluster('active');const inactiveUsers = users.cluster('inactive');
// Multiple operations use the same connectionawait activeUsers.record('user1', 'STRING', 'Alice').create('user1', 'STRING', 'Alice');await activeUsers.record('user2', 'STRING', 'Bob').create('user2', 'STRING', 'Bob');await inactiveUsers.record('old-user', 'STRING', 'Charlie').create('old-user', 'STRING', 'Charlie');
Batch Operations
Section titled “Batch Operations”Use the builder pattern to organize batch operations:
async function initializeApp(db: OstrichDB) { // Build the structure const app = db.project('new-app'); const users = app.collection('users'); const products = app.collection('products');
// Create structure await app.create(); await users.create(); await products.create();
// Create clusters const activeUsers = users.cluster('active'); const electronics = products.cluster('electronics');
await activeUsers.create(); await electronics.create();
console.log('App structure initialized');}
Next Steps
Section titled “Next Steps”Now that you understand the builder pattern:
- Framework Integration - See how to use builders in real applications
- Error Handling - Learn comprehensive error management
- Configuration - Optimize your setup for different environments
- API Reference - Explore all available methods
The builder pattern makes working with OstrichDB’s hierarchical structure intuitive and maintainable. Use it as your primary method for interacting with the database to write cleaner, more readable code.