CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-nedb

File-based embedded JavaScript database that provides persistent or in-memory data storage with a MongoDB-like API

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

cursor-operations.mddocs/

Cursor Operations

Chainable query interface for sorting, pagination, projection, and result processing with lazy evaluation and MongoDB-style query building.

Capabilities

Cursor Creation

Cursors are automatically created when calling find methods without a callback parameter, enabling method chaining.

/**
 * Cursor creation through find methods without callback
 * @returns {Cursor} Chainable cursor instance
 */
interface CursorCreation {
  find(query: object, projection?: object): Cursor;
  findOne(query: object, projection?: object): Cursor;
  count(query: object): Cursor;
}

/**
 * Cursor interface for chaining operations
 */
interface Cursor {
  limit(limit: number): Cursor;
  skip(skip: number): Cursor;
  sort(sortQuery: object): Cursor;
  projection(projection: object): Cursor;
  exec(callback: function): void;
}

Usage Examples:

const db = new Datastore({ filename: './products.db', autoload: true });

// Create cursor from find (no callback)
const cursor = db.find({ category: 'electronics' });

// Create cursor from findOne
const singleCursor = db.findOne({ sku: 'ABC123' });

// Create cursor from count
const countCursor = db.count({ status: 'active' });

// Cursors are chainable
const chainedCursor = db.find({ price: { $gte: 100 } })
  .sort({ price: 1 })
  .skip(10)
  .limit(5);

Result Limiting

Control the maximum number of documents returned by the query.

/**
 * Limit the number of results returned
 * @param {number} limit - Maximum number of documents to return
 * @returns {Cursor} Cursor instance for chaining
 */
limit(limit);

Usage Examples:

const db = new Datastore({ filename: './products.db', autoload: true });

// Get top 10 products
db.find({})
  .limit(10)
  .exec((err, products) => {
    console.log('Top 10 products:', products);
  });

// Get 5 most expensive products
db.find({})
  .sort({ price: -1 })
  .limit(5)
  .exec((err, products) => {
    console.log('5 most expensive products:', products);
  });

// Pagination - first page (5 items)
db.find({ category: 'books' })
  .sort({ title: 1 })
  .limit(5)
  .exec((err, books) => {
    console.log('First 5 books:', books);
  });

// Single result using limit
db.find({ featured: true })
  .sort({ priority: -1 })
  .limit(1)
  .exec((err, products) => {
    console.log('Top featured product:', products[0]);
  });

Result Skipping

Skip a specified number of documents from the beginning of the result set, useful for pagination.

/**
 * Skip specified number of documents from the beginning
 * @param {number} skip - Number of documents to skip
 * @returns {Cursor} Cursor instance for chaining
 */
skip(skip);

Usage Examples:

const db = new Datastore({ filename: './users.db', autoload: true });

// Pagination - skip first 10 results
db.find({})
  .sort({ createdAt: -1 })
  .skip(10)
  .limit(10)
  .exec((err, users) => {
    console.log('Users 11-20 (page 2):', users);
  });

// Skip first page for second page
const pageSize = 20;
const pageNumber = 2; // 0-indexed: page 2 = third page
const skipCount = pageNumber * pageSize;

db.find({ status: 'active' })
  .sort({ name: 1 })
  .skip(skipCount)
  .limit(pageSize)
  .exec((err, users) => {
    console.log(`Page ${pageNumber + 1} users:`, users);
  });

// Skip recent entries to get older data
db.find({ type: 'log' })
  .sort({ timestamp: -1 })
  .skip(100)  // Skip 100 most recent
  .limit(50)  // Get next 50
  .exec((err, logs) => {
    console.log('Older log entries:', logs);
  });

// Advanced pagination with total count
function paginateUsers(page, pageSize, callback) {
  const skip = page * pageSize;
  
  // Get total count
  db.count({}, (err, total) => {
    if (err) return callback(err);
    
    // Get page data
    db.find({})
      .sort({ name: 1 })
      .skip(skip)
      .limit(pageSize)
      .exec((err, users) => {
        if (err) return callback(err);
        
        callback(null, {
          users: users,
          totalCount: total,
          currentPage: page,
          totalPages: Math.ceil(total / pageSize),
          hasNextPage: (page + 1) * pageSize < total,
          hasPrevPage: page > 0
        });
      });
  });
}

// Usage
paginateUsers(0, 10, (err, result) => {
  if (!err) {
    console.log('Pagination result:', result);
  }
});

Result Sorting

Sort query results by one or multiple fields in ascending or descending order.

/**
 * Sort results by specified fields
 * @param {object} sortQuery - Sort specification object
 * @returns {Cursor} Cursor instance for chaining
 */
sort(sortQuery);

/**
 * Sort specification format
 */
interface SortQuery {
  [fieldName: string]: 1 | -1;  // 1 for ascending, -1 for descending
}

Usage Examples:

const db = new Datastore({ filename: './products.db', autoload: true });

// Sort by single field - ascending
db.find({})
  .sort({ name: 1 })
  .exec((err, products) => {
    console.log('Products sorted by name (A-Z):', products);
  });

// Sort by single field - descending
db.find({})
  .sort({ price: -1 })
  .exec((err, products) => {
    console.log('Products sorted by price (high to low):', products);
  });

// Sort by multiple fields
db.find({})
  .sort({ category: 1, price: -1 })
  .exec((err, products) => {
    console.log('Products sorted by category (A-Z) then price (high to low):', products);
  });

// Sort by nested fields using dot notation
db.find({})
  .sort({ 'details.weight': 1 })
  .exec((err, products) => {
    console.log('Products sorted by weight:', products);
  });

// Sort by date fields
db.find({})
  .sort({ createdAt: -1 })  // Most recent first
  .exec((err, products) => {
    console.log('Products sorted by creation date (newest first):', products);
  });

// Combining sort with other operations
db.find({ category: 'electronics' })
  .sort({ rating: -1, price: 1 })  // Best rated first, then cheapest
  .limit(10)
  .exec((err, products) => {
    console.log('Top 10 electronics by rating and price:', products);
  });

// Custom string comparison (if compareStrings option was set)
const customDb = new Datastore({ 
  filename: './data.db',
  compareStrings: (a, b) => a.localeCompare(b, 'en', { sensitivity: 'base' })
});

customDb.find({})
  .sort({ name: 1 })
  .exec((err, docs) => {
    console.log('Documents sorted with custom string comparison:', docs);
  });

Field Projection

Select specific fields to include or exclude in the query results, reducing data transfer and memory usage.

/**
 * Set field projection for results
 * @param {object} projection - Field projection specification
 * @returns {Cursor} Cursor instance for chaining
 */
projection(projection);

/**
 * Projection specification format
 */
interface ProjectionQuery {
  [fieldName: string]: 1 | 0;  // 1 to include, 0 to exclude (cannot mix except with _id)
}

Usage Examples:

const db = new Datastore({ filename: './users.db', autoload: true });

// Include specific fields only
db.find({})
  .projection({ name: 1, email: 1 })
  .exec((err, users) => {
    // Results contain only name, email, and _id fields
    console.log('User names and emails:', users);
  });

// Exclude specific fields
db.find({})
  .projection({ password: 0, secretKey: 0 })
  .exec((err, users) => {
    // Results contain all fields except password and secretKey
    console.log('Users without sensitive data:', users);
  });

// Exclude _id field
db.find({})
  .projection({ _id: 0, name: 1, email: 1 })
  .exec((err, users) => {
    // Results contain only name and email (no _id)
    console.log('Users without IDs:', users);
  });

// Nested field projection
db.find({})
  .projection({ 'profile.name': 1, 'profile.email': 1 })
  .exec((err, users) => {
    console.log('User profile names and emails:', users);
  });

// Projection with other cursor operations
db.find({ status: 'active' })
  .sort({ lastLogin: -1 })
  .limit(50)
  .projection({ name: 1, lastLogin: 1, status: 1 })
  .exec((err, users) => {
    console.log('Recent active users (limited fields):', users);
  });

// Projection for performance optimization
db.find({ category: 'electronics' })
  .projection({ name: 1, price: 1, rating: 1 })  // Only essential fields
  .sort({ rating: -1 })
  .limit(100)
  .exec((err, products) => {
    console.log('Electronics summary data:', products);
  });

// Complex projection example
db.find({ 
  $and: [
    { role: { $in: ['admin', 'moderator'] } },
    { active: true }
  ]
})
.projection({ 
  name: 1, 
  role: 1, 
  'permissions.read': 1, 
  'permissions.write': 1,
  lastLogin: 1,
  password: 0,  // Explicitly exclude sensitive data
  apiKeys: 0    // Explicitly exclude sensitive data
})
.sort({ lastLogin: -1 })
.exec((err, staff) => {
  console.log('Staff members with permissions:', staff);
});

Cursor Execution

Execute the constructed cursor query and retrieve results through a callback function.

/**
 * Execute the cursor query and return results
 * @param {function} callback - Callback function (err, results) => {}
 */
exec(callback);

/**
 * Execution callback signatures vary by operation type
 */
interface ExecutionCallbacks {
  find: (err: Error, docs: object[]) => void;      // Array of documents
  findOne: (err: Error, doc: object) => void;      // Single document or null
  count: (err: Error, count: number) => void;      // Number of matching documents
}

Usage Examples:

const db = new Datastore({ filename: './orders.db', autoload: true });

// Execute find cursor
db.find({ status: 'pending' })
  .sort({ createdAt: -1 })
  .limit(20)
  .exec((err, orders) => {
    if (err) {
      console.error('Query failed:', err);
      return;
    }
    console.log('Found', orders.length, 'pending orders');
    orders.forEach(order => {
      console.log('- Order', order._id, 'for', order.customerName);
    });
  });

// Execute findOne cursor
db.findOne({ orderId: 'ORD_12345' })
  .projection({ customerName: 1, total: 1, status: 1 })
  .exec((err, order) => {
    if (err) {
      console.error('Query failed:', err);
      return;
    }
    if (order) {
      console.log('Order found:', order);
    } else {
      console.log('Order not found');
    }
  });

// Execute count cursor
db.count({ status: 'completed', total: { $gte: 100 } })
  .exec((err, count) => {
    if (err) {
      console.error('Count failed:', err);
      return;
    }
    console.log('High-value completed orders:', count);
  });

// Error handling patterns
db.find({ invalidField: { $invalidOperator: 'value' } })
  .exec((err, results) => {
    if (err) {
      console.error('Query error:', err.message);
      console.error('Error type:', err.name);
      return;
    }
    console.log('Results:', results);
  });

// Promise-style wrapper for modern async/await usage
function promisifyQuery(cursor) {
  return new Promise((resolve, reject) => {
    cursor.exec((err, result) => {
      if (err) reject(err);
      else resolve(result);
    });
  });
}

// Usage with async/await
async function getTopProducts() {
  try {
    const products = await promisifyQuery(
      db.find({ featured: true })
        .sort({ rating: -1 })
        .limit(10)
        .projection({ name: 1, rating: 1, price: 1 })
    );
    console.log('Top featured products:', products);
    return products;
  } catch (err) {
    console.error('Failed to get top products:', err);
    throw err;
  }
}

Cursor Chaining Patterns

Common patterns for combining cursor operations effectively.

const db = new Datastore({ filename: './data.db', autoload: true });

// Pattern 1: Pagination with sorting
function getPaginatedData(page, pageSize, sortField = 'createdAt', sortOrder = -1) {
  return db.find({})
    .sort({ [sortField]: sortOrder })
    .skip(page * pageSize)
    .limit(pageSize);
}

// Usage
getPaginatedData(2, 10, 'name', 1).exec((err, data) => {
  console.log('Page 3 data:', data);
});

// Pattern 2: Search with relevance sorting
function searchProducts(searchTerm, maxResults = 20) {
  return db.find({ 
    $or: [
      { name: { $regex: new RegExp(searchTerm, 'i') } },
      { description: { $regex: new RegExp(searchTerm, 'i') } }
    ]
  })
  .sort({ featured: -1, rating: -1 })  // Featured first, then by rating
  .limit(maxResults)
  .projection({ name: 1, description: 1, price: 1, rating: 1, featured: 1 });
}

// Pattern 3: Recent items with exclusions
function getRecentItems(excludeIds = [], limit = 50) {
  const query = excludeIds.length > 0 
    ? { _id: { $nin: excludeIds } }
    : {};
    
  return db.find(query)
    .sort({ updatedAt: -1 })
    .limit(limit)
    .projection({ title: 1, updatedAt: 1, status: 1 });
}

// Pattern 4: Conditional sorting and projection
function getUsers(role = null, includeDetails = false) {
  const query = role ? { role: role } : {};
  const projection = includeDetails 
    ? { password: 0, apiKey: 0 }  // Exclude sensitive fields
    : { name: 1, email: 1, role: 1 };  // Include only basic fields
    
  return db.find(query)
    .sort({ name: 1 })
    .projection(projection);
}

// Pattern 5: Complex filtering with performance optimization
function getFilteredProducts(filters) {
  let cursor = db.find(filters.query || {});
  
  if (filters.sort) {
    cursor = cursor.sort(filters.sort);
  }
  
  if (filters.skip) {
    cursor = cursor.skip(filters.skip);
  }
  
  if (filters.limit) {
    cursor = cursor.limit(filters.limit);
  }
  
  if (filters.fields) {
    cursor = cursor.projection(filters.fields);
  }
  
  return cursor;
}

// Usage
const filters = {
  query: { category: 'electronics', price: { $lte: 500 } },
  sort: { rating: -1, price: 1 },
  skip: 20,
  limit: 10,
  fields: { name: 1, price: 1, rating: 1 }
};

getFilteredProducts(filters).exec((err, products) => {
  console.log('Filtered products:', products);
});

Cursor Performance Considerations

  • Cursors use lazy evaluation - operations are not executed until exec() is called
  • Sorting is performed in memory, so large result sets may impact performance
  • Use limit() to reduce memory usage for large queries
  • Combine projection() with queries to minimize data transfer
  • Index frequently sorted fields for better performance
  • Use skip() sparingly with large offsets as it still processes skipped documents

docs

cursor-operations.md

database-management.md

document-operations.md

index-management.md

index.md

query-system.md

tile.json