Advanced Usage Guide

This guide covers advanced usage patterns and techniques for NebulaDB.

Optimizing Performance

Indexing

NebulaDB now includes a powerful built-in indexing system with B-tree implementation for efficient queries:

const users = db.collection('users', {
  indexes: [
    { name: 'email_idx', fields: ['email'], type: 'unique' },
    { name: 'age_idx', fields: ['age'], type: 'single' },
    { name: 'name_age_idx', fields: ['name', 'age'], type: 'compound' },
    {
      name: 'active_users_idx',
      fields: ['lastActive'], type: 'single',
      options: { partial: { filter: { active: true } } }
    },
    {
      name: 'session_expiry_idx',
      fields: ['createdAt'], type: 'single',
      options: { expireAfterSeconds: 3600 }
    }
  ]
});

Batch Operations

// Instead of: for (const item of items) { await collection.insert(item); }
// Use built-in batch operations:
await collection.insertBatch(items);

// Batch updates
await collection.updateBatch(
  [{ id: '1' }, { id: '2' }, { id: '3' }],
  [{ $set: { processed: true } }, { $set: { processed: false } }, { $inc: { count: 1 } }]
);

// Batch deletes
await collection.deleteBatch([{ id: '1' }, { id: '2' }]);

Working with Relationships

One-to-Many Relationships

const users = db.collection('users');
const posts = db.collection('posts');

const user = await users.insert({ name: 'Alice', email: 'alice@example.com' });
await posts.insert({ title: 'First Post', content: 'Hello world!', userId: user.id });
await posts.insert({ title: 'Second Post', content: 'Another post', userId: user.id });

const userPosts = await posts.find({ userId: user.id });

Many-to-Many Relationships

const users = db.collection('users');
const tags = db.collection('tags');
const userTags = db.collection('userTags');

const alice = await users.insert({ name: 'Alice' });
const bob = await users.insert({ name: 'Bob' });
const tagDev = await tags.insert({ name: 'developer' });
const tagAdmin = await tags.insert({ name: 'admin' });

await userTags.insert({ userId: alice.id, tagId: tagDev.id });
await userTags.insert({ userId: alice.id, tagId: tagAdmin.id });
await userTags.insert({ userId: bob.id, tagId: tagDev.id });

async function getUsersByTag(tagId) {
  const relationships = await userTags.find({ tagId });
  const userIds = relationships.map(rel => rel.userId);
  return await users.find({ id: { $in: userIds } });
}

Transactions

async function transaction(operations) {
  const snapshot = {};
  for (const [collectionName, collection] of db.collections.entries()) {
    snapshot[collectionName] = collection.getAll();
  }
  try {
    const result = await operations();
    await db.save();
    return result;
  } catch (error) {
    for (const [collectionName, docs] of Object.entries(snapshot)) {
      db.collection(collectionName).setAll(docs);
    }
    throw error;
  }
}

Migrations

function createMigrationPlugin(migrations) {
  return {
    name: 'migration',
    async onInit(db) {
      const migrationsCollection = db.collection('_migrations');
      const applied = await migrationsCollection.find();
      const appliedVersions = new Set(applied.map(m => m.version));
      const pendingMigrations = migrations
        .filter(m => !appliedVersions.has(m.version))
        .sort((a, b) => a.version - b.version);
      for (const migration of pendingMigrations) {
        console.log(`Applying migration: ${migration.name}`);
        await migration.up(db);
        await migrationsCollection.insert({
          id: `migration-${migration.version}`,
          version: migration.version, name: migration.name,
          appliedAt: new Date().toISOString()
        });
      }
    }
  };
}

Working with Large Datasets

Memory Management

const db = createDb({
  adapter: new MemoryAdapter(),
  compression: { enabled: true, threshold: 1024, level: 6, fields: ['content', 'description'] }
});

await users.processInChunks(async (docs) => {
  for (const doc of docs) { console.log(doc.name); }
  return docs;
}, 500);

Pagination

async function getPage(collection, query, page, pageSize) {
  const allDocs = await collection.find(query);
  const start = (page - 1) * pageSize;
  return allDocs.slice(start, start + pageSize);
}

Streaming

async function* streamCollection(collection, query, batchSize = 100) {
  let page = 1, hasMore = true;
  while (hasMore) {
    const batch = await getPage(collection, query, page, batchSize);
    if (batch.length === 0) { hasMore = false; }
    else { for (const doc of batch) { yield doc; } page++; }
  }
}

Encryption at Rest

import { createDb } from '@nebula/core';
import { FileSystemAdapter } from '@nebula/adapter-filesystem';
import crypto from 'crypto';

class EncryptedFileSystemAdapter extends FileSystemAdapter {
  private encryptionKey: Buffer;
  constructor(filePath: string, encryptionKey: string) {
    super(filePath);
    this.encryptionKey = crypto.scryptSync(encryptionKey, 'salt', 32);
  }
  private encrypt(data: string): string {
    const iv = crypto.randomBytes(16);
    const cipher = crypto.createCipheriv('aes-256-cbc', this.encryptionKey, iv);
    let encrypted = cipher.update(data, 'utf8', 'hex');
    encrypted += cipher.final('hex');
    return iv.toString('hex') + ':' + encrypted;
  }
  private decrypt(data: string): string {
    const [ivHex, encryptedData] = data.split(':');
    const iv = Buffer.from(ivHex, 'hex');
    const decipher = crypto.createDecipheriv('aes-256-cbc', this.encryptionKey, iv);
    let decrypted = decipher.update(encryptedData, 'hex', 'utf8');
    decrypted += decipher.final('utf8');
    return decrypted;
  }
  async save(data: Record<string, any[]>): Promise<void> {
    const encryptedData = this.encrypt(JSON.stringify(data));
    await super.save({ data: encryptedData } as any);
  }
  async load(): Promise<Record<string, any[]>> {
    try {
      const encryptedData = await super.load();
      if (!encryptedData.data) return {};
      return JSON.parse(this.decrypt(encryptedData.data));
    } catch (error) { return {}; }
  }
}

const db = createDb({
  adapter: new EncryptedFileSystemAdapter('data.json', 'your-secret-password')
});