"use strict";
// Import external names locally
var Shared,
Db,
Collection,
DbDocument;
Shared = require('./Shared');
/**
* The Overview class provides a queryable interface for data aggregation in realtime.
* @class
*/
var Overview = function () {
this.init.apply(this, arguments);
};
/**
* Initialises the overview, called by the class as it is instantiated.
* @constructor
*/
Overview.prototype.init = function (name) {
var self = this;
this._name = name;
this._data = new DbDocument('__FDB__dc_data_' + this._name);
this._collData = new Collection();
this._sources = [];
this._sourceDroppedWrap = function () {
self._sourceDropped.apply(self, arguments);
};
};
Shared.addModule('Overview', Overview);
Shared.mixin(Overview.prototype, 'Mixin.Common');
Shared.mixin(Overview.prototype, 'Mixin.ChainReactor');
Shared.mixin(Overview.prototype, 'Mixin.Constants');
Shared.mixin(Overview.prototype, 'Mixin.Triggers');
Shared.mixin(Overview.prototype, 'Mixin.Events');
Shared.mixin(Overview.prototype, 'Mixin.Tags');
Collection = require('./Collection');
DbDocument = require('./Document');
Db = Shared.modules.Db;
/**
* Gets / sets the current state.
* @param {String=} val The name of the state to set.
* @returns {*}
*/
Shared.synthesize(Overview.prototype, 'state');
/**
* Gets / sets the db.
* @param {Db=} val The db to set.
* @returns {*}
*/
Shared.synthesize(Overview.prototype, 'db');
/**
* Gets / sets the name.
* @param {String=} val The name to set.
* @returns {*}
*/
Shared.synthesize(Overview.prototype, 'name');
/**
* Gets / sets the query.
* @param {Object=} val The query to set.
* @returns {*}
*/
Shared.synthesize(Overview.prototype, 'query', function (val) {
var ret = this.$super(val);
if (val !== undefined) {
this._refresh();
}
return ret;
});
/**
* Gets / sets the query options.
* @param {Object=} val The query options to set.
* @returns {*}
*/
Shared.synthesize(Overview.prototype, 'queryOptions', function (val) {
var ret = this.$super(val);
if (val !== undefined) {
this._refresh();
}
return ret;
});
/**
* Gets / sets the reduce object.
* @param {Object=} val The reduce object to set.
* @returns {*}
*/
Shared.synthesize(Overview.prototype, 'reduce', function (val) {
var ret = this.$super(val);
if (val !== undefined) {
this._refresh();
}
return ret;
});
/**
* Gets / sets the data source the overview uses for underlying data.
* @param {Collection|View=} source The data source to set.
* @returns {*}
*/
Overview.prototype.from = function (source) {
if (source !== undefined) {
if (typeof(source) === 'string') {
source = this._db.collection(source);
}
this._setFrom(source);
return this;
}
return this._sources;
};
/**
* Returns the data in the overview.
* @param {Object=} query The query object.
* @param {Object=} options An options object.
* @param {Function=} callback A callback method.
* @returns {*}
* @see Collection.find()
*/
Overview.prototype.find = function (query, options, callback) {
return this._collData.find.apply(this._collData, arguments);
};
/**
* Executes and returns the response from the current reduce method
* assigned to the overview.
* @returns {*}
*/
Overview.prototype.exec = function () {
var reduceFunc = this.reduce();
return reduceFunc ? reduceFunc.apply(this) : undefined;
};
/**
* Returns a count of the documents in the overview data set.
* @returns {*}
*/
Overview.prototype.count = function () {
return this._collData.count.apply(this._collData, arguments);
};
Overview.prototype._setFrom = function (source) {
// Remove all source references
while (this._sources.length) {
this._removeSource(this._sources[0]);
}
this._addSource(source);
return this;
};
Overview.prototype._addSource = function (source) {
if (source && source.className === 'View') {
// The source is a view so IO to the internal data collection
// instead of the view proper
source = source.data();
if (this.debug()) {
console.log(this.logIdentifier() + ' Using internal private data "' + source.instanceIdentifier() + '" for IO graph linking');
}
}
if (this._sources.indexOf(source) === -1) {
this._sources.push(source);
source.chain(this);
source.on('drop', this._sourceDroppedWrap);
this._refresh();
}
return this;
};
Overview.prototype._removeSource = function (source) {
if (source && source.className === 'View') {
// The source is a view so IO to the internal data collection
// instead of the view proper
source = source.data();
if (this.debug()) {
console.log(this.logIdentifier() + ' Using internal private data "' + source.instanceIdentifier() + '" for IO graph linking');
}
}
var sourceIndex = this._sources.indexOf(source);
if (sourceIndex > -1) {
this._sources.splice(source, 1);
source.unChain(this);
source.off('drop', this._sourceDroppedWrap);
this._refresh();
}
return this;
};
Overview.prototype._sourceDropped = function (source) {
if (source) {
// Source was dropped, remove from overview
this._removeSource(source);
}
};
Overview.prototype._refresh = function () {
if (!this.isDropped()) {
if (this._sources && this._sources[0]) {
this._collData.primaryKey(this._sources[0].primaryKey());
var tempArr = [],
i;
for (i = 0; i < this._sources.length; i++) {
tempArr = tempArr.concat(this._sources[i].find(this._query, this._queryOptions));
}
this._collData.setData(tempArr);
}
// Now execute the reduce method
if (this._reduce) {
var reducedData = this._reduce.apply(this);
// Update the document with the newly returned data
this._data.setData(reducedData);
}
}
};
Overview.prototype._chainHandler = function (chainPacket) {
switch (chainPacket.type) {
case 'setData':
case 'insert':
case 'update':
case 'remove':
this._refresh();
break;
default:
break;
}
};
/**
* Gets the module's internal data collection.
* @returns {Collection}
*/
Overview.prototype.data = function () {
return this._data;
};
/**
* Drops the overview.
* @param {Function=} callback A callback function.
* @returns {Boolean}
*/
Overview.prototype.drop = function (callback) {
if (!this.isDropped()) {
this._state = 'dropped';
delete this._data;
delete this._collData;
// Remove all source references
while (this._sources.length) {
this._removeSource(this._sources[0]);
}
delete this._sources;
if (this._db && this._name) {
delete this._db._overview[this._name];
}
this.emit('drop', this);
if (callback) { callback(false, true); }
delete this._listeners;
}
return true;
};
/**
* Create an overview instance from a Db instance.
* @param {String} name The name of the overview.
* @returns {*}
*/
Db.prototype.overview = function (name) {
var self = this;
if (name) {
// Handle being passed an instance
if (name instanceof Overview) {
return name;
}
if (this._overview && this._overview[name]) {
return this._overview[name];
}
this._overview = this._overview || {};
this._overview[name] = new Overview(name).db(this);
self.deferEmit('create', self._overview[name], 'overview', name);
return this._overview[name];
} else {
// Return an object of collection data
return this._overview || {};
}
};
/**
* Returns an array of overviews the DB currently has.
* @returns {Array} An array of objects containing details of each overview
* the database is currently managing.
*/
Db.prototype.overviews = function () {
var arr = [],
item,
i;
for (i in this._overview) {
if (this._overview.hasOwnProperty(i)) {
item = this._overview[i];
arr.push({
name: i,
count: item.count(),
linked: item.isLinked !== undefined ? item.isLinked() : false
});
}
}
return arr;
};
Shared.finishModule('Overview');
module.exports = Overview;