@statedelta-libs/gsindex
v0.0.1
Published
High-performance hierarchical indexing with O(1) lookup and GSI-style secondary indexes
Maintainers
Readme
@statedelta-libs/gsindex
High-performance hierarchical indexing with O(1) lookup and GSI-style secondary indexes.
Features
- O(1) Index Lookup - HashMap-based hierarchical path indexing
- Wildcards - Support for
*patterns at any level - Secondary Indexes (GSI) - Named indexes for alternative query patterns
- Query Cache - Up to 33M ops/s for repeated queries
- Zero Dependencies - Pure TypeScript, no external deps
- ~2KB gzipped - Smaller than alternatives (TinyBase 5-12KB, Orama 5-13KB, LokiJS 21KB)
Installation
pnpm add @statedelta-libs/gsindexQuick Start
import { createStore, type Indexable } from '@statedelta-libs/gsindex';
// Define your item type
interface GameRule extends Indexable {
name: string;
damage: number;
}
// Create store
const store = createStore<GameRule>({
cache: { enabled: true, maxSize: 1000 }
});
// Add items with hierarchical paths
store.add({
id: 'fireball',
index: 'game:combat:spell',
indexes: { byType: 'fire', byOwner: 'player1' },
name: 'Fireball',
damage: 50
});
store.add({
id: 'ice-shard',
index: 'game:combat:spell',
indexes: { byType: 'ice', byOwner: 'player1' },
name: 'Ice Shard',
damage: 30
});
// O(1) exact lookup
store.getByIndex('game:combat:spell');
// Wildcard queries
store.getCandidates({ path: 'game:combat:*' }); // All combat actions
store.getCandidates({ path: 'game:*' }); // All game actions
// GSI queries
store.getCandidates({ path: 'fire', indexName: 'byType' }); // All fire spells
store.getBySecondaryIndex('byOwner', 'player1'); // Player1's itemsIndex Path
Hierarchical string with : separator (configurable):
// Exact match
index: 'shop:cart:validate'
// Wildcards (only at the end)
index: 'shop:cart:*' // Any event in shop:cart
index: 'shop:*' // All of shop realm
index: '*' // Global (matches everything)Secondary Indexes (GSI)
Named indexes for alternative query dimensions:
store.add({
id: 'item-1',
index: 'game:action:attack',
indexes: {
byType: 'melee',
byOwner: 'player1',
byElement: 'fire'
}
});
// Query by any dimension
store.getBySecondaryIndex('byType', 'melee');
store.getBySecondaryIndex('byOwner', 'player1');
store.getCandidates({ path: 'fire', indexName: 'byElement' });API
createStore(options?)
const store = createStore<T>({
cache: {
enabled: true, // Enable query caching
maxSize: 1000, // Max cached queries (LRU)
ttl: 60000 // Cache TTL in ms
},
separator: ':' // Path separator (default: ':')
});CRUD
store.add(item); // Add item
store.addMany(items); // Add multiple
store.get(id); // Get by ID
store.has(id); // Check exists
store.update(id, updates); // Update item
store.remove(id); // Remove item
store.removeMany(ids); // Remove multiple
store.clear(); // Clear all
store.all(); // Get all items
store.size; // Item countQueries
// Exact path - O(1)
store.getByIndex('shop:cart:validate');
// With wildcards - O(1) per matching prefix
store.getCandidates({ path: 'shop:cart:validate' });
store.getCandidates({ path: 'shop:cart:*' });
store.getCandidates({ path: 'shop:*' });
store.getCandidates({ path: '*' });
// Secondary index
store.getBySecondaryIndex('byType', 'monster');
store.getCandidates({ path: 'monster', indexName: 'byType' });
// List secondary indexes
store.getSecondaryIndexNames(); // ['byType', 'byOwner', ...]
store.getSecondaryIndexValues('byType'); // ['monster', 'spell', ...]Enable/Disable
store.enable(id); // Enable item
store.disable(id); // Disable item
store.isEnabled(id); // Check if enabledCache
store.getCacheStats(); // { hits, misses, size, hitRate, enabled }
store.clearCache(); // Clear query cacheStats
store.stats();
// {
// total: 100,
// exactPaths: 80,
// prefixPaths: 15,
// globalWildcards: 5,
// secondaryIndexes: { byType: 50, byOwner: 100 }
// }Performance
| Operation | Performance | |-----------|-------------| | getByIndex (exact) | ~2.7M ops/s | | getCandidates (cache HIT) | ~33M ops/s | | getBySecondaryIndex | ~168K ops/s | | Scale test (constant) | ~1M ops/s |
Scale Invariance
Performance is constant regardless of store size when using indexed paths:
| Total Items | Matching | Performance | |-------------|----------|-------------| | 100 | 20 | ~1M ops/s | | 1,000 | 20 | ~1M ops/s | | 10,000 | 20 | ~1M ops/s |
Bundle Size
| Format | Size | |--------|------| | ESM (minified) | 6.5 KB | | ESM (gzip) | ~2 KB | | CJS (minified) | 6.5 KB | | CJS (gzip) | ~2 KB |
TypeScript
import {
// Types
type Indexable,
type IndexPath,
type IndexKey,
type SecondaryIndexes,
type IndexQuery,
type CacheConfig,
type CacheStats,
type StoreOptions,
type StoreStats,
type ItemUpdate,
// Constants
WILDCARD,
INDEX_SEPARATOR,
// Helpers
parseIndexPath,
buildIndexPath,
isWildcardPath,
getWildcardPrefix,
getQueryPrefixes,
matchesPath,
// Store
GSIndexStore,
createStore,
} from '@statedelta-libs/gsindex';License
MIT
