Indexes & Performance
Learn how to create and manage database indexes to dramatically improve query performance and optimize your database operations.
Quick Example
- TypeScript
- Java
- Go
- .NET
import { DatabaseService } from '@ductape/sdk';
const db = new DatabaseService();
await db.connect({ database: 'main-db' });
// Create an index
await db.schema.createIndex('users', ['email'], {
unique: true,
name: 'idx_users_email',
});
// List all indexes
const indexes = await db.schema.indexes('users');
console.log(`Table has ${indexes.length} indexes`);
import Map.of( DatabaseService ) from '@ductape/sdk';
Map<String, Object> db = new DatabaseService();
db.connect(Map.of( "database", "main-db" ));
// Create an index
db.schema.createIndex('users', ['email'], Map.of(
"unique", true,
"name", "idx_users_email"
));
// List all indexes
Map<String, Object> indexes = db.schema.indexes('users');
System.out.println(`Table has $Map.of(indexes.length) indexes`);
import { DatabaseService } from '@ductape/sdk';
db := new DatabaseService();
db.connect({ "database": "main-db" });
// Create an index
db.schema.createIndex('users', ['email'], {
"unique": true,
"name": "idx_users_email",
});
// List all indexes
indexes := db.schema.indexes('users');
fmt.Println(`Table has ${indexes.length} indexes`);
import { DatabaseService } from '@ductape/sdk';
var db = new DatabaseService();
await db.connect({ ["database"] = "main-db" });
// Create an index
await db.schema.createIndex('users', ['email'], {
["unique"] = true,
["name"] = "idx_users_email",
});
// List all indexes
var indexes = await db.schema.indexes('users');
Console.WriteLine(`Table has ${indexes.length} indexes`);
Why Indexes Matter
Indexes are critical for database performance:
Without Index:
- TypeScript
- Java
- Go
- .NET
// Full table scan - reads every row (slow)
const user = await db.findOne({
table: 'users',
where: { email: 'alice@example.com' },
});
// 1M rows = 1000ms+
// Full table scan - reads every row (slow)
Map<String, Object> user = db.findOne(Map.of(
"table", "users",
where: Map.of( "email", "alice@example.com" )
));
// 1M rows = 1000ms+
// Full table scan - reads every row (slow)
user := db.findOne({
"table": "users",
where: { "email": "alice@example.com" },
});
// 1M rows = 1000ms+
// Full table scan - reads every row (slow)
var user = await db.findOne({
["table"] = "users",
where: { ["email"] = "alice@example.com" },
});
// 1M rows = 1000ms+
With Index:
- TypeScript
- Java
- Go
- .NET
// Index lookup - direct access (fast)
const user = await db.findOne({
table: 'users',
where: { email: 'alice@example.com' },
});
// 1M rows = 1-5ms
// Index lookup - direct access (fast)
Map<String, Object> user = db.findOne(Map.of(
"table", "users",
where: Map.of( "email", "alice@example.com" )
));
// 1M rows = 1-5ms
// Index lookup - direct access (fast)
user := db.findOne({
"table": "users",
where: { "email": "alice@example.com" },
});
// 1M rows = 1-5ms
// Index lookup - direct access (fast)
var user = await db.findOne({
["table"] = "users",
where: { ["email"] = "alice@example.com" },
});
// 1M rows = 1-5ms
Indexes can provide 100-1000x performance improvement for queries.
Creating Indexes
Basic Index
- TypeScript
- Java
- Go
- .NET
await db.schema.createIndex('users', ['email']);
db.schema.createIndex('users', ['email']);
db.schema.createIndex('users', ['email']);
await db.schema.createIndex('users', ['email']);
Unique Index
Enforce uniqueness while improving query performance:
- TypeScript
- Java
- Go
- .NET
await db.schema.createIndex('users', ['email'], {
unique: true,
});
// Now duplicate emails will be rejected
try {
await db.insert({
table: 'users',
records: [{ email: 'alice@example.com', name: 'Alice' }],
});
// This will fail - email already exists
await db.insert({
table: 'users',
records: [{ email: 'alice@example.com', name: 'Bob' }],
});
} catch (error) {
console.log('Unique constraint violation');
}
db.schema.createIndex('users', ['email'], Map.of(
"unique", true
));
// Now duplicate emails will be rejected
try Map.of(
db.insert(Map.of(
"table", "users",
records: [Map.of( "email", "alice@example.com", "name", "Alice" )]
));
// This will fail - email already exists
db.insert(Map.of(
"table", "users",
records: [Map.of( "email", "alice@example.com", "name", "Bob" )]
));
) catch (error) Map.of(
System.out.println('Unique constraint violation');
)
db.schema.createIndex('users', ['email'], {
"unique": true,
});
// Now duplicate emails will be rejected
try {
db.insert({
"table": "users",
records: [{ "email": "alice@example.com", "name": "Alice" }],
});
// This will fail - email already exists
db.insert({
"table": "users",
records: [{ "email": "alice@example.com", "name": "Bob" }],
});
} catch (error) {
fmt.Println('Unique constraint violation');
}
await db.schema.createIndex('users', ['email'], {
["unique"] = true,
});
// Now duplicate emails will be rejected
try {
await db.insert({
["table"] = "users",
records: [{ ["email"] = "alice@example.com", ["name"] = "Alice" }],
});
// This will fail - email already exists
await db.insert({
["table"] = "users",
records: [{ ["email"] = "alice@example.com", ["name"] = "Bob" }],
});
} catch (error) {
Console.WriteLine('Unique constraint violation');
}
Composite Index
Index multiple columns together:
- TypeScript
- Java
- Go
- .NET
await db.schema.createIndex('orders', ['user_id', 'created_at'], {
name: 'idx_orders_user_date',
});
// Efficient for queries like:
const orders = await db.find({
table: 'orders',
where: { user_id: 123 },
orderBy: [{ column: 'created_at', order: 'DESC' }],
});
db.schema.createIndex('orders', ['user_id', 'created_at'], Map.of(
"name", "idx_orders_user_date"
));
// Efficient for queries like:
Map<String, Object> orders = db.find(Map.of(
"table", "orders",
where: Map.of( "user_id", 123 ),
orderBy: [Map.of( "column", "created_at", "order", "DESC" )]
));
db.schema.createIndex('orders', ['user_id', 'created_at'], {
"name": "idx_orders_user_date",
});
// Efficient for queries like:
orders := db.find({
"table": "orders",
where: { "user_id": 123 },
orderBy: [{ "column": "created_at", "order": "DESC" }],
});
await db.schema.createIndex('orders', ['user_id', 'created_at'], {
["name"] = "idx_orders_user_date",
});
// Efficient for queries like:
var orders = await db.find({
["table"] = "orders",
where: { ["user_id"] = 123 },
orderBy: [{ ["column"] = "created_at", ["order"] = "DESC" }],
});
Sparse Index (MongoDB)
Only index documents where the field exists:
- TypeScript
- Java
- Go
- .NET
await db.schema.createIndex('users', ['phone'], {
sparse: true,
});
db.schema.createIndex('users', ['phone'], Map.of(
"sparse", true
));
db.schema.createIndex('users', ['phone'], {
"sparse": true,
});
await db.schema.createIndex('users', ['phone'], {
["sparse"] = true,
});
Partial Index (SQL)
Index only rows matching a condition:
- TypeScript
- Java
- Go
- .NET
await db.schema.createIndex('users', ['email'], {
where: "status = 'active'",
name: 'idx_users_email_active',
});
// Smaller, faster index for frequently queried subset
db.schema.createIndex('users', ['email'], Map.of(
"where", "status = 'active'",
"name", "idx_users_email_active"
));
// Smaller, faster index for frequently queried subset
db.schema.createIndex('users', ['email'], {
"where": "status = 'active'",
"name": "idx_users_email_active",
});
// Smaller, faster index for frequently queried subset
await db.schema.createIndex('users', ['email'], {
["where"] = "status = 'active'",
["name"] = "idx_users_email_active",
});
// Smaller, faster index for frequently queried subset
TTL Index (MongoDB)
Automatically expire documents:
- TypeScript
- Java
- Go
- .NET
await db.schema.createIndex('sessions', ['expires_at'], {
expireAfterSeconds: 3600,
});
db.schema.createIndex('sessions', ['expires_at'], Map.of(
"expireAfterSeconds", 3600
));
db.schema.createIndex('sessions', ['expires_at'], {
"expireAfterSeconds": 3600,
});
await db.schema.createIndex('sessions', ['expires_at'], {
["expireAfterSeconds"] = 3600,
});
Index at Table Creation
Define indexes when creating tables:
- TypeScript
- Java
- Go
- .NET
await db.schema.create('orders', {
id: { type: 'uuid', primaryKey: true },
customer_id: 'uuid',
status: { type: 'string', length: 50 },
total: { type: 'decimal', precision: 10, scale: 2 },
}, {
timestamps: true,
indexes: [
{ fields: ['customer_id'] },
{ fields: ['status'] },
{ fields: ['status', 'created_at'] },
],
});
db.schema.create('orders', Map.of(
id: Map.of( "type", "uuid", "primaryKey", true ),
"customer_id", "uuid",
status: Map.of( "type", "string", "length", 50 ),
total: Map.of( "type", "decimal", "precision", 10, "scale", 2 )
), Map.of(
"timestamps", true,
indexes: [
Map.of( fields: ['customer_id'] ),
Map.of( fields: ['status'] ),
Map.of( fields: ['status', 'created_at'] ),
]
));
db.schema.create('orders', {
id: { "type": "uuid", "primaryKey": true },
"customer_id": "uuid",
status: { "type": "string", "length": 50 },
total: { "type": "decimal", "precision": 10, "scale": 2 },
}, {
"timestamps": true,
indexes: [
{ fields: ['customer_id'] },
{ fields: ['status'] },
{ fields: ['status', 'created_at'] },
],
});
await db.schema.create('orders', {
id: { ["type"] = "uuid", ["primaryKey"] = true },
["customer_id"] = "uuid",
status: { ["type"] = "string", ["length"] = 50 },
total: { ["type"] = "decimal", ["precision"] = 10, ["scale"] = 2 },
}, {
["timestamps"] = true,
indexes: [
{ fields: ['customer_id'] },
{ fields: ['status'] },
{ fields: ['status', 'created_at'] },
],
});
Managing Indexes
List Indexes
- TypeScript
- Java
- Go
- .NET
const indexes = await db.schema.indexes('users');
indexes.forEach(idx => {
console.log(`Index: ${idx.name}`);
console.log(` Columns: ${idx.columns.join(', ')}`);
console.log(` Unique: ${idx.unique}`);
console.log(` Primary: ${idx.primaryKey}`);
});
Map<String, Object> indexes = db.schema.indexes('users');
indexes.forEach(idx => Map.of(
System.out.println(`Index: $Map.of(idx.name)`);
System.out.println(` Columns: $Map.of(idx.columns.join(', '))`);
System.out.println(` Unique: $Map.of(idx.unique)`);
System.out.println(` Primary: $Map.of(idx.primaryKey)`);
));
indexes := db.schema.indexes('users');
indexes.forEach(idx => {
fmt.Println(`Index: ${idx.name}`);
fmt.Println(` Columns: ${idx.columns.join(', ')}`);
fmt.Println(` Unique: ${idx.unique}`);
fmt.Println(` Primary: ${idx.primaryKey}`);
});
var indexes = await db.schema.indexes('users');
indexes.forEach(idx => {
Console.WriteLine(`Index: ${idx.name}`);
Console.WriteLine(` Columns: ${idx.columns.join(', ')}`);
Console.WriteLine(` Unique: ${idx.unique}`);
Console.WriteLine(` Primary: ${idx.primaryKey}`);
});
Drop Index
- TypeScript
- Java
- Go
- .NET
await db.schema.dropIndex('users', 'idx_users_old');
db.schema.dropIndex('users', 'idx_users_old');
db.schema.dropIndex('users', 'idx_users_old');
await db.schema.dropIndex('users', 'idx_users_old');
Index Best Practices
1. Index Foreign Keys
Always index foreign key columns:
- TypeScript
- Java
- Go
- .NET
await db.schema.create('orders', {
id: { type: 'uuid', primaryKey: true },
user_id: 'uuid',
}, {
indexes: [
{ fields: ['user_id'] },
],
});
// Now joins are fast
const ordersWithUsers = await db.query({
query: `
SELECT o.*, u.name, u.email
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.status = $1
`,
params: ['pending'],
});
db.schema.create('orders', Map.of(
id: Map.of( "type", "uuid", "primaryKey", true ),
"user_id", "uuid"
), Map.of(
indexes: [
Map.of( fields: ['user_id'] ),
]
));
// Now joins are fast
Map<String, Object> ordersWithUsers = db.query(Map.of(
query: `
SELECT o.*, u.name, u.email
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.status = $1
`,
params: ['pending']
));
db.schema.create('orders', {
id: { "type": "uuid", "primaryKey": true },
"user_id": "uuid",
}, {
indexes: [
{ fields: ['user_id'] },
],
});
// Now joins are fast
ordersWithUsers := db.query({
query: `
SELECT o.*, u.name, u.email
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.status = $1
`,
params: ['pending'],
});
await db.schema.create('orders', {
id: { ["type"] = "uuid", ["primaryKey"] = true },
["user_id"] = "uuid",
}, {
indexes: [
{ fields: ['user_id'] },
],
});
// Now joins are fast
var ordersWithUsers = await db.query({
query: `
SELECT o.*, u.name, u.email
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.status = $1
`,
params: ['pending'],
});
2. Index WHERE Clause Columns
Index columns frequently used in WHERE conditions:
- TypeScript
- Java
- Go
- .NET
// If you often query by status
await db.schema.createIndex('orders', ['status']);
// If you often query by status AND date
await db.schema.createIndex('orders', ['status', 'created_at']);
// If you often query by status
db.schema.createIndex('orders', ['status']);
// If you often query by status AND date
db.schema.createIndex('orders', ['status', 'created_at']);
// If you often query by status
db.schema.createIndex('orders', ['status']);
// If you often query by status AND date
db.schema.createIndex('orders', ['status', 'created_at']);
// If you often query by status
await db.schema.createIndex('orders', ['status']);
// If you often query by status AND date
await db.schema.createIndex('orders', ['status', 'created_at']);
3. Index ORDER BY Columns
Index columns used for sorting:
- TypeScript
- Java
- Go
- .NET
await db.schema.createIndex('posts', ['created_at'], {
name: 'idx_posts_date_desc',
});
// Fast ordered queries
const recentPosts = await db.find({
table: 'posts',
orderBy: [{ column: 'created_at', order: 'DESC' }],
limit: 10,
});
db.schema.createIndex('posts', ['created_at'], Map.of(
"name", "idx_posts_date_desc"
));
// Fast ordered queries
Map<String, Object> recentPosts = db.find(Map.of(
"table", "posts",
orderBy: [Map.of( "column", "created_at", "order", "DESC" )],
"limit", 10
));
db.schema.createIndex('posts', ['created_at'], {
"name": "idx_posts_date_desc",
});
// Fast ordered queries
recentPosts := db.find({
"table": "posts",
orderBy: [{ "column": "created_at", "order": "DESC" }],
"limit": 10,
});
await db.schema.createIndex('posts', ['created_at'], {
["name"] = "idx_posts_date_desc",
});
// Fast ordered queries
var recentPosts = await db.find({
["table"] = "posts",
orderBy: [{ ["column"] = "created_at", ["order"] = "DESC" }],
["limit"] = 10,
});
4. Composite Index Column Order
Most selective column first:
- TypeScript
- Java
- Go
- .NET
// email is highly selective (unique)
// city is less selective (many users per city)
await db.schema.createIndex('users', ['email', 'city'], {
name: 'idx_users_email_city',
});
// This index efficiently supports:
// - WHERE email = 'x@y.com'
// - WHERE email = 'x@y.com' AND city = 'NYC'
// But NOT efficiently for:
// - WHERE city = 'NYC' alone
// email is highly selective (unique)
// city is less selective (many users per city)
db.schema.createIndex('users', ['email', 'city'], Map.of(
"name", "idx_users_email_city"
));
// This index efficiently supports:
// - WHERE email = 'x@y.com'
// - WHERE email = 'x@y.com' AND city = 'NYC'
// But NOT efficiently for:
// - WHERE city = 'NYC' alone
// email is highly selective (unique)
// city is less selective (many users per city)
db.schema.createIndex('users', ['email', 'city'], {
"name": "idx_users_email_city",
});
// This index efficiently supports:
// - WHERE email = 'x@y.com'
// - WHERE email = 'x@y.com' AND city = 'NYC'
// But NOT efficiently for:
// - WHERE city = 'NYC' alone
// email is highly selective (unique)
// city is less selective (many users per city)
await db.schema.createIndex('users', ['email', 'city'], {
["name"] = "idx_users_email_city",
});
// This index efficiently supports:
// - WHERE email = 'x@y.com'
// - WHERE email = 'x@y.com' AND city = 'NYC'
// But NOT efficiently for:
// - WHERE city = 'NYC' alone
Match query patterns:
- TypeScript
- Java
- Go
- .NET
// If you query: WHERE user_id = ? ORDER BY created_at DESC
await db.schema.createIndex('posts', ['user_id', 'created_at']);
// If you query: WHERE user_id = ? ORDER BY created_at DESC
db.schema.createIndex('posts', ['user_id', 'created_at']);
// If you query: WHERE user_id = ? ORDER BY created_at DESC
db.schema.createIndex('posts', ['user_id', 'created_at']);
// If you query: WHERE user_id = ? ORDER BY created_at DESC
await db.schema.createIndex('posts', ['user_id', 'created_at']);
5. Don't Over-Index
Problems with too many indexes:
- Slower INSERT/UPDATE/DELETE operations
- More disk space
- Higher memory usage
- Longer backup/restore times
- TypeScript
- Java
- Go
- .NET
// Bad - indexing everything
await db.schema.createIndex(...); // on name
await db.schema.createIndex(...); // on email
await db.schema.createIndex(...); // on phone
await db.schema.createIndex(...); // on address
await db.schema.createIndex(...); // on city
await db.schema.createIndex(...); // on state
// Result: Fast reads, VERY slow writes
// Good - strategic indexing
await db.schema.createIndex('users', ['email']); // for login
await db.schema.createIndex('users', ['city', 'state']); // for search
// Result: Fast reads, reasonable write speed
// Bad - indexing everything
db.schema.createIndex(...); // on name
db.schema.createIndex(...); // on email
db.schema.createIndex(...); // on phone
db.schema.createIndex(...); // on address
db.schema.createIndex(...); // on city
db.schema.createIndex(...); // on state
// Result: Fast reads, VERY slow writes
// Good - strategic indexing
db.schema.createIndex('users', ['email']); // for login
db.schema.createIndex('users', ['city', 'state']); // for search
// Result: Fast reads, reasonable write speed
// Bad - indexing everything
db.schema.createIndex(...); // on name
db.schema.createIndex(...); // on email
db.schema.createIndex(...); // on phone
db.schema.createIndex(...); // on address
db.schema.createIndex(...); // on city
db.schema.createIndex(...); // on state
// Result: Fast reads, VERY slow writes
// Good - strategic indexing
db.schema.createIndex('users', ['email']); // for login
db.schema.createIndex('users', ['city', 'state']); // for search
// Result: Fast reads, reasonable write speed
// Bad - indexing everything
await db.schema.createIndex(...); // on name
await db.schema.createIndex(...); // on email
await db.schema.createIndex(...); // on phone
await db.schema.createIndex(...); // on address
await db.schema.createIndex(...); // on city
await db.schema.createIndex(...); // on state
// Result: Fast reads, VERY slow writes
// Good - strategic indexing
await db.schema.createIndex('users', ['email']); // for login
await db.schema.createIndex('users', ['city', 'state']); // for search
// Result: Fast reads, reasonable write speed
Rule of thumb:
- 3-5 indexes per table is typical
- Only index columns you actually query
- Remove unused indexes
Common Index Patterns
User Authentication
- TypeScript
- Java
- Go
- .NET
await db.schema.create('users', {
id: { type: 'uuid', primaryKey: true },
email: { type: 'string', length: 255, required: true },
username: { type: 'string', length: 100, required: true },
status: { type: 'string', length: 20 },
last_login: 'timestamp',
}, {
timestamps: true,
indexes: [
{ fields: ['email'], unique: true },
{ fields: ['username'], unique: true },
],
});
// Partial index for active users only (SQL)
await db.schema.createIndex('users', ['last_login'], {
where: "status = 'active'",
name: 'idx_users_active_login',
});
db.schema.create('users', Map.of(
id: Map.of( "type", "uuid", "primaryKey", true ),
email: Map.of( "type", "string", "length", 255, "required", true ),
username: Map.of( "type", "string", "length", 100, "required", true ),
status: Map.of( "type", "string", "length", 20 ),
"last_login", "timestamp"
), Map.of(
"timestamps", true,
indexes: [
Map.of( fields: ['email'], "unique", true ),
Map.of( fields: ['username'], "unique", true ),
]
));
// Partial index for active users only (SQL)
db.schema.createIndex('users', ['last_login'], Map.of(
"where", "status = 'active'",
"name", "idx_users_active_login"
));
db.schema.create('users', {
id: { "type": "uuid", "primaryKey": true },
email: { "type": "string", "length": 255, "required": true },
username: { "type": "string", "length": 100, "required": true },
status: { "type": "string", "length": 20 },
"last_login": "timestamp",
}, {
"timestamps": true,
indexes: [
{ fields: ['email'], "unique": true },
{ fields: ['username'], "unique": true },
],
});
// Partial index for active users only (SQL)
db.schema.createIndex('users', ['last_login'], {
"where": "status = 'active'",
"name": "idx_users_active_login",
});
await db.schema.create('users', {
id: { ["type"] = "uuid", ["primaryKey"] = true },
email: { ["type"] = "string", ["length"] = 255, ["required"] = true },
username: { ["type"] = "string", ["length"] = 100, ["required"] = true },
status: { ["type"] = "string", ["length"] = 20 },
["last_login"] = "timestamp",
}, {
["timestamps"] = true,
indexes: [
{ fields: ['email'], ["unique"] = true },
{ fields: ['username'], ["unique"] = true },
],
});
// Partial index for active users only (SQL)
await db.schema.createIndex('users', ['last_login'], {
["where"] = "status = 'active'",
["name"] = "idx_users_active_login",
});
E-Commerce Orders
- TypeScript
- Java
- Go
- .NET
await db.schema.create('orders', {
id: { type: 'uuid', primaryKey: true },
user_id: 'uuid',
status: { type: 'string', length: 50 },
total: { type: 'decimal', precision: 10, scale: 2 },
items_count: 'integer',
}, {
timestamps: true,
indexes: [
// User's orders, most recent first
{ fields: ['user_id', 'created_at'] },
// Filter by status
{ fields: ['status', 'created_at'] },
],
});
db.schema.create('orders', Map.of(
id: Map.of( "type", "uuid", "primaryKey", true ),
"user_id", "uuid",
status: Map.of( "type", "string", "length", 50 ),
total: Map.of( "type", "decimal", "precision", 10, "scale", 2 ),
"items_count", "integer"
), Map.of(
"timestamps", true,
indexes: [
// User's orders, most recent first
Map.of( fields: ['user_id', 'created_at'] ),
// Filter by status
Map.of( fields: ['status', 'created_at'] ),
]
));
db.schema.create('orders', {
id: { "type": "uuid", "primaryKey": true },
"user_id": "uuid",
status: { "type": "string", "length": 50 },
total: { "type": "decimal", "precision": 10, "scale": 2 },
"items_count": "integer",
}, {
"timestamps": true,
indexes: [
// User's orders, most recent first
{ fields: ['user_id', 'created_at'] },
// Filter by status
{ fields: ['status', 'created_at'] },
],
});
await db.schema.create('orders', {
id: { ["type"] = "uuid", ["primaryKey"] = true },
["user_id"] = "uuid",
status: { ["type"] = "string", ["length"] = 50 },
total: { ["type"] = "decimal", ["precision"] = 10, ["scale"] = 2 },
["items_count"] = "integer",
}, {
["timestamps"] = true,
indexes: [
// User's orders, most recent first
{ fields: ['user_id', 'created_at'] },
// Filter by status
{ fields: ['status', 'created_at'] },
],
});
Social Media Posts
- TypeScript
- Java
- Go
- .NET
await db.schema.create('posts', {
id: { type: 'uuid', primaryKey: true },
user_id: 'uuid',
content: 'text',
tags: 'array',
}, {
timestamps: true,
indexes: [
// User's timeline
{ fields: ['user_id', 'created_at'] },
],
});
// Full-text search on content (database-specific)
// For PostgreSQL:
await db.query({
query: `CREATE INDEX idx_posts_content ON posts USING gin(to_tsvector('english', content))`,
});
db.schema.create('posts', Map.of(
id: Map.of( "type", "uuid", "primaryKey", true ),
"user_id", "uuid",
"content", "text",
"tags", "array"
), Map.of(
"timestamps", true,
indexes: [
// User's timeline
Map.of( fields: ['user_id', 'created_at'] ),
]
));
// Full-text search on content (database-specific)
// For PostgreSQL:
db.query(Map.of(
query: `CREATE INDEX idx_posts_content ON posts USING gin(to_tsvector('english', content))`
));
db.schema.create('posts', {
id: { "type": "uuid", "primaryKey": true },
"user_id": "uuid",
"content": "text",
"tags": "array",
}, {
"timestamps": true,
indexes: [
// User's timeline
{ fields: ['user_id', 'created_at'] },
],
});
// Full-text search on content (database-specific)
// For PostgreSQL:
db.query({
query: `CREATE INDEX idx_posts_content ON posts USING gin(to_tsvector('english', content))`,
});
await db.schema.create('posts', {
id: { ["type"] = "uuid", ["primaryKey"] = true },
["user_id"] = "uuid",
["content"] = "text",
["tags"] = "array",
}, {
["timestamps"] = true,
indexes: [
// User's timeline
{ fields: ['user_id', 'created_at'] },
],
});
// Full-text search on content (database-specific)
// For PostgreSQL:
await db.query({
query: `CREATE INDEX idx_posts_content ON posts USING gin(to_tsvector('english', content))`,
});
Audit Logs
- TypeScript
- Java
- Go
- .NET
await db.schema.create('audit_logs', {
id: { type: 'uuid', primaryKey: true },
entity_type: { type: 'string', length: 100 },
entity_id: 'integer',
action: { type: 'string', length: 50 },
}, {
timestamps: true,
indexes: [
// Query by entity
{ fields: ['entity_type', 'entity_id', 'created_at'] },
// Query by action type
{ fields: ['action', 'created_at'] },
],
});
db.schema.create('audit_logs', Map.of(
id: Map.of( "type", "uuid", "primaryKey", true ),
entity_type: Map.of( "type", "string", "length", 100 ),
"entity_id", "integer",
action: Map.of( "type", "string", "length", 50 )
), Map.of(
"timestamps", true,
indexes: [
// Query by entity
Map.of( fields: ['entity_type', 'entity_id', 'created_at'] ),
// Query by action type
Map.of( fields: ['action', 'created_at'] ),
]
));
db.schema.create('audit_logs', {
id: { "type": "uuid", "primaryKey": true },
entity_type: { "type": "string", "length": 100 },
"entity_id": "integer",
action: { "type": "string", "length": 50 },
}, {
"timestamps": true,
indexes: [
// Query by entity
{ fields: ['entity_type', 'entity_id', 'created_at'] },
// Query by action type
{ fields: ['action', 'created_at'] },
],
});
await db.schema.create('audit_logs', {
id: { ["type"] = "uuid", ["primaryKey"] = true },
entity_type: { ["type"] = "string", ["length"] = 100 },
["entity_id"] = "integer",
action: { ["type"] = "string", ["length"] = 50 },
}, {
["timestamps"] = true,
indexes: [
// Query by entity
{ fields: ['entity_type', 'entity_id', 'created_at'] },
// Query by action type
{ fields: ['action', 'created_at'] },
],
});
Database-Specific Features
PostgreSQL
- TypeScript
- Java
- Go
- .NET
// Partial indexes
await db.schema.createIndex('orders', ['status'], {
where: "status = 'pending'",
});
// GIN index for JSONB (via raw query)
await db.query({
query: `CREATE INDEX idx_users_metadata ON users USING gin(metadata)`,
});
// Concurrent index creation (via raw query)
await db.query({
query: `CREATE INDEX CONCURRENTLY idx_users_email ON users(email)`,
});
// Partial indexes
db.schema.createIndex('orders', ['status'], Map.of(
"where", "status = 'pending'"
));
// GIN index for JSONB (via raw query)
db.query(Map.of(
query: `CREATE INDEX idx_users_metadata ON users USING gin(metadata)`
));
// Concurrent index creation (via raw query)
db.query(Map.of(
query: `CREATE INDEX CONCURRENTLY idx_users_email ON users(email)`
));
// Partial indexes
db.schema.createIndex('orders', ['status'], {
"where": "status = 'pending'",
});
// GIN index for JSONB (via raw query)
db.query({
query: `CREATE INDEX idx_users_metadata ON users USING gin(metadata)`,
});
// Concurrent index creation (via raw query)
db.query({
query: `CREATE INDEX CONCURRENTLY idx_users_email ON users(email)`,
});
// Partial indexes
await db.schema.createIndex('orders', ['status'], {
["where"] = "status = 'pending'",
});
// GIN index for JSONB (via raw query)
await db.query({
query: `CREATE INDEX idx_users_metadata ON users USING gin(metadata)`,
});
// Concurrent index creation (via raw query)
await db.query({
query: `CREATE INDEX CONCURRENTLY idx_users_email ON users(email)`,
});
MySQL
- TypeScript
- Java
- Go
- .NET
// Full-text index (via raw query)
await db.query({
query: `CREATE FULLTEXT INDEX idx_articles_content ON articles(title, content)`,
});
// Prefix indexes for long strings (via raw query)
await db.query({
query: `CREATE INDEX idx_urls_path ON urls(path(255))`,
});
// Full-text index (via raw query)
db.query(Map.of(
query: `CREATE FULLTEXT INDEX idx_articles_content ON articles(title, content)`
));
// Prefix indexes for long strings (via raw query)
db.query(Map.of(
query: `CREATE INDEX idx_urls_path ON urls(path(255))`
));
// Full-text index (via raw query)
db.query({
query: `CREATE FULLTEXT INDEX idx_articles_content ON articles(title, content)`,
});
// Prefix indexes for long strings (via raw query)
db.query({
query: `CREATE INDEX idx_urls_path ON urls(path(255))`,
});
// Full-text index (via raw query)
await db.query({
query: `CREATE FULLTEXT INDEX idx_articles_content ON articles(title, content)`,
});
// Prefix indexes for long strings (via raw query)
await db.query({
query: `CREATE INDEX idx_urls_path ON urls(path(255))`,
});
MongoDB
- TypeScript
- Java
- Go
- .NET
// Sparse index
await db.schema.createIndex('users', ['phone'], {
sparse: true,
});
// TTL index
await db.schema.createIndex('sessions', ['expires_at'], {
expireAfterSeconds: 3600,
});
// Sparse index
db.schema.createIndex('users', ['phone'], Map.of(
"sparse", true
));
// TTL index
db.schema.createIndex('sessions', ['expires_at'], Map.of(
"expireAfterSeconds", 3600
));
// Sparse index
db.schema.createIndex('users', ['phone'], {
"sparse": true,
});
// TTL index
db.schema.createIndex('sessions', ['expires_at'], {
"expireAfterSeconds": 3600,
});
// Sparse index
await db.schema.createIndex('users', ['phone'], {
["sparse"] = true,
});
// TTL index
await db.schema.createIndex('sessions', ['expires_at'], {
["expireAfterSeconds"] = 3600,
});
Performance Monitoring
Check Index Usage (PostgreSQL)
- TypeScript
- Java
- Go
- .NET
const stats = await db.query({
query: `
SELECT
indexname,
idx_scan as scans,
idx_tup_read as tuples_read,
pg_size_pretty(pg_relation_size(indexrelid)) as size
FROM pg_stat_user_indexes
WHERE schemaname = 'public'
ORDER BY idx_scan ASC
`,
});
// Find unused indexes (scans = 0)
const unused = stats.records.filter(s => s.scans === 0);
console.log('Unused indexes:', unused);
Map<String, Object> stats = db.query(Map.of(
query: `
SELECT
indexname,
idx_scan as scans,
idx_tup_read as tuples_read,
pg_size_pretty(pg_relation_size(indexrelid)) as size
FROM pg_stat_user_indexes
WHERE schemaname = 'public'
ORDER BY idx_scan ASC
`
));
// Find unused indexes (scans = 0)
Map<String, Object> unused = stats.records.filter(s => s.scans === 0);
System.out.println('Unused indexes:', unused);
stats := db.query({
query: `
SELECT
indexname,
idx_scan as scans,
idx_tup_read as tuples_read,
pg_size_pretty(pg_relation_size(indexrelid)) as size
FROM pg_stat_user_indexes
WHERE schemaname = 'public'
ORDER BY idx_scan ASC
`,
});
// Find unused indexes (scans = 0)
unused := stats.records.filter(s => s.scans === 0);
fmt.Println('Unused indexes:', unused);
var stats = await db.query({
query: `
SELECT
indexname,
idx_scan as scans,
idx_tup_read as tuples_read,
pg_size_pretty(pg_relation_size(indexrelid)) as size
FROM pg_stat_user_indexes
WHERE schemaname = 'public'
ORDER BY idx_scan ASC
`,
});
// Find unused indexes (scans = 0)
var unused = stats.records.filter(s => s.scans === 0);
Console.WriteLine('Unused indexes:', unused);
Check Index Usage (MySQL)
- TypeScript
- Java
- Go
- .NET
const stats = await db.query({
query: `
SELECT
table_name,
index_name,
count_star as rows_selected
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE object_schema = DATABASE()
AND index_name IS NOT NULL
ORDER BY count_star DESC
`,
});
Map<String, Object> stats = db.query(Map.of(
query: `
SELECT
table_name,
index_name,
count_star as rows_selected
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE object_schema = DATABASE()
AND index_name IS NOT NULL
ORDER BY count_star DESC
`
));
stats := db.query({
query: `
SELECT
table_name,
index_name,
count_star as rows_selected
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE object_schema = DATABASE()
AND index_name IS NOT NULL
ORDER BY count_star DESC
`,
});
var stats = await db.query({
query: `
SELECT
table_name,
index_name,
count_star as rows_selected
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE object_schema = DATABASE()
AND index_name IS NOT NULL
ORDER BY count_star DESC
`,
});
Index Size Analysis
- TypeScript
- Java
- Go
- .NET
// PostgreSQL
const sizes = await db.query({
query: `
SELECT
tablename,
indexname,
pg_size_pretty(pg_relation_size(indexrelid)) as size
FROM pg_stat_user_indexes
ORDER BY pg_relation_size(indexrelid) DESC
`,
});
// PostgreSQL
Map<String, Object> sizes = db.query(Map.of(
query: `
SELECT
tablename,
indexname,
pg_size_pretty(pg_relation_size(indexrelid)) as size
FROM pg_stat_user_indexes
ORDER BY pg_relation_size(indexrelid) DESC
`
));
// PostgreSQL
sizes := db.query({
query: `
SELECT
tablename,
indexname,
pg_size_pretty(pg_relation_size(indexrelid)) as size
FROM pg_stat_user_indexes
ORDER BY pg_relation_size(indexrelid) DESC
`,
});
// PostgreSQL
var sizes = await db.query({
query: `
SELECT
tablename,
indexname,
pg_size_pretty(pg_relation_size(indexrelid)) as size
FROM pg_stat_user_indexes
ORDER BY pg_relation_size(indexrelid) DESC
`,
});
Troubleshooting
Index Not Being Used
Problem: Query is slow despite having an index.
Solutions:
- Check column data types match:
- TypeScript
- Java
- Go
- .NET
// Won't use index if types don't match
// Table: user_id is INTEGER
// Query: WHERE user_id = '123' (STRING)
// Fix: Use correct type
const users = await db.find({
table: 'users',
where: { user_id: 123 }, // INTEGER, not string
});
// Won't use index if types don't match
// Table: user_id is INTEGER
// Query: WHERE user_id = '123' (STRING)
// Fix: Use correct type
Map<String, Object> users = db.find(Map.of(
"table", "users",
where: Map.of( "user_id", 123 ), // INTEGER, not string
));
// Won't use index if types don't match
// Table: user_id is INTEGER
// Query: WHERE user_id = '123' (STRING)
// Fix: Use correct type
users := db.find({
"table": "users",
where: { "user_id": 123 }, // INTEGER, not string
});
// Won't use index if types don't match
// Table: user_id is INTEGER
// Query: WHERE user_id = '123' (STRING)
// Fix: Use correct type
var users = await db.find({
["table"] = "users",
where: { ["user_id"] = 123 }, // INTEGER, not string
});
- Avoid functions on indexed columns:
-- Won't use index
WHERE LOWER(email) = 'alice@example.com'
-- Will use index
WHERE email = 'alice@example.com'
- Use EXPLAIN to analyze:
- TypeScript
- Java
- Go
- .NET
const plan = await db.query({
query: `EXPLAIN ANALYZE SELECT * FROM users WHERE email = $1`,
params: ['alice@example.com'],
});
console.log(plan.records);
// Look for "Index Scan" vs "Seq Scan"
Map<String, Object> plan = db.query(Map.of(
query: `EXPLAIN ANALYZE SELECT * FROM users WHERE email = $1`,
params: ['alice@example.com']
));
System.out.println(plan.records);
// Look for "Index Scan" vs "Seq Scan"
plan := db.query({
query: `EXPLAIN ANALYZE SELECT * FROM users WHERE email = $1`,
params: ['alice@example.com'],
});
fmt.Println(plan.records);
// Look for "Index Scan" vs "Seq Scan"
var plan = await db.query({
query: `EXPLAIN ANALYZE SELECT * FROM users WHERE email = $1`,
params: ['alice@example.com'],
});
Console.WriteLine(plan.records);
// Look for "Index Scan" vs "Seq Scan"
Slow Index Creation
Problem: Creating index takes too long or locks table.
Solutions:
- TypeScript
- Java
- Go
- .NET
// PostgreSQL: Use concurrent index creation
await db.query({
query: `CREATE INDEX CONCURRENTLY idx_large_table ON large_table(column)`,
});
// PostgreSQL: Use concurrent index creation
db.query(Map.of(
query: `CREATE INDEX CONCURRENTLY idx_large_table ON large_table(column)`
));
// PostgreSQL: Use concurrent index creation
db.query({
query: `CREATE INDEX CONCURRENTLY idx_large_table ON large_table(column)`,
});
// PostgreSQL: Use concurrent index creation
await db.query({
query: `CREATE INDEX CONCURRENTLY idx_large_table ON large_table(column)`,
});
When to Create Indexes
| Column Type | Index Recommended |
|---|---|
| Primary key | Automatic |
| Foreign key | Yes |
| Columns in WHERE clauses | Yes |
| Columns in ORDER BY | Consider |
| Columns in JOIN conditions | Yes |
| Low cardinality (few unique values) | Usually no |
| Frequently updated columns | Consider trade-offs |
Next Steps
- Query Optimization - Optimize database queries
- Transactions - Ensure data consistency
- Migrations - Manage schema changes
- Aggregations - Complex data analysis
See Also
- Table Management - Create and manage tables
- Querying Data - Find and filter records
- Best Practices - Database optimization