SlintORM
Docsnpm

Batch Operations

SlintORM provides batch methods for efficiently operating on multiple rows at once. These methods are significantly faster than looping over individual .insert(), .update(), or .delete() calls.

insertMany

insertMany(items[]) performs a batch insert and returns the number of rows inserted. On SQLite the operation is wrapped in a single transaction. On Postgres each inserted row is returned with auto-generated field values.

// insertMany(items[]) — returns number of rows inserted
// SQLite wraps all inserts in a single transaction for performance
// Postgres/MySQL use batch INSERT statements

const count = await User.insertMany([
  { email: 'alice@example.com', name: 'Alice', role: 'user' },
  { email: 'bob@example.com', name: 'Bob', role: 'user' },
  { email: 'carol@example.com', name: 'Carol', role: 'admin' },
]);

console.log(count); // 3

// On Postgres, each row is returned with auto-generated fields:
// const users = await User.insertMany([...]);
// users[0].id, users[0].createdAt, etc. are all populated.

Chunking large datasets

// Large batch — consider chunking for very large arrays
const batchSize = 500;
const allRecords = []; // imagine 10,000 items

for (let i = 0; i < allRecords.length; i += batchSize) {
  const chunk = allRecords.slice(i, i + batchSize);
  const inserted = await User.insertMany(chunk);
  console.log(`Inserted ${inserted} users (batch ${i / batchSize + 1})`);
}

updateMany

updateMany(filter, data) updates all rows matching the filter and returns the number of affected rows. The updatedAt field is set automatically.

// updateMany(filter, data) — returns number of affected rows
// Sets updatedAt automatically

const count = await User.updateMany(
  { role: 'guest' },
  { role: 'user' }
);

console.log(`Upgraded ${count} guests to users`);

// Can update multiple fields at once
await Post.updateMany(
  { status: 'draft', authorId: null },
  { status: 'archived', archivedAt: new Date().toISOString() }
);

deleteMany

deleteMany(filter) deletes all rows matching the filter and returns the count. On models with soft-delete enabled, this performs a logical delete instead of a physical removal.

// deleteMany(filter) — returns number of deleted rows
// WARNING: omitting filter or passing {} may be restricted depending on driver

const count = await Session.deleteMany({
  expiresAt: { $lt: new Date().toISOString() }
});

console.log(`Cleaned up ${count} expired sessions`);

// Soft-delete models: deleteMany performs a logical delete
// (sets deletedAt), not a physical removal

findInBatches

findInBatches(filter, batchSize, callback) processes records in manageable chunks to avoid loading the entire result set into memory. The callback receives (batch: EntityWithUpdate<T>[], batchNumber: number).

// findInBatches(filter, batchSize, callback) — processes records
// in manageable chunks to avoid memory spikes

await User.findInBatches(
  { role: 'user' },
  100,  // batch size
  (batch, batchNumber) => {
    console.log(`Processing batch ${batchNumber} (${batch.length} users)`);

    for (const user of batch) {
      // Each user is a full EntityWithUpdate<T>
      // Process without loading all records into memory at once
    }
  }
);

// The callback can be async
await User.findInBatches(
  { active: true },
  50,
  async (batch, batchNumber) => {
    for (const user of batch) {
      await user.update({ notifiedAt: new Date().toISOString() });
    }
    console.log(`Batch ${batchNumber} complete`);
  }
);

truncate

truncate() removes all rows from the table and resets auto-increment counters. Use with extreme caution — this operation is not reversible.

// truncate() — deletes ALL rows. Irreversible.
// Resets auto-increment counters on most databases.

await TempSession.truncate();

console.log('All temporary sessions removed');

// On SQLite: DELETE FROM table (no transaction wrap)
// On Postgres: TRUNCATE table RESTART IDENTITY
// On MySQL: TRUNCATE table
// On MongoDB: deleteMany({})

Performance tips

// Performance tips for batch operations:

// 1. SQLite — insertMany wraps in a transaction automatically.
//    Avoid manual transaction wrapping around insertMany.

// 2. Postgres — insertMany uses multi-row INSERT:
//    INSERT INTO users (email, name) VALUES ($1,$2),($3,$4),...

// 3. deleteMany with large datasets — consider findInBatches
//    + individual delete if you need per-row hooks.

// 4. MongoDB — insertMany uses insertMany(), deleteMany uses
//    deleteMany(). No transaction support.

// 5. Always prefer batch methods over loop + individual calls
//    for bulk operations — they are 10-100x faster.
PreviousUpsert & findOrCreateNextQuery Builder