qube
v3.1.0
Published
Powerful in-memory analytics cube (OLAP) with N-dimensional hypercube support
Maintainers
Readme
╔═══════════════╗
║ ◇ QUBE ◇ ║
╚═══════════════╝Powerful in-memory OLAP cube for JavaScript/TypeScript
✨ Features
- 🔷 Hypercube Support — 1 to N dimensions (not limited to 3!)
- 📊 Rich Aggregations — sum, count, min, max, average, first, last, distinct, variance, stddev
- 🔍 Flexible Querying — slice, dice, query, rollup, drilldown, filter
- 📋 Dimension Operations — enumerate, get dimension info
- 💾 Serialization — save and restore cubes
- 📝 TypeScript — full type definitions included
📦 Installation
npm install qube🚀 Quick Start
import { Qube } from 'qube';
// or: const { Qube } = require('qube');
const qube = new Qube({
measures: [
{ type: 'sum', key: 'sales', name: 'total_sales' },
{ type: 'count', key: 'sales', name: 'transaction_count' },
{ type: 'average', key: 'sales', name: 'avg_sales' }
],
dimensions: [
{ type: 'string', key: 'year' },
{ type: 'string', key: 'location' },
{ type: 'string', key: 'product' }
]
});
// Add data
qube.push([
{ year: '2023', location: 'Seattle', product: 'Apple', sales: 100 },
{ year: '2023', location: 'Seattle', product: 'Orange', sales: 80 },
{ year: '2023', location: 'Portland', product: 'Apple', sales: 120 },
{ year: '2024', location: 'Seattle', product: 'Apple', sales: 150 },
]);
// Query total
qube.one({ measure: 'total_sales' }); // → 450
// Query with partial dimensions (slice)
qube.query({
measure: 'total_sales',
dimensions: { year: '2023', product: 'Apple' }
}); // → 220
// Query specific cell (dice)
qube.dice({
measure: 'total_sales',
dimensions: { year: '2023', location: 'Seattle', product: 'Apple' }
}); // → 100📊 Measure Types
| Type | Description |
|------|-------------|
| sum | Sum of all values |
| count | Count of records |
| min | Minimum value |
| max | Maximum value |
| average | Average of values |
| first | First value encountered |
| last | Last value encountered |
| distinct | Count of unique values |
| variance | Population variance |
| stddev | Population standard deviation |
📖 API Reference
Constructor
const qube = new Qube({ measures, dimensions })| Option | Description |
|--------|-------------|
| measures | Array of { type, key, name } |
| dimensions | Array of { type, key } |
Properties
| Property | Description |
|----------|-------------|
| rowCount | Number of rows pushed |
| dimensionCount | Number of dimensions |
Methods
Data Operations
qube.push(rows) // Add rows
qube.clear() // Clear all dataQuerying
// Get total for a measure
qube.one({ measure: 'total_sales' })
// Query with any combination of dimensions
qube.query({ measure: 'total_sales', dimensions: { year: '2023' } })
// Slice (alias for query)
qube.slice({ measure: 'total_sales', dimensions: { year: '2023', location: 'Seattle' } })
// Dice - query specific cell (all dimensions provided)
qube.dice({ measure: 'total_sales', dimensions: { year: '2023', location: 'Seattle', product: 'Apple' } })Rollup & Drilldown
// Rollup - aggregate by dimension with totals
qube.rollup('year', { measure: 'total_sales' })
// → [{ dimensions: { year: '2023' }, value: 300 },
// { dimensions: { year: '__TOTAL__' }, value: 450 }]
// Drilldown - break down by a dimension
qube.drilldown('product', { measure: 'total_sales', dimensions: { year: '2023' } })
// → [{ dimensions: { year: '2023', product: 'Apple' }, value: 220 }, ...]Enumeration
// Get unique values for a dimension
qube.enumerateDimension('year') // → ['2023', '2024']
// Query across all values of a dimension
qube.queryWithEnumeration('year', { measure: 'total_sales' })
// → [{ value: 300, year: '2023' }, { value: 150, year: '2024' }]Filter
// Filter by dimension values
qube.filter({ dimensions: { year: '2023' } })
// Filter by multiple values
qube.filter({ dimensions: { year: ['2023', '2024'] } })
// Filter with custom predicate
qube.filter({ predicate: (dims, measures) => measures.total_sales > 100 })Metadata
qube.getDimensionInfo()
// → [{ key: 'year', type: 'string', cardinality: 2, values: ['2023', '2024'] }, ...]
qube.getDimensionInfo('year')
// → { key: 'year', type: 'string', cardinality: 2, values: ['2023', '2024'] }
qube.getMeasureInfo()
// → [{ name: 'total_sales', key: 'sales', type: 'sum' }, ...]Serialization
// Basic serialization
const data = qube.serializeCube() // Serialize to object
const restored = Qube.fromCube(data) // Restore from object
// Compressed serialization (gzip) - ~80% smaller
const compressed = await qube.serializeCompressed() // Returns Buffer
const restored = await Qube.fromCompressed(compressed) // Restore from Buffer
// Sync versions also available
const compressed = qube.serializeCompressedSync()
const restored = Qube.fromCompressedSync(compressed)
// Save to file
const fs = require('fs');
fs.writeFileSync('cube.gz', qube.serializeCompressedSync());
const loaded = Qube.fromCompressedSync(fs.readFileSync('cube.gz'));🔷 Hypercube Example (N Dimensions)
const qube = new Qube({
measures: [{ type: 'sum', key: 'revenue', name: 'total_revenue' }],
dimensions: [
{ type: 'string', key: 'year' },
{ type: 'string', key: 'quarter' },
{ type: 'string', key: 'region' },
{ type: 'string', key: 'product' },
{ type: 'string', key: 'channel' } // 5 dimensions!
]
});
qube.push([
{ year: '2024', quarter: 'Q1', region: 'West', product: 'Widget', channel: 'Online', revenue: 1000 },
// ... more data
]);
// Query across any combination of dimensions
qube.query({
measure: 'total_revenue',
dimensions: { year: '2024', region: 'West' }
});⚠️ Limitations
- In-memory storage only
- All data must fit in memory
🤝 Contributing
Contributions welcome! Please open an issue to discuss changes first.
📄 License
MIT
