Comparison of MongoDB native vs monSQLize extended functions
This document provides a detailed comparison of the functional differences between MongoDB's native driver and monSQLize to help you understand the additional value provided by monSQLize.
Quick comparison table
MongoDB native functions (full support)
monSQLize fully encapsulates all native functions of MongoDB. You can use the familiar MongoDB API:
Complete CRUD operation
Atomic operations
Index management
All query options
monSQLize’s unique extension functions
Based on MongoDB's native functions, monSQLize provides additional convenience and performance optimization:
1. Intelligent caching system
MongoDB native: no cache
//MongoDB native: query the database every time
const db = client.db('shop');
const products = await db.collection('products').find({
category: 'electronics'
}).toArray();
//Latency depends on the query, indexes, dataset, server, and network.
//Query again: still query the database
const products2 = await db.collection('products').find({
category: 'electronics'
}).toArray();
//This still performs a database round trip.
monSQLize: smart caching
//monSQLize: automatic caching
const products = await collection('products').find(
{ category: 'electronics' },
{ cache: 5000 } //Cache for 5 seconds
);
//The first call queries the database and populates the configured cache.
//Query again: return from cache
const products2 = await collection('products').find(
{ category: 'electronics' },
{ cache: 5000 }
);
//The second call can return from cache while the entry remains valid.
Comparison of cache features
Detailed documentation: Cache system
Performance note: A cache hit avoids the database query, but the resulting latency depends on serialization, cache tier, payload size, network placement, and contention. Measure the workload you deploy; see Performance evidence.
2. Explicit cache invalidation
MongoDB native: Manual cache management
//Requires manual management of cache consistency
const cache = new Map();
//Manually check cache when querying
const cacheKey = 'products:electronics';
let products = cache.get(cacheKey);
if (!products) {
products = await db.collection('products').find({
category: 'electronics'
}).toArray();
cache.set(cacheKey, products);
}
//Manually clear the cache when updating (easy to miss)
await db.collection('products').insertOne({
name: 'New Product',
category: 'electronics'
});
//The relevant cache must be cleared manually
cache.delete('products:electronics'); //Easy to forget or incomplete cleaning
monSQLize: explicit cache invalidation
// monSQLize: cache a read query
const products = await collection('products').find(
{ category: 'electronics' },
{ cache: 5000 }
);
// The read cache has been created.
// Insert new data and precisely clear the affected cached query.
await collection('products').insertOne(
{
name: 'New Product',
category: 'electronics'
},
{
cache: {
invalidate: [{
operation: 'find',
query: { category: 'electronics' },
options: { cache: 5000 }
}]
}
}
);
// Query again: the declared cache entry has been invalidated.
const freshProducts = await collection('products').find(
{ category: 'electronics' },
{ cache: 5000 }
);
// The invalidation scope is declared on the write operation.
Operations supported by explicit invalidation
Benefits: Keep the invalidation scope close to the write path and avoid unexpected broad cache deletes.
3. Depth paging (cursor paging)
// MongoDB native: use skip + limit (deep paging is slow)
const page = 1000; //Page 1000
const pageSize = 20;
const products = await db.collection('products')
.find({ category: 'electronics' })
.sort({ createdAt: -1 })
.skip((page - 1) * pageSize) // Skip 19980 documents (very slow)
.limit(pageSize)
.toArray();
// Problem:
// - skip requires scanning all previous documents (performance decreases linearly with the number of pages)
// - Page 1000 needs to scan 19980 documents, which is very slow
// - Paging results are unstable when data changes (insertions/deletions affect subsequent pages)
Performance comparison:
// monSQLize: use cursor paging (deep paging is also fast)
const page1 = await collection('products').findPage(
{ category: 'electronics' },
{
limit: 20,
sort: { createdAt: -1 },
bookmarks: {
step: 10, // Cache a bookmark every 10 pages
maxHops: 20 // Jump up to 20 times
}
}
);
// Jump to page 1000 (jump via bookmark, no need to scan all data)
const page1000 = await collection('products').findPage(
{ category: 'electronics' },
{
limit: 20,
page: 1000, // Skip directly to page 1000
bookmarks: { step: 10, maxHops: 20 }
}
);
// Performance:
// - Skip through bookmarks to avoid scanning large amounts of data
// - Bounded bookmark hops when the configured bookmark is available
// - Data changes do not affect existing pages (the cursor locks the data set at query time)
Performance comparison:
Do not treat these characteristics as fixed speedups. Benchmark the actual indexes, selectivity, document size, page depth, concurrency, and MongoDB deployment; see Performance evidence.
Comparison of paging features
Detailed documentation: findPage guide
// MongoDB native: requires manual profiling configuration
await db.setProfilingLevel(1, { slowms: 100 });
// View the slow query log (requires querying system.profile separately)
const slowQueries = await db.collection('system.profile')
.find({ millis: { $gt: 100 } })
.toArray();
// Problem:
// - Requires manual configuration
// - Logs are stored in the database (occupying space)
// - Requires separate query and analysis
// - Slow-query warnings cannot be seen directly in the code
monSQLize: Slow query log out of the box
// monSQLize: automatically monitor slow queries
const msq = new MonSQLize({
type: 'mongodb',
databaseName: 'shop',
config: { uri: 'mongodb://localhost:27017' },
slowQueryMs: 1000 // Log warning after 1 second (default)
});
// Automatically monitor slow query events
msq.on('slow-query', (data) => {
console.warn('Slow query warning:', {
operation: data.operation,
collection: data.collectionName,
duration: data.duration,
query: data.query,
options: data.options
});
});
// Execute queries (automated monitoring)
const products = await collection('products').find({
category: 'electronics'
});
// If the query takes more than 1 second, the slow-query event is automatically triggered.
// Output: Slow query warning: { operation: 'find', collection: 'products', duration: 1200, ... }
Detailed documentation: Event system
5. Cross-database access
MongoDB native: Manually switch databases
// MongoDB native: switch databases manually
const client = new MongoClient('mongodb://localhost:27017');
await client.connect();
// Access the shop database
const shopDb = client.db('shop');
const products = await shopDb.collection('products').find({}).toArray();
// Access the analytics database (requires manual switching)
const analyticsDb = client.db('analytics'); // Manual switching
const events = await analyticsDb.collection('events').find({}).toArray();
// Problem:
// - Each cross-database access needs a manual switch
// - Verbose code
// - Error-prone
monSQLize: scoped access across databases
// monSQLize: cross-database access with an explicit scoped accessor
const msq = new MonSQLize({
type: 'mongodb',
databaseName: 'shop', // Default database
config: { uri: 'mongodb://localhost:27017' }
});
const { collection, use } = await msq.connect();
// Access the default database (shop)
const products = await collection('products').find({});
// Access analytics with a scoped database accessor
const events = await use('analytics').collection('events').find({});
// Concise and clear for business collection access
// Chained cross-database access
const logs = await use('logs').collection('access_logs').find({});
Comparison of cross-database access features
Detailed documentation: Connection configuration
6. Type Safety (TypeScript)
MongoDB native: generic types
//MongoDB native: basic generic types
import { MongoClient, Collection } from 'mongodb';
interface Product {
_id?: ObjectId;
name: string;
price: number;
}
const client = new MongoClient('mongodb://localhost:27017');
const db = client.db('shop');
const products: Collection<Product> = db.collection('products');
//Basic type inference
const result = await products.findOne({ name: 'iPhone' });
// result: Product | null
monSQLize: complete type declaration
//monSQLize: Complete TypeScript types
import MonSQLize from 'monsqlize';
interface Product {
_id?: ObjectId;
name: string;
price: number;
category: string;
}
const msq = new MonSQLize({
type: 'mongodb',
databaseName: 'shop',
config: { uri: 'mongodb://localhost:27017' }
});
const { collection } = await msq.connect();
//Type-safe queries
const products = await collection('products').find<Product>(
{ category: 'electronics' },
{
cache: 5000, //Option type check
projection: { name: 1, price: 1 }, //Projection type check
limit: 20 //Parameter type checking
}
);
// products: Product[]
//Option autocomplete
const result = await collection('products').findPage<Product>(
{ category: 'electronics' },
{
cache: 5000,
bookmarks: {
step: 10, //IDE auto-completion
maxHops: 20, //Type tips
ttlMs: 3600000 //Type checking
}
}
);
TypeScript supports comparison
Type declaration file: types/index.d.ts
MongoDB native: standard insertMany
//MongoDB native: standard insertMany
const documents = Array.from({ length: 10000 }, (_, i) => ({
index: i,
name: `Product ${i}`,
price: Math.random() * 1000
}));
//One-time insert (may timeout or run out of memory)
const result = await db.collection('products').insertMany(documents);
//Latency and memory use depend on payload size and deployment limits.
monSQLize: Intelligent batch insertion
//monSQLize: insertMany (automatic optimization)
const documents = Array.from({ length: 10000 }, (_, i) => ({
index: i,
name: `Product ${i}`,
price: Math.random() * 1000
}));
//Standard insertMany (performance optimized)
const result = await collection('products').insertMany(documents);
//Uses the package write path; benchmark it against the same driver options.
//Very large batches: use insertBatch (automatic batching)
const result2 = await collection('products').insertBatch(documents, {
batchSize: 1000 //1000 pieces per batch
});
//Limits each batch; failures and timeouts are still possible and must be handled.
Batch insert trade-offs
Chunking improves control over request size; it does not guarantee a universal throughput gain. Benchmark identical write concern, ordered mode, indexes, document sizes, and concurrency; see Performance evidence.
Detailed documentation: insertMany guide, insertBatch guide
8. Multi-layer caching (local + Redis)
MongoDB native: no cache (8. Multi-layer cache (local + Redis))
//MongoDB native: query the database every time
const products = await db.collection('products').find({
category: 'electronics'
}).toArray();
//Every call reaches the database; latency is workload-dependent.
monSQLize: multi-layer caching
//monSQLize: local memory + Redis multi-layer cache
const msq = new MonSQLize({
type: 'mongodb',
databaseName: 'shop',
config: { uri: 'mongodb://localhost:27017' },
cache: {
multiLevel: true,
local: { maxEntries: 10000 }, //Local cache 10,000 items
remote: MonSQLize.createRedisCacheAdapter('redis://localhost:6379/0')
}
});
//The first call queries MongoDB and populates the configured tiers.
const products1 = await collection('products').find(
{ category: 'electronics' },
{ cache: 10000 }
);
//The second call can hit the process-local cache.
const products2 = await collection('products').find(
{ category: 'electronics' },
{ cache: 10000 }
);
//If the local entry expires while Redis remains valid, read from Redis.
Compare these paths with the same payload and concurrency in your deployment; see Performance evidence.
Comparison of multi-layer cache features
Detailed documentation: Multi-layer caching
9. Chain call API
MongoDB native: cursor chain call
//MongoDB native: cursor chain call
const cursor = db.collection('products')
.find({ category: 'electronics' })
.sort({ price: -1 })
.skip(20)
.limit(10);
const products = await cursor.toArray();
monSQLize: complete chain call + cache
//monSQLize: chained calls + cache support
const products = await collection('products')
.find({ category: 'electronics' })
.sort({ price: -1 })
.skip(20)
.limit(10)
.cache(5000) //Chain cache
.maxTimeMS(3000) //✅Chain timeout
.comment('API:listProducts') //✅Chained comments
.toArray();
Comparison of chain call features
Detailed documentation: Chain query API
10. Event system
MongoDB native: listening to driver events
//MongoDB native: listening to underlying driver events
client.on('commandStarted', (event) => {
console.log('Command:', event.commandName);
});
client.on('serverHeartbeatFailed', (event) => {
console.error('Heartbeat failed');
});
//Question:
//- Only low-level driver events
//- No slow query events
//- No cache related events
monSQLize: rich business events
//monSQLize: business-level events
msq.on('slow-query', (data) => {
console.warn('Slow query:', data.operation, data.duration);
});
msq.on('cache-hit', (data) => {
console.log('Cache hit:', data.key);
});
msq.on('cache-miss', (data) => {
console.log('Cache miss:', data.key);
});
msq.on('connected', () => {
console.log('Database is connected');
});
msq.on('error', (data) => {
console.error('Error:', data.error.message);
});
Event system comparison
Detailed documentation: Event system
Usage suggestions
When to use MongoDB native driver?
Suitable scene:
- Simple script or tool
- No caching required
- No advanced pagination required
- Low performance requirements
When to use monSQLize?
Suitable scene:
- Production Application - Requires caching and performance optimization
- High Traffic API - Caching can reduce database pressure
- Deep Pagination - list page, search results, etc.
- Multi-database application - requires cross-database access
- Performance Monitoring - Slow query alarm required
- Complex Business - Requires declared cache invalidation boundaries
Summary and comparison
Quick Start
If you want to experience the extended capabilities of monSQLize, start here:
- Installation:
npm install monsqlize
- Enable cache: Add
{ cache: 5000 } to the query
- Use paging: Use
findPage() instead of find()
- Monitor slow query: Listen for
slow-query events
- Cross-database access: use
use(name).collection(name) for business collections; reserve db(name) for database-level commands
Full example: View the getting started guide