DocsGuidesAdaptive Indexing

Adaptive Indexing

TopGun’s Adaptive Indexing system tracks your query patterns and either suggests indexes (production mode) or automatically creates them (development mode). This eliminates guesswork and ensures optimal performance.

Index Advisor

Get smart suggestions based on actual query patterns in production.

Auto-Indexing

Zero-config development mode that creates indexes on demand.

Production Safe

Advisor mode suggests but never modifies - you stay in control.

Index Advisor (Production Mode)

The Index Advisor tracks query patterns and generates recommendations. It never modifies your indexes - you review and apply suggestions manually.

Index Advisor Setup
import { IndexedLWWMap, simpleAttribute, HLC } from '@topgunbuild/core';

const hlc = new HLC('node-1');
const products = new IndexedLWWMap<string, Product>(hlc, {
  adaptiveIndexing: {
    advisor: {
      enabled: true,
      minQueryCount: 10,    // Suggest after 10 queries
      minAverageCost: 1     // Suggest if avg cost > 1ms
    }
  }
});

// Application runs... queries accumulate
for (let i = 0; i < 100; i++) {
  products.queryValues({ type: 'eq', attribute: 'category', value: 'electronics' });
  products.queryValues({ type: 'between', attribute: 'price', from: 10, to: 100 });
}

// Get suggestions
const suggestions = products.getIndexSuggestions();
console.log(suggestions);
// [
//   {
//     attribute: 'category',
//     indexType: 'hash',
//     reason: 'Queried 100× with 2.5ms average cost. Expected 50× speedup.',
//     priority: 'high',
//     estimatedBenefit: 50,
//     queryCount: 100
//   },
//   {
//     attribute: 'price',
//     indexType: 'navigable',
//     reason: 'Queried 100× with 3.1ms average cost. Expected 30× speedup.',
//     priority: 'high',
//     estimatedBenefit: 30,
//     queryCount: 100
//   }
// ]

Applying Suggestions

Review suggestions and apply them to your codebase:

Reviewing and Applying Suggestions
// Review suggestions
const suggestions = products.getIndexSuggestions();

for (const suggestion of suggestions) {
  console.log(`Suggestion: Add ${suggestion.indexType} index on '${suggestion.attribute}'`);
  console.log(`  Reason: ${suggestion.reason}`);
  console.log(`  Priority: ${suggestion.priority}`);
}

// Apply high-priority suggestions manually
const categoryAttr = simpleAttribute<Product, string>('category', p => p.category);
const priceAttr = simpleAttribute<Product, number>('price', p => p.price);

products.addHashIndex(categoryAttr);
products.addNavigableIndex(priceAttr);

// Reset statistics after applying
products.resetQueryStatistics();

Suggestion Properties

PropertyDescription
attributeField name to index
indexTypeRecommended type: hash, navigable, or inverted
reasonHuman-readable explanation
priorityhigh, medium, or low
estimatedBenefitExpected speedup multiplier
queryCountNumber of queries that triggered suggestion
averageCostAverage query time before indexing

Auto-Indexing (Development Mode)

Auto-Indexing creates indexes automatically after a threshold number of queries. Great for development and prototyping.

Important: Auto-indexing requires pre-registered attributes. Register attributes before running queries, or the system won’t know how to extract values.

Auto-Indexing Setup
const products = new IndexedLWWMap<string, Product>(hlc, {
  adaptiveIndexing: {
    autoIndex: {
      enabled: true,
      threshold: 10,          // Auto-create after 10 queries
      maxIndexes: 20,         // Safety limit
      onIndexCreated: (attr, type) => {
        console.log(`✅ Auto-created ${type} index on '${attr}'`);
      }
    }
  }
});

// IMPORTANT: Register attributes before querying
// Auto-index needs to know how to extract values
products.registerAttribute(
  simpleAttribute<Product, string>('category', p => p.category),
  ['hash']  // Allow only hash index for this attribute
);

products.registerAttribute(
  simpleAttribute<Product, number>('price', p => p.price),
  ['hash', 'navigable']  // Allow hash or navigable
);

// Queries 1-9: Full scan (slow)
// Query 10+: Auto-creates index, becomes fast
for (let i = 0; i < 20; i++) {
  products.queryValues({ type: 'eq', attribute: 'category', value: 'electronics' });
}
// Console: ✅ Auto-created hash index on 'category'

Configuration Options

OptionDefaultDescription
threshold10Queries before auto-creating index
maxIndexes20Safety limit on total indexes
onIndexCreated-Callback when index is created

Production

Production Configuration
// Production: Advisor only (safe, no automatic changes)
const products = new IndexedLWWMap<string, Product>(hlc, {
  adaptiveIndexing: {
    advisor: { enabled: true }
    // autoIndex not enabled - production safe
  }
});

// Check suggestions periodically (e.g., in admin dashboard)
function getIndexRecommendations() {
  const suggestions = products.getIndexSuggestions({
    minQueryCount: 100,      // Higher threshold for production
    minAverageCost: 5,       // Only slow queries
    maxSuggestions: 10       // Top 10 recommendations
  });

  return suggestions.filter(s => s.priority === 'high');
}

Development

Development Configuration
// Development: Auto-index for fast iteration
const products = new IndexedLWWMap<string, Product>(hlc, {
  adaptiveIndexing: {
    advisor: { enabled: true },
    autoIndex: {
      enabled: process.env.NODE_ENV === 'development',
      threshold: 5,           // Lower threshold for quick feedback
      maxIndexes: 30,
      onIndexCreated: (attr, type) => {
        console.log(`[DEV] Auto-indexed '${attr}' with ${type}`);
      }
    }
  }
});

// Register all attributes you might query
const attrs = [
  simpleAttribute<Product, string>('category', p => p.category),
  simpleAttribute<Product, string>('status', p => p.status),
  simpleAttribute<Product, number>('price', p => p.price),
  simpleAttribute<Product, string>('name', p => p.name)
];

attrs.forEach(attr => products.registerAttribute(attr));

Query Statistics

Monitor query patterns with detailed statistics:

Query Statistics
// Get detailed query statistics
const stats = products.getQueryStatistics();

console.table(stats.map(s => ({
  attribute: s.attribute,
  queryType: s.queryType,
  count: s.queryCount,
  avgCost: `${s.averageCost.toFixed(2)}ms`,
  hasIndex: s.hasIndex ? 'Yes' : 'No'
})));

// Example output:
// ┌───────────┬───────────┬───────┬─────────┬──────────┐
// │ attribute │ queryType │ count │ avgCost │ hasIndex │
// ├───────────┼───────────┼───────┼─────────┼──────────┤
// │ category  │ eq        │ 150   │ 0.02ms  │ Yes      │
// │ price     │ between   │ 75    │ 2.50ms  │ No       │
// │ status    │ eq        │ 45    │ 1.80ms  │ No       │
// └───────────┴───────────┴───────┴─────────┴──────────┘

Default Indexing Strategy

For simple use cases, enable automatic indexing of all scalar fields:

Default Indexing
// 'scalar' mode: Auto-index all top-level primitive fields
const products = new IndexedLWWMap<string, Product>(hlc, {
  defaultIndexing: 'scalar'
});

// First record triggers automatic hash indexes on:
// - category (string)
// - status (string)
// - price (number)
// - inStock (boolean)
// NOT indexed: nested objects, arrays, functions

products.set('p1', {
  id: 'p1',
  category: 'electronics',  // → HashIndex
  status: 'active',         // → HashIndex
  price: 99,                // → HashIndex (not NavigableIndex)
  inStock: true,            // → HashIndex
  metadata: { weight: 500 } // NOT indexed (nested object)
});
StrategyBehavior
'none'No automatic indexing (default)
'scalar'Hash index on all primitive fields
'all'Index nested fields too (not recommended)

How Index Type is Selected

The advisor maps query types to optimal index types:

Index Type Selection
// Query type → Recommended index type
//
// eq, neq, in, has        → HashIndex
// gt, gte, lt, lte, between → NavigableIndex
// contains, containsAll, containsAny → InvertedIndex

// The advisor detects patterns and suggests accordingly:
products.queryValues({ type: 'eq', attribute: 'status', value: 'active' });
// → Suggests HashIndex

products.queryValues({ type: 'between', attribute: 'price', from: 10, to: 100 });
// → Suggests NavigableIndex

products.queryValues({ type: 'contains', attribute: 'name', value: 'wireless' });
// → Suggests InvertedIndex

Performance Impact

MetricValue
Tracking overhead< 1% of query time
Memory for statistics~100 bytes per attribute
Suggestion generation< 1ms

Best Practices

  1. Start with Advisor in production

    • Safe, no automatic changes
    • Review suggestions weekly
  2. Use Auto-Index in development

    • Fast iteration
    • Export suggestions for production
  3. Register attributes explicitly

    • Required for auto-indexing
    • Allows type-safe index creation
  4. Set reasonable thresholds

    • Production: 100+ queries, 5ms+ cost
    • Development: 5-10 queries
  5. Monitor and reset

    • Check statistics periodically
    • Reset after applying indexes

Migration from Manual Indexing

Before vs After
// Before: Manual index setup
const products = new IndexedLWWMap<string, Product>(hlc);
products.addHashIndex(categoryAttr);  // Guessed this was needed

// After: Data-driven indexing
const products = new IndexedLWWMap<string, Product>(hlc, {
  adaptiveIndexing: { advisor: { enabled: true } }
});

// Run for a week, then check
const suggestions = products.getIndexSuggestions();
// Apply only what's actually needed based on real usage

Next Steps