Powerful in-memory analytics cube (OLAP) with N-dimensional hypercube support
npm install qube


```
╔═══════════════╗
║ ◇ QUBE ◇ ║
╚═══════════════╝
> Powerful in-memory OLAP cube for JavaScript/TypeScript
---
- 🔷 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
`bash`
npm install qube
`javascript
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
`
| 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 |
`javascript`
const qube = new Qube({ measures, dimensions })
| Option | Description |
|--------|-------------|
| measures | Array of { type, key, name } |dimensions
| | Array of { type, key } |
| Property | Description |
|----------|-------------|
| rowCount | Number of rows pushed |dimensionCount
| | Number of dimensions |
#### Data Operations
`javascript`
qube.push(rows) // Add rows
qube.clear() // Clear all data
#### Querying
`javascript
// 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
`javascript
// 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
`javascript
// 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
`javascript
// 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
`javascript
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
`javascript
// 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'));
`
`javascript
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' }
});
``
- In-memory storage only
- All data must fit in memory
Contributions welcome! Please open an issue to discuss changes first.
MIT