Esix provides powerful aggregation methods that allow you to perform calculations and analytics on your MongoDB collections with type-safe, chainable queries.
Counting Records
The count() method returns the number of documents matching your query:
const userCount = await User.count();
const activeUserCount = await User.where('status', 'active').count();
Method Signature
static async count<T extends BaseModel>(
this: ObjectType<T>
): Promise<number>
Source: packages/esix/src/base-model.ts:69
The count() method works with all query filters and returns 0 when no matching documents are found.
Sum
The sum() method calculates the total of all values for a numeric field:
const totalSales = await Order.sum('amount');
const categoryTotal = await Order
.where('category', 'electronics')
.sum('amount');
Method Signature
static async sum<T extends BaseModel, K extends keyof T>(
this: ObjectType<T>,
key: K
): Promise<number>
Source: packages/esix/src/base-model.ts:318
Average
The average() method calculates the mean value of a numeric field:
const avgAge = await User.average('age');
const avgPremiumAge = await User
.where('subscription', 'premium')
.average('age');
Method Signature
static async average<T extends BaseModel, K extends keyof T>(
this: ObjectType<T>,
key: K
): Promise<number>
Source: packages/esix/src/base-model.ts:54
The average() method returns 0 when no values exist.
Min
The min() method finds the smallest value for a numeric field:
const minAge = await User.min('age');
const lowestPrice = await Product
.where('inStock', true)
.min('price');
Method Signature
static async min<T extends BaseModel, K extends keyof T>(
this: ObjectType<T>,
key: K
): Promise<number>
Source: packages/esix/src/base-model.ts:233
Max
The max() method finds the largest value for a numeric field:
const maxScore = await Test.max('score');
const highestPrice = await Product
.where('category', 'premium')
.max('price');
Method Signature
static async max<T extends BaseModel, K extends keyof T>(
this: ObjectType<T>,
key: K
): Promise<number>
Source: packages/esix/src/base-model.ts:216
Percentile
The percentile() method calculates the nth percentile of values for a numeric field:
// Median (50th percentile)
const median = await ResponseTime.percentile('value', 50);
// 95th percentile response time
const p95 = await ResponseTime.percentile('value', 95);
// 99th percentile
const p99 = await ResponseTime
.where('statusCode', 200)
.percentile('value', 99);
Method Signature
static async percentile<T extends BaseModel, K extends keyof T>(
this: ObjectType<T>,
key: K,
n: number
): Promise<number>
Source: packages/esix/src/base-model.ts:271
Percentiles are useful for performance monitoring and SLA tracking. The 95th percentile (p95) is commonly used to measure response times.
Custom Aggregations
The aggregate() method provides direct access to MongoDB’s aggregation pipeline:
const results = await User.aggregate([
{ $group: { _id: '$department', count: { $sum: 1 } } }
]);
const salesByMonth = await Order.aggregate([
{
$group: {
_id: { $month: '$createdAt' },
total: { $sum: '$amount' },
count: { $sum: 1 }
}
},
{ $sort: { _id: 1 } }
]);
Method Signature
static async aggregate<T extends BaseModel>(
this: ObjectType<T>,
stages: Record<string, unknown>[]
): Promise<any[]>
Source: packages/esix/src/base-model.ts:25
The aggregate() method bypasses Esix’s type safety and returns raw MongoDB results. Use it when built-in aggregation methods don’t meet your needs.
Combining with Filters
All aggregation methods can be combined with query filters:
class ResponseTime extends BaseModel {
public value = 0;
public statusCode = 200;
public endpoint = 'index';
}
// Average response time for successful requests
const avgResponseTime = await ResponseTime
.where('statusCode', 200)
.average('value');
// Count of slow requests
const slowRequestCount = await ResponseTime
.where('value', '>', 1000)
.count();
// Sum of all response times for a specific endpoint
const totalTime = await ResponseTime
.where('endpoint', '/api/users')
.sum('value');
Practical Examples
Analytics Dashboard
class Order extends BaseModel {
public amount = 0;
public status = 'pending';
public customerId = '';
}
// Get comprehensive sales metrics
const totalOrders = await Order.count();
const totalRevenue = await Order.sum('amount');
const averageOrderValue = await Order.average('amount');
const largestOrder = await Order.max('amount');
const smallestOrder = await Order.min('amount');
console.log('Sales Dashboard');
console.log('===============');
console.log(`Total Orders: ${totalOrders}`);
console.log(`Total Revenue: $${totalRevenue.toFixed(2)}`);
console.log(`Average Order: $${averageOrderValue.toFixed(2)}`);
console.log(`Largest Order: $${largestOrder.toFixed(2)}`);
console.log(`Smallest Order: $${smallestOrder.toFixed(2)}`);
class ResponseTime extends BaseModel {
public value = 0;
public statusCode = 200;
}
// Calculate response time percentiles
const p50 = await ResponseTime
.where('statusCode', 200)
.percentile('value', 50);
const p95 = await ResponseTime
.where('statusCode', 200)
.percentile('value', 95);
const p99 = await ResponseTime
.where('statusCode', 200)
.percentile('value', 99);
const maxResponseTime = await ResponseTime
.where('statusCode', 200)
.max('value');
console.log('API Performance Metrics');
console.log('========================');
console.log(`Median (p50): ${p50}ms`);
console.log(`95th Percentile: ${p95}ms`);
console.log(`99th Percentile: ${p99}ms`);
console.log(`Max: ${maxResponseTime}ms`);
Department Statistics
const departmentStats = await User.aggregate([
{
$group: {
_id: '$department',
count: { $sum: 1 },
avgAge: { $avg: '$age' },
minAge: { $min: '$age' },
maxAge: { $max: '$age' }
}
},
{ $sort: { count: -1 } }
]);
departmentStats.forEach(dept => {
console.log(`${dept._id}: ${dept.count} employees`);
console.log(` Age range: ${dept.minAge}-${dept.maxAge} (avg: ${dept.avgAge})`);
});
Data Type Validation
All numeric aggregation methods (sum, average, min, max, percentile) validate that the field contains only numeric values. An error will be thrown if non-numeric values are encountered.
// This will throw an error if 'age' contains non-numeric values
try {
const avgAge = await User.average('age');
} catch (error) {
console.error('Non-numeric values found in age field');
}
Source: packages/esix/src/query-builder.ts:88
MongoDB Aggregation Pipeline
For complex analytics, use the aggregation pipeline:
// Monthly sales report with multiple metrics
const monthlySales = await Order.aggregate([
{
$group: {
_id: {
year: { $year: '$createdAt' },
month: { $month: '$createdAt' }
},
totalRevenue: { $sum: '$amount' },
orderCount: { $sum: 1 },
avgOrderValue: { $avg: '$amount' },
maxOrder: { $max: '$amount' }
}
},
{ $sort: { '_id.year': -1, '_id.month': -1 } },
{ $limit: 12 }
]);
Next Steps