123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390 |
- 'use strict';
- const Promise = require('bluebird');
- const { parseArgs, shuffle } = require('./util');
- class Query {
- /**
- * Query constructor.
- *
- * @param {Array} data
- */
- constructor(data) {
- this.data = data;
- this.length = data.length;
- }
- /**
- * Returns the number of elements.
- *
- * @return Number
- */
- count() {
- return this.length;
- }
- /**
- * Iterates over all documents.
- *
- * @param {Function} iterator
- */
- forEach(iterator) {
- const { data, length } = this;
- for (let i = 0; i < length; i++) {
- iterator(data[i], i);
- }
- }
- /**
- * Returns an array containing all documents.
- *
- * @return {Array}
- */
- toArray() {
- return this.data;
- }
- /**
- * Returns the document at the specified index. `num` can be a positive or
- * negative number.
- *
- * @param {Number} i
- * @return {Document|Object}
- */
- eq(i) {
- const index = i < 0 ? this.length + i : i;
- return this.data[index];
- }
- /**
- * Returns the first document.
- *
- * @return {Document|Object}
- */
- first() {
- return this.eq(0);
- }
- /**
- * Returns the last document.
- *
- * @return {Document|Object}
- */
- last() {
- return this.eq(-1);
- }
- /**
- * Returns the specified range of documents.
- *
- * @param {Number} start
- * @param {Number} [end]
- * @return {Query}
- */
- slice(start, end) {
- return new this.constructor(this.data.slice(start, end));
- }
- /**
- * Limits the number of documents returned.
- *
- * @param {Number} i
- * @return {Query}
- */
- limit(i) {
- return this.slice(0, i);
- }
- /**
- * Specifies the number of items to skip.
- *
- * @param {Number} i
- * @return {Query}
- */
- skip(i) {
- return this.slice(i);
- }
- /**
- * Returns documents in a reversed order.
- *
- * @return {Query}
- */
- reverse() {
- return new this.constructor(this.data.slice().reverse());
- }
- /**
- * Returns documents in random order.
- *
- * @return {Query}
- */
- shuffle() {
- return new this.constructor(shuffle(this.data));
- }
- /**
- * 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}
- */
- find(query, options = {}) {
- const filter = this._schema._execQuery(query);
- const { data, length } = this;
- const { lean = false } = options;
- let { limit = length, skip } = options;
- const arr = [];
- for (let i = 0; limit && i < length; i++) {
- const item = data[i];
- if (filter(item)) {
- if (skip) {
- skip--;
- } else {
- arr.push(lean ? item.toObject() : item);
- limit--;
- }
- }
- }
- return 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}
- */
- findOne(query, options = {}) {
- options.limit = 1;
- const 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}
- */
- sort(orderby, order) {
- const sort = parseArgs(orderby, order);
- const 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}
- */
- map(iterator) {
- const { data, length } = this;
- const result = new Array(length);
- for (let i = 0; i < length; 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 {*}
- */
- reduce(iterator, initial) {
- const { data, length } = this;
- let result, i;
- if (initial === undefined) {
- i = 1;
- result = data[0];
- } else {
- i = 0;
- result = initial;
- }
- for (; i < length; 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 {*}
- */
- reduceRight(iterator, initial) {
- const { data, length } = this;
- let result, i;
- if (initial === undefined) {
- i = length - 2;
- result = data[length - 1];
- } else {
- i = length - 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}
- */
- filter(iterator) {
- const { data, length } = this;
- const arr = [];
- for (let i = 0; i < length; i++) {
- const 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}
- */
- every(iterator) {
- const { data, length } = this;
- for (let i = 0; i < length; 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}
- */
- some(iterator) {
- const { data, length } = this;
- for (let i = 0; i < length; i++) {
- if (iterator(data[i], i)) return true;
- }
- return false;
- }
- /**
- * Update all documents.
- *
- * @param {Object} data
- * @param {Function} [callback]
- * @return {Promise}
- */
- update(data, callback) {
- const model = this._model;
- const stack = this._schema._parseUpdate(data);
- return Promise.mapSeries(this.data, item => model._updateWithStack(item._id, stack)).asCallback(callback);
- }
- /**
- * Replace all documents.
- *
- * @param {Object} data
- * @param {Function} [callback]
- * @return {Promise}
- */
- replace(data, callback) {
- const model = this._model;
- return Promise.map(this.data, item => model.replaceById(item._id, data)).asCallback(callback);
- }
- /**
- * Remove all documents.
- *
- * @param {Function} [callback]
- * @return {Promise}
- */
- remove(callback) {
- const model = this._model;
- return Promise.mapSeries(this.data, item => model.removeById(item._id)).asCallback(callback);
- }
- /**
- * Populates document references.
- *
- * @param {String|Object} expr
- * @return {Query}
- */
- populate(expr) {
- const stack = this._schema._parsePopulate(expr);
- const { data, length } = this;
- const model = this._model;
- for (let i = 0; i < length; i++) {
- data[i] = model._populate(data[i], stack);
- }
- return this;
- }
- }
- Query.prototype.size = Query.prototype.count;
- Query.prototype.each = Query.prototype.forEach;
- Query.prototype.random = Query.prototype.shuffle;
- module.exports = Query;
|