UNPKG

kibana-123

Version:

Kibana is an open source (Apache Licensed), browser based analytics and search dashboard for Elasticsearch. Kibana is a snap to setup and start using. Kibana strives to be easy to get started with, while also being flexible and powerful, just like Elastic

159 lines (134 loc) 5.11 kB
import _ from 'lodash'; import inflector from 'ui/indexed_array/inflector'; let pathGetter = _(_.get).rearg(1, 0).ary(2); let inflectIndex = inflector('by'); let inflectOrder = inflector('in', 'Order'); let CLEAR_CACHE = {}; let OPT_NAMES = IndexedArray.OPT_NAMES = ['index', 'group', 'order', 'initialSet', 'immutable']; /** * Generic extension of Array class, which will index (and reindex) the * objects it contains based on their properties. * * @class IndexedArray * @module utils * @constructor * @param {object} [config] - describes the properties of this registry object * @param {string[]} [config.index] - a list of props/paths that should be used to index the docs. * @param {string[]} [config.group] - a list of keys/paths to group docs by. * @param {string[]} [config.order] - a list of keys/paths to order the keys by. * @param {object[]} [config.initialSet] - the initial dataset the IndexedArray should contain. * @param {boolean} [config.immutable] - a flag that hints to people reading the implementation * that this IndexedArray should not be modified. It's modification * methods are also removed */ _.class(IndexedArray).inherits(Array); function IndexedArray(config) { IndexedArray.Super.call(this); // just to remind future us that this list is important config = _.pick(config || {}, OPT_NAMES); this.raw = []; // setup indices this._indexNames = _.union( this._setupIndices(config.group, inflectIndex, _.organizeBy), this._setupIndices(config.index, inflectIndex, _.indexBy), this._setupIndices(config.order, inflectOrder, _.sortBy) ); if (config.initialSet) { this.push.apply(this, config.initialSet); } if (config.immutable) { // just a hint, bugs caused by updates not propogating would be very // very very hard to track down this.push = this.splice = undefined; } } /** * Create indices for a group of object properties. getters and setters are used to * read and control the indices. * * @param {string[]} props - the properties that should be used to index docs * @param {function} inflect - a function that will be called with a property name, and * creates the public property at which the index will be exposed * @param {function} op - the function that will be used to create the indices, it is passed * the raw representaion of the registry, and a getter for reading the * right prop * * @returns {string[]} - the public keys of all indices created */ IndexedArray.prototype._setupIndices = function (props, inflect, op) { // shortcut for empty props if (!props || props.length === 0) return; let self = this; return props.map(function (prop) { let from = pathGetter.partial(prop).value(); let to = inflect(prop); let cache; Object.defineProperty(self, to, { enumerable: false, configurable: false, set: function (val) { // can't set any value other than the CLEAR_CACHE constant if (val === CLEAR_CACHE) { cache = false; } else { throw new TypeError(to + ' can not be set, it is a computed index of values'); } }, get: function () { return cache || (cache = op(self.raw, from)); } }); return to; }); }; /** * (Re)run index/group/order procedures to create indices of * sub-objects. * * @return {undefined} */ IndexedArray.prototype._clearIndices = function () { let self = this; self._indexNames.forEach(function (name) { self[name] = CLEAR_CACHE; }); }; /** * Copy all array methods which have side-effects, and wrap them * in a function that will reindex after each call, as well * as duplex the operation to the .raw version of the IndexedArray. * * @param {[type]} method [description] * @return {[type]} [description] */ 'pop push shift splice unshift reverse'.split(' ').forEach(function (method) { let orig = Array.prototype[method]; IndexedArray.prototype[method] = function (/* args... */) { // call the original method with this context orig.apply(this, arguments); // run the indexers this._clearIndices(); // call the original method on our "raw" array, and return the result(s) return orig.apply(this.raw, arguments); }; }); /** * Remove items from this based on a predicate * @param {function|object|string} predicate - the predicate used to decide what is removed * @param {object} context - this binding for predicate * @return {array} - the removed data */ IndexedArray.prototype.remove = function (predicate, context) { let out = _.remove(this, predicate, context); _.remove(this.raw, predicate, context); this._clearIndices(); return out; }; /** * provide a hook for the JSON serializer * @return {array} - a plain, vanilla array with our same data */ IndexedArray.prototype.toJSON = function () { return this.raw; }; export default IndexedArray;