Esix provides simple yet powerful methods for paginating and sorting your query results, making it easy to build efficient data browsing interfaces.
Limiting Results
The limit() method restricts the number of models returned:
const firstTenPosts = await BlogPost.limit(10).get();
Method Signature
static limit<T extends BaseModel>(
this: ObjectType<T>,
length: number
): QueryBuilder<T>
Source: packages/esix/src/base-model.ts:199
Skipping Results
The skip() method skips a specified number of results, useful for pagination:
const nextTenPosts = await BlogPost.skip(10).limit(10).get();
Method Signature
static skip<T extends BaseModel>(
this: ObjectType<T>,
length: number
): QueryBuilder<T>
Source: packages/esix/src/base-model.ts:301
The skip() method is chainable with limit() and other query methods for flexible pagination.
Ordering Results
The orderBy() method sorts results by a specific field:
// Ascending order (default)
const postsByTitle = await BlogPost.orderBy('title').get();
// Descending order
const latestPosts = await BlogPost.orderBy('createdAt', 'desc').get();
Method Signature
static orderBy<T extends BaseModel, K extends keyof T>(
this: ObjectType<T>,
key: K,
order: 'asc' | 'desc' = 'asc'
): QueryBuilder<T>
Source: packages/esix/src/base-model.ts:251
Multiple Sort Fields
Chain multiple orderBy() calls to sort by multiple fields:
const products = await Product
.orderBy('price', 'desc')
.orderBy('name', 'asc')
.get();
This first sorts by price (descending), then by name (ascending) for items with the same price.
Combine skip() and limit() for page-based navigation:
const pageSize = 10;
const pageNumber = 2; // Zero-based: 0 = first page, 1 = second page, etc.
const products = await Product
.skip(pageNumber * pageSize)
.limit(pageSize)
.get();
Create a reusable pagination helper:
async function paginate<T extends BaseModel>(
model: ObjectType<T>,
page: number,
perPage: number = 10
) {
const skip = page * perPage;
const [items, total] = await Promise.all([
model.skip(skip).limit(perPage).get(),
model.count()
]);
return {
items,
page,
perPage,
total,
totalPages: Math.ceil(total / perPage),
hasNext: (page + 1) * perPage < total,
hasPrev: page > 0
};
}
// Usage
const result = await paginate(Product, 0, 20);
console.log(`Page ${result.page + 1} of ${result.totalPages}`);
console.log(`Showing ${result.items.length} of ${result.total} products`);
Sorting Examples
Ascending Order
const productsByPrice = await Product
.orderBy('price')
.get();
// Returns: Widget 4 ($19.99), Widget 2 ($44.99), Chair 2 ($49.99), ...
Descending Order
const productsByPriceDesc = await Product
.orderBy('price', 'desc')
.get();
// Returns: Widget 3 ($129.99), Widget 1 ($79.99), Chair 1 ($79.99), ...
Multi-Field Sorting
const products = await Product
.orderBy('categoryId', 'asc')
.orderBy('price', 'desc')
.get();
// Groups by category, then sorts by price within each category
Here’s a full example showing pagination with filtering and sorting:
class Product extends BaseModel {
public name = '';
public price = 0;
public categoryId?: number;
}
// Get the second page of products in category 1,
// sorted by price, with 10 items per page
const page = 1; // Second page (zero-based)
const pageSize = 10;
const products = await Product
.where('categoryId', 1)
.orderBy('price', 'asc')
.skip(page * pageSize)
.limit(pageSize)
.get();
console.log(`Showing products ${page * pageSize + 1}-${(page + 1) * pageSize}`);
The pluck() method extracts an array of values for a specific field:
const titles = await BlogPost.pluck('title');
// Returns: ['My First Post', 'Another Post', ...]
const prices = await Product
.orderBy('price')
.pluck('price');
// Returns: [19.99, 44.99, 49.99, 79.99, ...]
Method Signature
static pluck<T extends BaseModel, K extends keyof T>(
this: ObjectType<T>,
key: K
): Promise<T[K][]>
Source: packages/esix/src/base-model.ts:289
Use pluck() when you only need values from a single field, as it’s more efficient than retrieving full models.
For large datasets, avoid large skip values (e.g., skip(10000)). MongoDB must traverse all skipped documents, which can be slow. Consider cursor-based pagination for better performance.
For better performance with large datasets:
// First page
const firstPage = await Product
.orderBy('id', 'asc')
.limit(20)
.get();
const lastId = firstPage[firstPage.length - 1].id;
// Next page using cursor
const nextPage = await Product
.where('id', '>', lastId)
.orderBy('id', 'asc')
.limit(20)
.get();
Next Steps