CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-taffydb

JavaScript in-memory database with SQL-like query capabilities for array manipulation

Pending
Overview
Eval results
Files

data-manipulation.mddocs/

Data Manipulation

Methods for modifying, sorting, and transforming query results, including updates, deletions, data aggregation, and result processing.

Capabilities

Data Modification

Update or remove records based on query results with event callback support.

/**
 * Update records in the query result set
 * @param changes - Object containing field updates
 * @param runEvent - Whether to trigger event callbacks (default: true)
 * @returns Updated QueryResult
 */
update(changes: object, runEvent?: boolean): QueryResult;

/**
 * Remove records in the query result set
 * @param runEvent - Whether to trigger event callbacks (default: true)
 * @returns QueryResult (empty after removal)
 */
remove(runEvent?: boolean): QueryResult;

Usage Examples:

// Update single field
db({ id: 1 }).update({ price: 199.99 });

// Update multiple fields
db({ category: "electronics" }).update({
  discount: 0.15,
  sale: true,
  updated: new Date()
});

// Update with event callbacks disabled
db({ status: "pending" }).update({ status: "processed" }, false);

// Remove records
db({ expired: true }).remove();

// Remove with callback events disabled
db({ temporary: true }).remove(false);

// Chain updates
db({ department: "Sales" })
  .filter({ performance: { lt: 2.0 } })
  .update({ needsTraining: true, reviewed: new Date() });

Result Retrieval

Get query results in various formats for data processing and analysis.

/**
 * Get all records as a plain JavaScript array
 * @returns Array of record objects
 */
get(): object[];

/**
 * Get the first record from the result set
 * @returns First record object or false if empty
 */
first(): object | false;

/**
 * Get the last record from the result set
 * @returns Last record object or false if empty
 */
last(): object | false;

/**
 * Get the count of records in the result set
 * @returns Number of records
 */
count(): number;

/**
 * Convert query results to JSON string
 * @returns JSON string representation of results
 */
stringify(): string;

Usage Examples:

// Get all matching records
const activeUsers = db({ active: true }).get();
console.log('Found', activeUsers.length, 'active users');

// Get single records (returns false if no results)
const firstUser = db().order("created").first();
const lastUser = db().order("created").last();
const newestUser = db().order("created desc").first();

// Check if results exist
if (firstUser === false) {
  console.log("No users found");
} else {
  console.log("First user:", firstUser.name);
}

// Get counts
const totalUsers = db().count();
const activeCount = db({ active: true }).count();
const inactiveCount = db({ active: false }).count();

// Export as JSON
const jsonData = db({ department: "Engineering" }).stringify();
localStorage.setItem('engineeringData', jsonData);

// Conditional operations
if (db({ role: "admin" }).count() === 0) {
  console.log("No administrators found!");
}

Data Selection and Projection

Extract specific fields or compute derived values from query results.

/**
 * Select specific columns/properties from results
 * @param columns - Column names to extract
 * @returns Array of values or arrays of values
 */
select(...columns: string[]): any[] | any[][];

/**
 * Get unique values from one or more columns
 * @param columns - Column names to extract unique values from
 * @returns Array of unique values (single column) or array of unique value arrays (multiple columns)
 */
distinct(...columns: string[]): any[] | any[][];

Usage Examples:

// Select single column
const names = db({ active: true }).select("name");
// Returns: ["Alice", "Bob", "Charlie"]

// Select multiple columns
const userInfo = db({ department: "Sales" }).select("name", "email", "salary");
// Returns: [["Alice", "alice@company.com", 75000], ["Bob", "bob@company.com", 68000]]

// Get unique values from single column
const departments = db().distinct("department");
// Returns: ["Engineering", "Sales", "Marketing", "HR"]

const locations = db({ active: true }).distinct("location");
// Returns: ["New York", "San Francisco", "Austin"]

// Get unique combinations from multiple columns
const roleDeptCombos = db().distinct("role", "department");
// Returns: [["Manager", "Sales"], ["Developer", "Engineering"], ["Manager", "Engineering"]]

// Combine with filtering
const seniorRoles = db({ experience: { gte: 5 } }).distinct("role");
const highEarners = db({ salary: { gte: 100000 } }).select("name", "salary", "department");

Data Aggregation

Perform mathematical operations and statistical calculations on numeric data.

/**
 * Calculate sum of numeric columns (all columns are summed together)
 * @param columns - Column names to sum
 * @returns Single sum value combining all specified columns
 */
sum(...columns: string[]): number;

/**
 * Find minimum value in a column
 * @param column - Column name to analyze
 * @returns Minimum value found
 */
min(column: string): any;

/**
 * Find maximum value in a column
 * @param column - Column name to analyze
 * @returns Maximum value found
 */
max(column: string): any;

Usage Examples:

// Calculate sums (single column)
const totalSalary = db({ department: "Engineering" }).sum("salary");

// Calculate combined sum (all columns summed together)
const totalCompensation = db({ active: true }).sum("salary", "bonus", "commission");
// Returns: single number (salary + bonus + commission for all active users)

// Find min/max values
const youngestAge = db({ active: true }).min("age");
const oldestAge = db({ active: true }).max("age");
const lowestSalary = db({ department: "Sales" }).min("salary");
const highestSalary = db({ department: "Sales" }).max("salary");

// Aggregate analysis
const salesStats = {
  count: db({ department: "Sales" }).count(),
  totalSalary: db({ department: "Sales" }).sum("salary"),
  avgSalary: db({ department: "Sales" }).sum("salary") / db({ department: "Sales" }).count(),
  minSalary: db({ department: "Sales" }).min("salary"),
  maxSalary: db({ department: "Sales" }).max("salary")
};

// Multiple aggregations
const departmentSums = db().distinct("department").map(dept => ({
  department: dept,
  totalSalary: db({ department: dept }).sum("salary"),
  employeeCount: db({ department: dept }).count()
}));

Iteration and Transformation

Process records individually or transform entire result sets.

/**
 * Iterate over each record in the result set
 * @param callback - Function to call for each record
 * @returns QueryResult for chaining
 */
each(callback: (record: object, index?: number) => any): QueryResult;

/**
 * Transform each record and return new array
 * @param callback - Function to transform each record
 * @returns Array of transformed values
 */
map(callback: (record: object, index?: number) => any): any[];

/**
 * Inject record values into string templates
 * @param template - String template with {property} placeholders
 * @param returnArray - Whether to return array of strings (default: false)
 * @returns Single string or array of strings
 */
supplant(template: string, returnArray?: boolean): string | string[];

Usage Examples:

// Iterate with each
db({ active: true }).each(function(user, index) {
  console.log(`${index + 1}. ${user.name} - ${user.email}`);
  
  // Return TAFFY.EXIT to break early
  if (index >= 5) return TAFFY.EXIT;
});

// Transform with map
const emailList = db({ active: true }).map(function(user) {
  return user.email.toLowerCase();
});

const userSummaries = db({ department: "Engineering" }).map(function(user, index) {
  return {
    id: user.id,
    fullName: `${user.firstName} ${user.lastName}`,
    seniority: user.experience >= 5 ? "Senior" : "Junior",
    position: index + 1
  };
});

// Template substitution
const emailTemplate = "Hello {name}, your account balance is ${balance}.";

// Single string (concatenated)
const emailBody = db({ id: 123 }).supplant(emailTemplate);
// Returns: "Hello John Smith, your account balance is $1250.50."

// Array of strings (one per record)
const emailBodies = db({ active: true }).supplant(emailTemplate, true);
// Returns: ["Hello Alice...", "Hello Bob...", "Hello Charlie..."]

// HTML generation
const tableRows = db({ department: "Sales" }).supplant(
  "<tr><td>{name}</td><td>{email}</td><td>${salary}</td></tr>",
  true
);

Asynchronous Operations

Execute callbacks with optional delays for non-blocking operations.

/**
 * Execute callback with query results after optional delay
 * @param fn - Callback function to execute
 * @param delay - Delay in milliseconds (default: 0)
 * @returns QueryResult for chaining
 */
callback(fn: (results: object[]) => void, delay?: number): QueryResult;

Usage Examples:

// Immediate callback
db({ active: true }).callback(function(results) {
  console.log('Found', results.length, 'active users');
  results.forEach(user => {
    console.log('- ' + user.name);
  });
});

// Delayed callback
db({ needsNotification: true }).callback(function(results) {
  results.forEach(user => {
    sendNotificationEmail(user.email, user.message);
  });
}, 1000); // Wait 1 second before executing

// Chain with other operations
db({ status: "pending" })
  .update({ status: "processing" })
  .callback(function(results) {
    console.log('Started processing', results.length, 'items');
  })
  .filter({ priority: "high" })
  .callback(function(results) {
    console.log('High priority items:', results.length);
  }, 500);

Table Joins

Perform SQL-like inner joins between TaffyDB instances or query results with flexible condition matching.

/**
 * Join current query results with another table using specified conditions
 * @param table - TaffyDB instance or QueryResult to join with
 * @param conditions - Join conditions (arrays, functions, or mixed)
 * @returns QueryResult containing joined records
 */
join(table: TaffyDatabase | QueryResult, ...conditions: (JoinCondition | JoinFunction)[]): QueryResult;

// Join condition types
type JoinCondition = [string, string] | [string, string, string];  // [leftField, rightField] or [leftField, operator, rightField]
type JoinFunction = (leftRow: object, rightRow: object) => boolean;

Usage Examples:

// Basic join on matching fields
const users = TAFFY([
  { id: 1, name: "Alice", deptId: 10 },
  { id: 2, name: "Bob", deptId: 20 },
  { id: 3, name: "Charlie", deptId: 10 }
]);

const departments = TAFFY([
  { id: 10, name: "Engineering", budget: 500000 },
  { id: 20, name: "Sales", budget: 300000 }
]);

// Join users with departments on matching IDs
const userDeptInfo = users().join(departments, ["deptId", "id"]);
// Results contain combined records with department info merged

// Join with custom comparison operator
const highBudgetUsers = users().join(departments, ["deptId", "===", "id"]);

// Join with custom function condition
const customJoin = users({ name: { like: "A" } }).join(departments, function(user, dept) {
  return user.deptId === dept.id && dept.budget > 400000;
});

// Multiple join conditions (all must be true)
const complexJoin = users().join(departments, 
  ["deptId", "id"],                                    // Basic field matching
  function(user, dept) {                               // Custom condition
    return user.name.length > 3;
  }
);

// Available operators in array conditions
const operatorExamples = users().join(departments,
  ["deptId", "===", "id"],    // Strict equality (default)
  ["deptId", "!==", "id"],    // Not equal
  ["deptId", "<", "id"],      // Less than
  ["deptId", ">", "id"],      // Greater than
  ["deptId", "<=", "id"],     // Less than or equal
  ["deptId", ">=", "id"],     // Greater than or equal
  ["deptId", "==", "id"],     // Loose equality
  ["deptId", "!=", "id"]      // Loose inequality
);

Join Result Structure:

// When joining, conflicting field names are prefixed with "right_"
const result = users().join(departments, ["deptId", "id"]).get();
// Example result record:
// {
//   id: 1,              // From left table (users)
//   name: "Alice",      // From left table (users)
//   deptId: 10,         // From left table (users)
//   right_id: 10,       // From right table (departments) - prefixed to avoid conflict
//   right_name: "Engineering",  // From right table (departments) - prefixed
//   budget: 500000      // From right table (departments) - no conflict, no prefix
// }

// Chain joins with other operations
const filteredJoin = users({ name: { left: "A" } })
  .join(departments, ["deptId", "id"])
  .filter({ budget: { gt: 400000 } })
  .order("name")
  .get();

Advanced Result Processing

Complex data manipulation combining multiple query operations.

Usage Examples:

// Batch processing with pagination
function processAllUsers(pageSize = 100) {
  const totalUsers = db({ active: true }).count();
  const totalPages = Math.ceil(totalUsers / pageSize);
  
  for (let page = 1; page <= totalPages; page++) {
    const users = db({ active: true })
      .order("id")
      .start((page - 1) * pageSize + 1)
      .limit(pageSize)
      .get();
    
    // Process batch
    users.forEach(user => {
      processUser(user);
    });
  }
}

// Conditional updates based on aggregations
const avgSalary = db({ department: "Engineering" }).sum("salary") / 
                  db({ department: "Engineering" }).count();

db({ department: "Engineering" })
  .filter(function() { return this.salary < avgSalary * 0.8; })
  .update({ needsReview: true, reviewDate: new Date() });

// Complex reporting
const departmentReport = db().distinct("department").map(dept => {
  const deptEmployees = db({ department: dept });
  return {
    department: dept,
    totalEmployees: deptEmployees.count(),
    activeEmployees: deptEmployees.filter({ active: true }).count(),
    totalSalary: deptEmployees.sum("salary"),
    avgSalary: deptEmployees.sum("salary") / deptEmployees.count(),
    topPerformer: deptEmployees.order("performance desc").first()
  };
});

Install with Tessl CLI

npx tessl i tessl/npm-taffydb

docs

data-manipulation.md

database-management.md

index.md

querying-filtering.md

utility-functions.md

tile.json