NebulaDB supports indexing to improve query performance. This guide explains how to use indexes effectively.
Indexes in NebulaDB are data structures that improve the speed of data retrieval operations. They work by creating a reference to the data based on specific fields, allowing the database to find documents without scanning the entire collection.
NebulaDB supports several types of indexes:
You can create indexes when defining a collection or later using the createIndex
method:
import { createDb, IndexType } from '@nebula/core';
import { MemoryAdapter } from '@nebula/adapter-memory';
const db = createDb({ adapter: new MemoryAdapter() });
// Create a collection with indexes
const users = db.collection('users', {
indexes: [
{
name: 'email_idx',
fields: ['email'],
type: IndexType.UNIQUE
},
{
name: 'name_age_idx',
fields: ['name', 'age'],
type: IndexType.COMPOUND
}
]
});
import { IndexType } from '@nebula/core';
// Create a single field index
users.createIndex({
name: 'age_idx',
fields: ['age'],
type: IndexType.SINGLE
});
// Create a unique index
users.createIndex({
name: 'username_idx',
fields: ['username'],
type: IndexType.UNIQUE
});
// Create a compound index
users.createIndex({
name: 'location_idx',
fields: ['country', 'city'],
type: IndexType.COMPOUND
});
NebulaDB automatically uses indexes when processing queries. The query engine analyzes the query and determines if an index can be used to improve performance.
For an index to be used, the query must include the indexed fields with equality conditions:
// Will use the 'email_idx' index
const user = await users.findOne({ email: 'alice@example.com' });
// Will use the 'name_age_idx' compound index
const results = await users.find({
name: 'Alice',
age: 30
});
// Will NOT use the 'name_age_idx' index (range query on age)
const results = await users.find({
name: 'Alice',
age: { $gt: 25 }
});
const indexes = users.getIndexes();
console.log(indexes);
// [
// { name: 'email_idx', fields: ['email'], type: 'unique' },
// { name: 'name_age_idx', fields: ['name', 'age'], type: 'compound' },
// { name: 'age_idx', fields: ['age'], type: 'single' }
// ]
// Drop an index by name
users.dropIndex('age_idx');
$or
, $not
, etc. may not fully utilize indexes// Create a collection with 10,000 documents
const products = db.collection('products');
// Add 10,000 products
for (let i = 0; i < 10000; i++) {
await products.insert({
name: `Product ${i}`,
category: ['A', 'B', 'C'][i % 3],
price: Math.floor(Math.random() * 1000),
inStock: Math.random() > 0.5
});
}
// Query without index
console.time('Without index');
const resultsWithoutIndex = await products.find({ category: 'B', inStock: true });
console.timeEnd('Without index');
// Create an index
products.createIndex({
name: 'category_stock_idx',
fields: ['category', 'inStock'],
type: IndexType.COMPOUND
});
// Query with index
console.time('With index');
const resultsWithIndex = await products.find({ category: 'B', inStock: true });
console.timeEnd('With index');