| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408 |
- 'use strict';
- var _ = require('lodash');
- var Promise = require('bluebird');
- var util = require('./util');
- var reverse = util.reverse;
- var shuffle = _.shuffle;
- var parseArgs = util.parseArgs;
- /**
- * Query constructor.
- *
- * @class
- * @param {Array} data
- */
- function Query(data) {
- this.data = data;
- this.length = data.length;
- }
- /**
- * Returns the number of elements.
- *
- * @return Number
- */
- Query.prototype.count = function() {
- return this.length;
- };
- Query.prototype.size = Query.prototype.count;
- /**
- * Iterates over all documents.
- *
- * @param {Function} iterator
- */
- Query.prototype.forEach = function(iterator) {
- var data = this.data;
- for (var i = 0, len = this.length; i < len; i++) {
- iterator(data[i], i);
- }
- };
- Query.prototype.each = Query.prototype.forEach;
- /**
- * Returns an array containing all documents.
- *
- * @return {Array}
- */
- Query.prototype.toArray = function() {
- return this.data;
- };
- /**
- * Returns the document at the specified index. `num` can be a positive or
- * negative number.
- *
- * @param {Number} i
- * @return {Document|Object}
- */
- Query.prototype.eq = function(i) {
- var index = i < 0 ? this.length + i : i;
- return this.data[index];
- };
- /**
- * Returns the first document.
- *
- * @return {Document|Object}
- */
- Query.prototype.first = function() {
- return this.eq(0);
- };
- /**
- * Returns the last document.
- *
- * @return {Document|Object}
- */
- Query.prototype.last = function() {
- return this.eq(-1);
- };
- /**
- * Returns the specified range of documents.
- *
- * @param {Number} start
- * @param {Number} [end]
- * @return {Query}
- */
- Query.prototype.slice = function(start, end) {
- return new this.constructor(this.data.slice(start, end));
- };
- /**
- * Limits the number of documents returned.
- *
- * @param {Number} i
- * @return {Query}
- */
- Query.prototype.limit = function(i) {
- return this.slice(0, i);
- };
- /**
- * Specifies the number of items to skip.
- *
- * @param {Number} i
- * @return {Query}
- */
- Query.prototype.skip = function(i) {
- return this.slice(i);
- };
- /**
- * Returns documents in a reversed order.
- *
- * @return {Query}
- */
- Query.prototype.reverse = function() {
- return new this.constructor(reverse(this.data.slice()));
- };
- /**
- * Returns documents in random order.
- *
- * @return {Query}
- */
- Query.prototype.shuffle = function() {
- return new this.constructor(shuffle(this.data.slice()));
- };
- Query.prototype.random = Query.prototype.shuffle;
- /**
- * Finds matching documents.
- *
- * @param {Object} query
- * @param {Object} [options]
- * @param {Number} [options.limit=0] Limits the number of documents returned.
- * @param {Number} [options.skip=0] Skips the first elements.
- * @param {Boolean} [options.lean=false] Returns a plain JavaScript object.
- * @return {Query|Array}
- */
- Query.prototype.find = function(query, options_) {
- var options = options_ || {};
- var filter = this._schema._execQuery(query);
- var data = this.data;
- var i = 0;
- var len = this.length;
- var limit = options.limit || len;
- var skip = options.skip;
- var arr = [];
- var item;
- for (; limit && i < len; i++) {
- item = data[i];
- if (filter(item)) {
- if (skip) {
- skip--;
- } else {
- arr.push(item);
- limit--;
- }
- }
- }
- return options.lean ? arr : new this.constructor(arr);
- };
- /**
- * Finds the first matching documents.
- *
- * @param {Object} query
- * @param {Object} [options]
- * @param {Number} [options.skip=0] Skips the first elements.
- * @param {Boolean} [options.lean=false] Returns a plain JavaScript object.
- * @return {Document|Object}
- */
- Query.prototype.findOne = function(query, options_) {
- var options = options_ || {};
- options.limit = 1;
- var result = this.find(query, options);
- return options.lean ? result[0] : result.data[0];
- };
- /**
- * Sorts documents.
- *
- * Example:
- *
- * ``` js
- * query.sort('date', -1);
- * query.sort({date: -1, title: 1});
- * query.sort('-date title');
- * ```
- *
- * If the `order` equals to `-1`, `desc` or `descending`, the data will be
- * returned in reversed order.
- *
- * @param {String|Object} orderby
- * @param {String|Number} [order]
- * @return {Query}
- */
- Query.prototype.sort = function(orderby, order) {
- var sort = parseArgs(orderby, order);
- var fn = this._schema._execSort(sort);
- return new this.constructor(this.data.slice().sort(fn));
- };
- /**
- * Creates an array of values by iterating each element in the collection.
- *
- * @param {Function} iterator
- * @return {Array}
- */
- Query.prototype.map = function(iterator) {
- var len = this.length;
- var result = new Array(len);
- var data = this.data;
- for (var i = 0; i < len; i++) {
- result[i] = iterator(data[i], i);
- }
- return result;
- };
- /**
- * Reduces a collection to a value which is the accumulated result of iterating
- * each element in the collection.
- *
- * @param {Function} iterator
- * @param {*} [initial] By default, the initial value is the first document.
- * @return {*}
- */
- Query.prototype.reduce = function(iterator, initial) {
- var len = this.length;
- var data = this.data;
- var result, i;
- if (initial === undefined) {
- i = 1;
- result = data[0];
- } else {
- i = 0;
- result = initial;
- }
- for (; i < len; i++) {
- result = iterator(result, data[i], i);
- }
- return result;
- };
- /**
- * Reduces a collection to a value which is the accumulated result of iterating
- * each element in the collection from right to left.
- *
- * @param {Function} iterator
- * @param {*} [initial] By default, the initial value is the last document.
- * @return {*}
- */
- Query.prototype.reduceRight = function(iterator, initial) {
- var len = this.length;
- var data = this.data;
- var result, i;
- if (initial === undefined) {
- i = len - 2;
- result = data[len - 1];
- } else {
- i = len - 1;
- result = initial;
- }
- for (; i >= 0; i--) {
- result = iterator(result, data[i], i);
- }
- return result;
- };
- /**
- * Creates a new array with all documents that pass the test implemented by the
- * provided function.
- *
- * @param {Function} iterator
- * @return {Query}
- */
- Query.prototype.filter = function(iterator) {
- var data = this.data;
- var arr = [];
- var item;
- for (var i = 0, len = this.length; i < len; i++) {
- item = data[i];
- if (iterator(item, i)) arr.push(item);
- }
- return new this.constructor(arr);
- };
- /**
- * Tests whether all documents pass the test implemented by the provided
- * function.
- *
- * @param {Function} iterator
- * @return {Boolean}
- */
- Query.prototype.every = function(iterator) {
- var data = this.data;
- for (var i = 0, len = data.length; i < len; i++) {
- if (!iterator(data[i], i)) return false;
- }
- return true;
- };
- /**
- * Tests whether some documents pass the test implemented by the provided
- * function.
- *
- * @param {Function} iterator
- * @return {Boolean}
- */
- Query.prototype.some = function(iterator) {
- var data = this.data;
- for (var i = 0, len = data.length; i < len; i++) {
- if (iterator(data[i], i)) return true;
- }
- return false;
- };
- /**
- * Update all documents.
- *
- * @param {Object} data
- * @param {Function} [callback]
- * @return {Promise}
- */
- Query.prototype.update = function(data, callback) {
- var model = this._model;
- var stack = this._schema._parseUpdate(data);
- return Promise.mapSeries(this.data, function(item) {
- return model._updateWithStack(item._id, stack);
- }).asCallback(callback);
- };
- /**
- * Replace all documents.
- *
- * @param {Object} data
- * @param {Function} [callback]
- * @return {Promise}
- */
- Query.prototype.replace = function(data, callback) {
- var model = this._model;
- return Promise.map(this.data, function(item) {
- return model.replaceById(item._id, data);
- }).asCallback(callback);
- };
- /**
- * Remove all documents.
- *
- * @param {Function} [callback]
- * @return {Promise}
- */
- Query.prototype.remove = function(callback) {
- var model = this._model;
- return Promise.mapSeries(this.data, function(item) {
- return model.removeById(item._id);
- }).asCallback(callback);
- };
- /**
- * Populates document references.
- *
- * @param {String|Object} expr
- * @return {Query}
- */
- Query.prototype.populate = function(expr) {
- var stack = this._schema._parsePopulate(expr);
- var data = this.data;
- var model = this._model;
- for (var i = 0, len = this.length; i < len; i++) {
- data[i] = model._populate(data[i], stack);
- }
- return this;
- };
- module.exports = Query;
|