mongoose-timeseries
Advanced tools
Comparing version 1.1.0 to 1.2.0
110
index.js
@@ -5,7 +5,11 @@ var mongoose = require('mongoose') | ||
var TimeSeriesSchema = new Schema({ | ||
timestamp: { type: Date, default: Date.now, required: true, index: true }, | ||
interval: { type: String, enum: ['minute', 'hour', 'day', 'month'], required: true }, | ||
date: { | ||
start: { type: Date, index: true }, | ||
end: { type: Date, index: true } | ||
}, | ||
resolution: { type: String, enum: ['minute', 'hour', 'day', 'month'], required: true }, | ||
count: { type: Number }, | ||
key: {}, | ||
data: {} | ||
}, { timestamps: { createdAt: 'created', updatedAt: 'updated' } }) | ||
}) | ||
@@ -17,12 +21,12 @@ module.exports = exports = function timeSeriesPlugin (schema, options) { | ||
} | ||
if (!options.name) { | ||
throw new Error('you must specify the timeseries plugin options attribute "name"') | ||
} else if (!options.intervals) { | ||
throw new Error('you must specify the timeseries plugin options attribute "intervals"') | ||
} else if (!options.keys) { | ||
throw new Error('you must specify the timeseries plugin options attribute "keys"') | ||
if (!options.target) { | ||
throw new Error('you must specify the timeseries plugin options attribute "target"') | ||
} else if (!options.resolutions) { | ||
throw new Error('you must specify the timeseries plugin options attribute "resolutions"') | ||
} else if (!options.key) { | ||
throw new Error('you must specify the timeseries plugin options attribute "key"') | ||
} | ||
// New model name for every plugin usage | ||
var TimeSeriesModel = mongoose.model(options.name, TimeSeriesSchema) | ||
// New model target for every plugin usage | ||
var TimeSeriesModel = mongoose.model(options.target, TimeSeriesSchema) | ||
@@ -32,15 +36,13 @@ schema.post('save', function () { | ||
var documentDate | ||
if (options.dateKey) { | ||
documentDate = document[options.dateKey] | ||
} else { | ||
documentDate = document._id.getTimeStamp() | ||
var documentDate = document._id.getTimestamp() | ||
if (options.dateField) { | ||
documentDate = document[options.dateField] | ||
} | ||
var timestamps = {} | ||
for (var i = 0; i < options.intervals.length; i++) { | ||
var interval = options.intervals[i] | ||
switch (interval) { | ||
for (var i = 0; i < options.resolutions.length; i++) { | ||
var resolution = options.resolutions[i] | ||
switch (resolution) { | ||
case 'minute': | ||
timestamps[interval] = new Date( | ||
timestamps[resolution] = new Date( | ||
documentDate.getFullYear(), | ||
@@ -54,3 +56,3 @@ documentDate.getMonth(), | ||
case 'hour': | ||
timestamps[interval] = new Date( | ||
timestamps[resolution] = new Date( | ||
documentDate.getFullYear(), | ||
@@ -63,3 +65,3 @@ documentDate.getMonth(), | ||
case 'day': | ||
timestamps[interval] = new Date( | ||
timestamps[resolution] = new Date( | ||
documentDate.getFullYear(), | ||
@@ -71,3 +73,3 @@ documentDate.getMonth(), | ||
case 'month': | ||
timestamps[interval] = new Date( | ||
timestamps[resolution] = new Date( | ||
documentDate.getFullYear(), | ||
@@ -80,9 +82,14 @@ documentDate.getMonth() | ||
var keys = {} | ||
for (i = 0; i < options.keys.length; i++) { | ||
var key = options.keys[i].key | ||
if (options.keys[i].value) { | ||
keys[key] = options.keys[i].value(document) | ||
var key = {} | ||
var keyNames = Object.keys(options.key) | ||
var keyObject = options.key | ||
for (i = 0; i < keyNames.length; i++) { | ||
var keyName = keyNames[i] | ||
var keyValue = keyObject[keyName] | ||
if (typeof keyValue === 'function') { | ||
key[keyName] = keyValue(document) | ||
} else if (keyValue === 1) { | ||
key[keyName] = document[keyName] | ||
} else { | ||
keys[key] = document[key] | ||
throw new Error('invalid timeseries plugin option key value (must be 1 or function)') | ||
} | ||
@@ -92,15 +99,17 @@ } | ||
var inc = {} | ||
// count document itself before custom operations | ||
inc['count'] = 1 | ||
// count document itself before custom sumpoints | ||
inc['data.count'] = 1 | ||
if (options.sums) { | ||
for (i = 0; i < options.sums.length; i++) { | ||
var sumpoint = options.sums[i] | ||
var keyBase = 'data.' + sumpoint.name | ||
if (has(document, sumpoint.key)) { | ||
inc[keyBase + '.sum'] = get(document, sumpoint.key) | ||
inc[keyBase + '.count'] = 1 | ||
if (options.data) { | ||
var dataNames = Object.keys(options.data) | ||
var dataObject = options.data | ||
for (i = 0; i < dataNames.length; i++) { | ||
var dataName = dataNames[i] | ||
var dataValue = dataObject[dataName] | ||
var keyBase = 'data.' + dataName | ||
if (dataValue.operation === 'sum') { | ||
if (has(document, dataValue.source)) { | ||
inc[keyBase + '.sum'] = get(document, dataValue.source) | ||
inc[keyBase + '.count'] = 1 | ||
} | ||
} | ||
@@ -110,14 +119,19 @@ } | ||
for (i = 0; i < options.intervals.length; i++) { | ||
interval = options.intervals[i] | ||
for (i = 0; i < options.resolutions.length; i++) { | ||
resolution = options.resolutions[i] | ||
var findBy = { | ||
timestamp: timestamps[interval], | ||
interval: interval, | ||
key: keys | ||
'date.start': timestamps[resolution], | ||
resolution: resolution, | ||
key: key | ||
} | ||
// Upsert interval | ||
var set = { | ||
'date.end': new Date() | ||
} | ||
// Upsert resolution | ||
TimeSeriesModel.findOneAndUpdate(findBy, { | ||
$inc: inc | ||
$inc: inc, | ||
$set: set | ||
}, { upsert: true, new: true }, function (err, datapoint) { | ||
@@ -124,0 +138,0 @@ if (err) console.log(err) |
{ | ||
"name": "mongoose-timeseries", | ||
"version": "1.1.0", | ||
"version": "1.2.0", | ||
"description": "a time series data and analytics storage plugin for Mongoose", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
122
README.md
@@ -39,25 +39,18 @@ # mongoose-timeseries | ||
YourDocumentSchema.plugin(timeseries, { | ||
name: 'TimeSeriesDocument', | ||
dateKey: 'date', | ||
intervals: ['minute', 'day'], | ||
keys: [ | ||
{ | ||
key: 'attr1' | ||
}, | ||
{ | ||
key: 'attr2' | ||
}, | ||
{ | ||
key: 'info', | ||
value: function(doc) { | ||
return doc.sub1 + doc.sub2 + doc.sub3 | ||
} | ||
target: 'TimeSeriesDocument', | ||
dateField: 'date', | ||
resolutions: ['minute', 'day'], | ||
key: { | ||
attr1: 1, | ||
attr2: 1, | ||
info: function(doc) { | ||
return doc.info.sub1 + doc.info.sub2 + doc.info.sub3 | ||
} | ||
], | ||
sums: [ | ||
{ | ||
name: 'metric', | ||
key: 'analytics.metric' | ||
}, | ||
data: { | ||
metric: { | ||
source: 'analytics.metric', | ||
operation: 'sum' | ||
} | ||
] | ||
} | ||
}) | ||
@@ -71,16 +64,17 @@ ``` | ||
{ | ||
interval: 'day', | ||
timestamp: Mon Aug 01 2016 00:00:00 GMT-0600(MDT), | ||
created: Mon Aug 01 2016 00:51:09 GMT-0600(MDT), | ||
updated: Wed Aug 31 2016 22:19:42 GMT-0600(MDT), | ||
date: { | ||
start: Mon Aug 01 2016 00:00:00 GMT-0600(MDT), | ||
end: Mon Aug 01 2016 23:58:42 GMT-0600(MDT) | ||
} | ||
resolution: 'day', | ||
count: 5, | ||
data: { | ||
count: 5, | ||
metric: { | ||
sum: 697, | ||
count: 5 | ||
count: 5, | ||
sum: 697 | ||
} | ||
}, | ||
key: { | ||
attr1: 55931aba4f3b26d63810a55d, | ||
attr2: 5536011b00a57af8243d7e5b, | ||
attr1: '55931aba4f3b26d63810a55d', | ||
attr2: '5536011b00a57af8243d7e5b', | ||
info: 'ABC' | ||
@@ -110,5 +104,5 @@ }, | ||
```js | ||
name(String) | ||
target(String) | ||
``` | ||
The collection name of the specific time series data. | ||
The MongoDB collection name (destination) of the specific time series data. | ||
@@ -118,6 +112,6 @@ --- | ||
```js | ||
dateKey(String) | ||
dateField(String) | ||
``` | ||
The custom date key of your schema (if applicable). | ||
If not set, defaults to ```document._id.getTimeStamp()``` | ||
The custom date field of your schema (if applicable). | ||
If not set, defaults to ```document._id.getTimestamp()``` | ||
@@ -127,5 +121,5 @@ --- | ||
```js | ||
intervals(Array) | ||
resolutions(Array) | ||
``` | ||
The time series intervals you want: | ||
The time series resolutions you want: | ||
Can include any or all of ['minute', 'hour', 'day', 'month'] | ||
@@ -136,12 +130,10 @@ | ||
```js | ||
keys(Array) | ||
key(Object) | ||
``` | ||
The unique information you'd like your time series to separate and store. | ||
--- | ||
```js | ||
keys.key(String) | ||
key.'attribute'(Number | Function) | ||
``` | ||
The name of the key. | ||
For each key, use the number '1' to relay the name, or a function that returns your value to store on the key. | ||
@@ -151,32 +143,22 @@ --- | ||
```js | ||
keys.value(Function) | ||
data(Object) | ||
``` | ||
The function that returns your a value to store on the associated key: | ||
*Defaults to the name of the key* | ||
The data you'd like to keep track of. | ||
--- | ||
```js | ||
sums(Array) | ||
data.'attribute'.source(String) | ||
``` | ||
The sums you'd like to keep track of. | ||
The source of the parameter you're tracking. | ||
Can be nested like: | ||
```js | ||
'analytics.metrics.metric1' | ||
``` | ||
--- | ||
```js | ||
sum.name(String) | ||
data.'attribute'.operation(String) | ||
``` | ||
The name of the sum. | ||
The operation to perform. Currently only `'sum'` is supported. | ||
--- | ||
```js | ||
sum.key(String) | ||
``` | ||
The key of the sum. | ||
Can be nested like: | ||
```js | ||
'analytics.metrics.metric1' | ||
``` | ||
## Using the Time Series Data | ||
@@ -190,4 +172,4 @@ | ||
TimeSeriesAnalyticsModel.find({ | ||
interval: 'day', | ||
timestamp: { | ||
resolution: 'day', | ||
'date.start': { | ||
$gte: startDateFromUI, | ||
@@ -230,4 +212,16 @@ $lte: endDateFromUI | ||
## Assumptions | ||
- Original source documents are a continual stream of data being dumped | ||
- Documents in the source time-series collection are never themselves found and updated | ||
## To-do | ||
- [ ] Tests | ||
- [ ] More operations beyond count and sum if possible (average, max, min) | ||
- [ ] Auto-indexing | ||
- [ ] Auto-remove (removes source time-series documents automatically after a set interval) | ||
## License | ||
[MIT](LICENSE) |
11434
159
219