UNPKG

mongo-portable

Version:

Portable Pure JS MongoDB - Based on Monglodb (https://github.com/euforic/monglodb.git) by Christian Sullivan (http://RogueSynaptics.com)

671 lines (668 loc) 22.1 kB
"use strict"; var __values = (this && this.__values) || function (o) { var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; if (m) return m.call(o); return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; }; Object.defineProperty(exports, "__esModule", { value: true }); var jsw_logger_1 = require("jsw-logger"); var _ = require("lodash"); var selector_1 = require("../selector"); /* class Options { public skip: number; public limit: number; public sort; private __defaultOptions = { skip: 0, limit: 15, sort: null }; constructor(options?: any) { if (_.isNil(options)) { options = {}; } this.skip = (options.skip ? options.skip : this.__defaultOptions.skip); this.limit = (options.limit ? options.limit : this.__defaultOptions.limit); this.sort = (options.sort ? options.sort : this.__defaultOptions.sort); } } */ /*** * Cursor * * @module Cursor * @since 0.0.1 * @author Eduardo Astolfi <eduardo.astolfi91@gmail.com> * @copyright 2016 Eduardo Astolfi <eduardo.astolfi91@gmail.com> * @license MIT Licensed * @classdesc Cursor class that maps a MongoDB-like cursor */ var Cursor = /** @class */ (function () { /*** * @param {MongoPortable} db - Additional options * @param {Array} documents - The list of documents * @param {Object|Array|String} [selection={}] - The selection for matching documents * @param {Object|Array|String} [fields={}] - The fields of the document to show * @param {Object} [options] - Database object * * @param {Object} [options.pkFactory=null] - Object overriding the basic "ObjectId" primary key generation. */ function Cursor(documents, selection, fields, options) { var e_1, _a, e_2, _b; if (options === void 0) { options = {}; } this.sorted = false; this.indexes = null; this.defaultOptions = { skip: 0, limit: 15, sort: null }; this.documents = documents; this.selector = selection; var opts = _.assign({}, this.defaultOptions, options); this.skipValue = opts.skip; this.limitValue = opts.limit; this.sortValue = opts.sort; this.logger = jsw_logger_1.JSWLogger.instance; /**** ADD IDX ****/ if (selector_1.Selector.isSelectorCompiled(this.selector)) { this.selectorCompiled = this.selector; } else { this.selectorCompiled = new selector_1.Selector(this.selector, selector_1.Selector.MATCH_SELECTOR); } try { for (var _c = __values(this.selectorCompiled.clauses), _d = _c.next(); !_d.done; _d = _c.next()) { var clause = _d.value; if (clause.key === "_id") { this.selectorId = clause.value; } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (_d && !_d.done && (_a = _c.return)) _a.call(_c); } finally { if (e_1) throw e_1.error; } } try { for (var _e = __values(this.selectorCompiled.clauses), _f = _e.next(); !_f.done; _f = _e.next()) { var clause = _f.value; if (clause.key === "_id") { var val = clause.value; if (_.isString(val) || _.isNumber(val)) { this.selectorId = val; } } } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (_f && !_f.done && (_b = _e.return)) _b.call(_e); } finally { if (e_2) throw e_2.error; } } /**** ADD IDX ****/ this.fetchMode = Cursor.COLSCAN || Cursor.IDXSCAN; // this.indexes = null;//findUsableIndexes(); // if (cursor.fetchMode === Cursor.COLSCAN) { // // COLSCAN, wi will iterate over all documents // docs = _.cloneDeep(cursor.collection.docs); // } else if (cursor.fetchMode === Cursor.IDXSCAN) { // // IDXSCAN, wi will iterate over all needed documents // for (let i = 0; i < cursor.indexes.length; i++) { // let index = cursor.indexes[i]; // for (let i = index.start; i < index.end; i++) { // let idx_id = cursor.collection.getIndex(index.name)[i]; // docs.push(cursor.collection.docs[idx_id]); // } // } // } this.fields = new selector_1.Selector(fields, selector_1.Selector.FIELD_SELECTOR); this.sortCompiled = new selector_1.Selector(this.sortValue, selector_1.Selector.SORT_SELECTOR); this.dbObjects = null; this.cursorPosition = 0; } /*** * Moves a cursor to the begining * * @method Cursor#rewind */ Cursor.prototype.rewind = function () { this.dbObjects = null; this.cursorPosition = 0; }; /*** * Iterates over the cursor, calling a callback function * * @method Cursor#forEach * * @param {Function} [callback=null] - Callback function to be called for each document */ Cursor.prototype.forEach = function (callback) { var e_3, _a; var docs = this.fetchAll(); try { for (var docs_1 = __values(docs), docs_1_1 = docs_1.next(); !docs_1_1.done; docs_1_1 = docs_1.next()) { var doc = docs_1_1.value; callback(doc); } } catch (e_3_1) { e_3 = { error: e_3_1 }; } finally { try { if (docs_1_1 && !docs_1_1.done && (_a = docs_1.return)) _a.call(docs_1); } finally { if (e_3) throw e_3.error; } } }; /*** * Iterates over the cursor, returning a new array with the documents affected by the callback function * * @method Cursor#map * * @param {Function} [callback=null] - Callback function to be called for each document * * @returns {Array} The documents after being affected with the callback function */ Cursor.prototype.map = function (callback) { var res = []; this.forEach(function (doc) { res.push(callback(doc)); }); return res; }; /*** * Checks if the cursor has one document to be fetched * * @method Cursor#hasNext * * @returns {Boolean} True if we can fetch one more document */ Cursor.prototype.hasNext = function () { return (this.cursorPosition < this.documents.length); }; /*** * Alias for {@link Cursor#fetchOne} * * @method Cursor#next */ Cursor.prototype.next = function () { return this.fetchOne(); }; /*** * Alias for {@link Cursor#fetchAll} * * @method Cursor#fetch */ Cursor.prototype.fetch = function () { return this.fetchAll(); }; /*** * Fetch all documents in the cursor * * @method Cursor#fetchAll * * @returns {Array} All the documents contained in the cursor */ Cursor.prototype.fetchAll = function () { return getDocuments(this, false) || []; }; /*** * Retrieves the next document in the cursor * * @method Cursor#fetchOne * * @returns {Object} The next document in the cursor */ Cursor.prototype.fetchOne = function () { return getDocuments(this, true); }; /*** * Obtains the total of documents of the cursor * * @method Cursor#count * * @returns {Number} The total of documents in the cursor */ Cursor.prototype.count = function () { return this.fetchAll().length; }; /*** * Set the sorting of the cursor * * @method Cursor#sort * * @param {Object|Array|String} spec - The sorting specification * * @returns {Cursor} This instance so it can be chained with other methods */ Cursor.prototype.setSorting = function (spec) { if (_.isNil(spec)) { this.logger.throw("You need to specify a sorting"); } if (spec) { this.sortValue = spec; this.sortCompiled = (new selector_1.Selector(spec, selector_1.Selector.SORT_SELECTOR)); } return this; }; /*** * Applies a sorting on the cursor * * @method Cursor#sort * * @param {Object|Array|String} spec - The sorting specification * * @returns {Cursor} This instance so it can be chained with other methods */ Cursor.prototype.sort = function (spec) { var _sort = this.sortCompiled || null; if (spec) { _sort = new selector_1.Selector(spec, selector_1.Selector.SORT_SELECTOR); } if (_sort) { if (!_.isNil(this.dbObjects) && _.isArray(this.dbObjects)) { this.dbObjects = this.dbObjects.sort(_sort); this.sorted = true; } else { this.setSorting(spec); } } return this; }; /*** * Set the number of document to skip when fetching the cursor * * @method Cursor#skip * * @param {Number} skip - The number of documents to skip * * @returns {Cursor} This instance so it can be chained with other methods */ Cursor.prototype.skip = function (skip) { if (_.isNil(skip) || _.isNaN(skip)) { throw new Error("Must pass a number"); } this.skipValue = skip; return this; }; /*** * Set the max number of document to fetch * * @method Cursor#limit * * @param {Number} limit - The max number of documents * * @returns {Cursor} This instance so it can be chained with other methods */ Cursor.prototype.limit = function (limit) { if (_.isNil(limit) || _.isNaN(limit)) { throw new Error("Must pass a number"); } this.limitValue = limit; return this; }; /*** * @todo Implement */ Cursor.prototype.batchSize = function () { // Controls the number of documents MongoDB will return to the client in a single network message. throw new Error("Not yet implemented"); }; /*** * @todo Implement */ Cursor.prototype.close = function () { // Close a cursor and free associated server resources. throw new Error("Not yet implemented"); }; /*** * @todo Implement */ Cursor.prototype.comment = function () { // Attaches a comment to the query to allow for traceability in the logs and the system.profile collection. throw new Error("Not yet implemented"); }; /*** * @todo Implement */ Cursor.prototype.explain = function () { // Reports on the query execution plan for a cursor. throw new Error("Not yet implemented"); }; /*** * @todo Implement */ Cursor.prototype.hint = function () { // Forces MongoDB to use a specific index for a query. throw new Error("Not yet implemented"); }; /*** * @todo Implement */ Cursor.prototype.itcount = function () { // Computes the total number of documents in the cursor client-side by fetching and iterating the result set. throw new Error("Not yet implemented"); }; /*** * @todo Implement */ Cursor.prototype.maxScan = function () { // Specifies the maximum number of items to scan; documents for collection scans, keys for index scans. throw new Error("Not yet implemented"); }; /*** * @todo Implement */ Cursor.prototype.maxTimeMS = function () { // Specifies a cumulative time limit in milliseconds for processing operations on a cursor. throw new Error("Not yet implemented"); }; /*** * @todo Implement */ Cursor.prototype.max = function () { // Specifies an exclusive upper index bound for a cursor. For use with cursor.hint() throw new Error("Not yet implemented"); }; /*** * @todo Implement */ Cursor.prototype.min = function () { // Specifies an inclusive lower index bound for a cursor. For use with cursor.hint() throw new Error("Not yet implemented"); }; /*** * @todo Implement */ Cursor.prototype.noCursorTimeout = function () { // Instructs the server to avoid closing a cursor automatically after a period of inactivity. throw new Error("Not yet implemented"); }; /*** * @todo Implement */ Cursor.prototype.objsLeftInBatch = function () { // Returns the number of documents left in the current cursor batch. throw new Error("Not yet implemented"); }; /*** * @todo Implement */ Cursor.prototype.pretty = function () { // Configures the cursor to display results in an easy-to-read format. throw new Error("Not yet implemented"); }; /*** * @todo Implement */ Cursor.prototype.readConcern = function () { // Specifies a read concern for a find() operation. throw new Error("Not yet implemented"); }; /*** * @todo Implement */ Cursor.prototype.readPref = function () { // Specifies a read preference to a cursor to control how the client directs queries to a replica set. throw new Error("Not yet implemented"); }; /*** * @todo Implement */ Cursor.prototype.returnKey = function () { // Modifies the cursor to return index keys rather than the documents. throw new Error("Not yet implemented"); }; /*** * @todo Implement */ Cursor.prototype.showRecordId = function () { // Adds an internal storage engine ID field to each document returned by the cursor. throw new Error("Not yet implemented"); }; /*** * @todo Implement */ Cursor.prototype.size = function () { // Returns a count of the documents in the cursor after applying skip() and limit() methods. throw new Error("Not yet implemented"); }; /*** * @todo Implement */ Cursor.prototype.snapshot = function () { // Forces the cursor to use the index on the _id field. Ensures that the cursor returns each document, // with regards to the value of the _id field, only once. throw new Error("Not yet implemented"); }; /*** * @todo Implement */ Cursor.prototype.tailable = function () { // Marks the cursor as tailable. Only valid for cursors over capped collections. throw new Error("Not yet implemented"); }; /*** * @todo Implement */ Cursor.prototype.toArray = function () { // Returns an array that contains all documents returned by the cursor. throw new Error("Not yet implemented"); }; Cursor.sort = function (doc, fields) { // Sort the elements of a cursor throw new Error("Not yet implemented"); }; /*** * Projects the fields of one or several documents, changing the output * * @method Cursor.project * * @param {Array|Object} doc - The document/s that will be projected * @param {String|Array|Object} spec - Fields projection specification. Can be an space/comma separated list, an array, or an object * * @returns {Array|Object} The document/s after the projection */ Cursor.project = function (doc, spec, aggregation) { // if (_.isNil(doc)) this.logger.throw("doc param required"); // if (_.isNil(spec)) this.logger.throw("spec param required"); if (aggregation === void 0) { aggregation = false; } var fields = null; if (aggregation) { fields = new selector_1.Selector(spec, selector_1.Selector.AGG_FIELD_SELECTOR); } else { fields = new selector_1.Selector(spec, selector_1.Selector.FIELD_SELECTOR); } if (_.isArray(doc)) { for (var i = 0; i < doc.length; i++) { doc[i] = mapFields(doc[i], fields); } return doc; } else { return mapFields(doc, fields); } }; Cursor.COLSCAN = "colscan"; Cursor.IDXSCAN = "idxscan"; return Cursor; }()); exports.Cursor = Cursor; var mapFields = function (doc, fields) { var e_4, _a; var _doc = _.cloneDeep(doc); if (!_.isNil(fields) && _.isPlainObject(fields) && !_.isEqual(fields, {})) { var showId = true; var showing = null; // Whether if we showing the _id field if (_.hasIn(fields, "_id") && fields._id === -1) { showId = false; } for (var field in fields) { // Whether if we are showing or hidding fields if (field !== "_id") { if (fields[field] === 1) { showing = true; break; } else if (fields[field] === -1) { showing = false; break; } } } var tmp = null; try { for (var _b = __values(Object.keys(fields)), _c = _b.next(); !_c.done; _c = _b.next()) { var field = _c.value; if (tmp === null) { if (showing) { tmp = {}; } else { tmp = _.cloneDeep(doc); } } // Add or remove the field if (fields[field] === 1 || fields[field] === -1) { // Show the field if (showing) { tmp[field] = doc[field]; } else { // Hide the field delete tmp[field]; } } else { // Show the new field (rename) tmp[field] = doc[fields[field]]; } } } catch (e_4_1) { e_4 = { error: e_4_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_4) throw e_4.error; } } // Add or remove the _id field if (showId) { tmp._id = doc._id; } else { delete tmp._id; } _doc = tmp; } return _doc; }; /*** * Retrieves one or all the documents in the cursor * * @method getDocuments * @private * * @param {Cursor} cursor - The cursor with the documents * @param {Boolean} [justOne=false] - Whether it retrieves one or all the documents * * @returns {Array|Object} If [justOne=true] returns the next document, otherwise returns all the documents */ var getDocuments = function (cursor, justOne) { if (justOne === void 0) { justOne = false; } var e_5, _a; var docs = []; if (cursor.fetchMode === Cursor.COLSCAN) { // COLSCAN, wi will iterate over all documents docs = _.cloneDeep(cursor.documents); } else if (cursor.fetchMode === Cursor.IDXSCAN) { try { // IDXSCAN, wi will iterate over all needed documents for (var cursor_1 = __values(cursor), cursor_1_1 = cursor_1.next(); !cursor_1_1.done; cursor_1_1 = cursor_1.next()) { var index = cursor_1_1.value; for (var i = index.start; i < index.end; i++) { // let idxId = cursor.collection.getIndex(index.name)[i]; var idxId = index.index[i]; docs.push(cursor.documents[idxId]); } } } catch (e_5_1) { e_5 = { error: e_5_1 }; } finally { try { if (cursor_1_1 && !cursor_1_1.done && (_a = cursor_1.return)) _a.call(cursor_1); } finally { if (e_5) throw e_5.error; } } } // if (cursor.selectorId) { // if (_.hasIn(cursor.collection.doc_indexes, _.toString(cursor.selectorId))) { // let idx = cursor.collection.doc_indexes[_.toString(cursor.selectorId)]; // return Cursor.project(cursor.collection.docs[idx], cursor.fields); // } else { // if (justOne) { // return null; // } else { // return []; // } // } // } // TODO add warning when sort/skip/limit and fetching one // TODO add warning when skip/limit without order // TODO index while (cursor.cursorPosition < docs.length) { var _doc = docs[cursor.cursorPosition]; cursor.cursorPosition++; if (cursor.selectorCompiled.test(_doc)) { if (_.isNil(cursor.dbObjects)) { cursor.dbObjects = []; } _doc = Cursor.project(_doc, cursor.fields); cursor.dbObjects.push(_doc); if (justOne) { // Add force sort return _doc; } } } if (_.isNil(cursor.dbObjects)) { return null; } if (!cursor.sorted && hasSorting(cursor)) { cursor.sort(); } var idxFrom = cursor.skipValue; var idxTo = cursor.limitValue !== -1 ? (cursor.limitValue + idxFrom) : cursor.dbObjects.length; return cursor.dbObjects.slice(idxFrom, idxTo); }; /*** * Checks if a cursor has a sorting defined * * @method hasSorting * @private * * @param {Cursor} cursor - The cursor * * @returns {Boolean} Whether the cursor has sorting or not */ var hasSorting = function (cursor) { if (_.isNil(cursor.sortValue)) { return false; } return true; }; //# sourceMappingURL=Cursor.js.map