Source: lib/query/Query.js

'use strict';

/**
 * An abstract Query which allows retrieving results
 * @alias query.Query<T>
 */
class Query {
  constructor(entityManager, resultClass) {
    /**
     * The owning EntityManager of this query
     * @type EntityManager
     * @readonly
     */
    this.entityManager = entityManager;

    /**
     * The result class of this query
     * @type Class<T>
     * @readonly
     */
    this.resultClass = resultClass;
  }

  /**
   * Add an ascending sort for the specified field to this query
   * @param {string} field The field to sort
   * @return {this} The resulting Query
   */
  ascending(field) {
    return this.addOrder(field, 1);
  }

  /**
   * Add an decending sort for the specified field to this query
   * @param {string} field The field to sort
   * @return {this} The resulting Query
   */
  descending(field) {
    return this.addOrder(field, -1);
  }

  /**
   * Sets the sort of the query and discard all existing paramaters
   * @param {Object<string,number>} sort The new sort of the query which is an object whose keys are fields and the
   * values are either +1 for ascending order or -1 for descending order
   * @return {this} The resulting Query
   *
   * @see http://docs.mongodb.org/manual/reference/method/cursor.sort/
   */
  sort(sort) {
    if (!(sort instanceof Object) || Object.getPrototypeOf(sort) !== Object.prototype) {
      throw new Error('sort must be an object.');
    }

    return this.addOrder(sort);
  }

  /**
   * Sets the offset of the query, i.e. how many elements should be skipped
   * @param {number} offset The offset of this query
   * @return {this} The resulting Query
   *
   * @see http://docs.mongodb.org/manual/reference/method/cursor.skip/
   */
  offset(offset) {
    if (offset < 0) {
      throw new Error('The offset can\'t be nagative.');
    }

    return this.addOffset(offset);
  }

  /**
   * Sets the limit of this query, i.e hox many objects should be returnd
   * @param {number} limit The limit of this query
   * @return {this} The resulting Query
   *
   * @see http://docs.mongodb.org/manual/reference/method/cursor.limit/
   */
  limit(limit) {
    if (limit < 0) {
      throw new Error('The limit can\'t be nagative.');
    }

    return this.addLimit(limit);
  }

  /**
   * Execute the query and return the query results as a List
   *
   * Note: All local unsaved changes on matching objects, will be discarded.
   *
   * @param {Object} [options] The query options
   * @param {number|boolean} [options.depth=0] The object depth which will be loaded. Depth 0 load only the found
   * objects, <code>true</code> loads the objects by reachability.
   * @param {query.Query~resultListCallback=} doneCallback Called when the operation succeed.
   * @param {query.Query~failCallback=} failCallback Called when the operation failed.
   * @return {Promise<Array<T>>} A promise that will be resolved with the query result as a list
   */
  resultList(options, doneCallback, failCallback) {} // eslint-disable-line no-unused-vars

  /**
   * Execute the query and return the query results as a List
   *
   * Note: All local unsaved changes on matching objects, will be discarded.
   *
   * @param {query.Query~resultListCallback=} doneCallback Called when the operation succeed.
   * @param {query.Query~failCallback=} failCallback Called when the operation failed.
   * @return {Promise<Array<T>>} A promise that will be resolved with the query result as a list
   * @name resultList
   * @memberOf query.Query<T>.prototype
   * @method
   */

  /**
   * Execute the query that returns a single result
   *
   * Note: All local unsaved changes on the matched object, will be discarded.
   *
   * @param {Object} [options] The query options
   * @param {number|boolean} [options.depth=0] The object depth which will be loaded. Depth 0 load only the found
   * object, <code>true</code> loads the objects by reachability.
   * @param {query.Query~singleResultCallback=} doneCallback Called when the operation succeed.
   * @param {query.Query~failCallback=} failCallback Called when the operation failed.
   * @return {Promise<?T>} A promise that will be resolved with the query result as a single result
   */
  singleResult(options, doneCallback, failCallback) {} // eslint-disable-line no-unused-vars

  /**
   * Execute the query that returns a single result
   *
   * Note: All local unsaved changes on the matched object, will be discarded.
   *
   * @param {query.Query~singleResultCallback=} doneCallback Called when the operation succeed.
   * @param {query.Query~failCallback=} failCallback Called when the operation failed.
   * @return {Promise<?T>} A promise that will be resolved with the query result as a single result
   * @name singleResult
   * @memberOf query.Query<T>.prototype
   * @method
   */

  /**
   * Returns an observable that receives change events for a real-time query
   *
   * Multiple subscriptions can be created on top of this observable:
   *
   * <pre><code>
   * var query = DB.Todo.find();
   * var options = { ... };
   * var stream = query.eventStream(options);
   * var sub = stream.subscribe(onNext, onError, onComplete);
   * var otherSub = stream.subscribe(otherOnNext, otherOnError, otherOnComplete);
   * </code></pre>
   *
   * @param {Object} [options] options to narrow down the events you will receive
   * @param {boolean} [options.initial=true] whether or not you want to receive the initial result set (i.e. the
   * entities matching the query at subscription time)
   * @param {(string|Array<string>)} [options.matchTypes=['all']] the match types you are interested in; accepts the
   * default ('all'/['all']) or any combination of 'match', 'add', 'change', 'changeIndex' and 'remove'
   * @param {(string|Array<string>)} [options.operations=['any']] the operations you are interested in; accepts the
   * default ('any'/['any']) or any combination of 'insert', 'update', 'delete' and 'none'
   * @return {Observable<RealtimeEvent<T>>} an observable
   */
  eventStream(options) {} // eslint-disable-line no-unused-vars

  /**
   * Returns a subscription that handles change events for a real-time query.
   *
   * The underlying stream object is hidden. To create multiple subscriptions on the same stream, create the stream
   * object first and then subscribe to the stream (see the other signature {@link #eventStream(options)}).
   * @param {Object} [options] options to narrow down the events you will receive
   * @param {boolean} [options.initial=true] whether or not you want to receive the initial result set (i.e. the
   * entities matching the query at subscription time)
   * @param {(string|Array<string>)} [options.matchTypes=['all']] the match types you are interested in; accepts the
   * default ('all'/['all']) or any combination of 'match', 'add', 'change', 'changeIndex' and 'remove'
   * @param {(string|Array<string>)} [options.operations=['any']] the operations you are interested in; accepts the
   * default ('any'/['any']) or any combination of 'insert', 'update', 'delete' and 'none'
   * @param {query.Query~nextEventCallback=} onNext Called when an event is received
   * @param {query.Query~failCallback=} onError Called when there is a server-side error
   * @param {query.Query~completeCallback=} onComplete Called when the network connection is closed (e.g. because of
   * lost Wi-Fi connection)
   * @return {Subscription} a real-time query subscription
   * @name eventStream
   * @memberOf query.Query<T>.prototype
   * @method
   */


  /**
   * Returns a subscription that handles change events for a real-time query.
   *
   * The underlying stream object is hidden. To create multiple subscriptions on the same stream, create the stream
   * object first and then subscribe to the stream (see the other signature {@link #eventStream(options)}).

   * @param {query.Query~nextEventCallback=} onNext Called when an event is received
   * @param {query.Query~failCallback=} onError Called when there is a server-side error
   * @param {query.Query~completeCallback=} onComplete Called when the network connection is closed (e.g. because of
   * lost Wi-Fi connection)
   * @return {Subscription} a real-time query subscription
   * @name eventStream
   * @memberOf query.Query<T>.prototype
   * @method
   */

  /**
   * Returns an observable that receives the complete real-time query result
   *
   * The full result is received initially (i.e. on subscription) and on every change.
   *
   * var query = DB.Todo.find();
   * var stream = query.resultStream();
   * var sub = stream.subscribe(onNext, onError, onComplete);
   * var otherSub = stream.subscribe(otherOnNext, otherOnError, otherOnComplete);
   * </code></pre>
   *
   * @param {Object} [options] additional options
   * @param {number} [options.reconnects=-1] the number of times that a real-time query subscription should be renewed
   * after connection loss, before it is downgraded to a regular query that does not maintain itself; negative numbers
   * represent infinite reconnection attempts
   * @return {Observable<Array<T>>} an observable on which multiple subscriptions can be created on
   */
  resultStream(options) {} // eslint-disable-line no-unused-vars

  /**
   * Returns a subscription that handles the complete real-time query result
   *
   * The full result is received initially (i.e. on subscription) and on every change.
   *
   * The underlying stream object is hidden. To create multiple subscriptions on the same stream, create the stream
   * object first and then subscribe to the stream (see the other signature {@link #resultStream(options)}).
   *
   * @param {Object} [options] additional options
   * @param {number} [options.reconnects=-1] the number of times that a real-time query subscription should be renewed
   * after connection loss, before it is downgraded to a regular query that does not maintain itself; negative numbers
   * represent infinite reconnection attempts
   * @param {query.Query~nextResultCallback=} onNext Called when the query result changes in any way
   * @param {query.Query~failCallback=} onError Called when there is a server-side error
   * @param {query.Query~completeCallback=} onComplete Called when the network connection is closed (e.g. because of
   * network timeout or lost Wi-Fi connection) and the specified number of reconnects have been exhausted; will never be
   * called when infinite reconnects are configured (default)
   * @return {Subscription} a real-time query subscription handling complete query results.
   * @name resultStream
   * @memberOf query.Query<T>.prototype
   * @method
   */

  /**
   * Returns a subscription that handles the complete real-time query result
   *
   * The full result is received initially (i.e. on subscription) and on every change.
   *
   * The underlying stream object is hidden. To create multiple subscriptions on the same stream, create the stream
   * object first and then subscribe to the stream (see the other signature {@link #resultStream(options)}).
   *
   * As the real-time query will reconnect infinitely often, there is no onComplete callback. (In other words, the
   * observable will never complete.)
   *
   * @param {query.Query~nextResultCallback=} onNext Called when the query result changes in any way
   * @param {query.Query~failCallback=} onError Called when there is a server-side error
   * @return {Subscription} a real-time query subscription handling complete query results.
   * @name resultStream
   * @memberOf query.Query<T>.prototype
   * @method
   */

  /**
   * Execute the query that returns the matching objects count.
   * @param {query.Query~countCallback=} doneCallback Called when the operation succeed.
   * @param {query.Query~failCallback=} failCallback Called when there is a server-side error
   * @return {Promise<number>} The total number of matched objects
   */
  count(doneCallback, failCallback) {} // eslint-disable-line no-unused-vars
}

Query.MAX_URI_SIZE = 2000;

Query.varargs = function varargs(offset, args) {
  return Array.prototype.concat.apply([], Array.prototype.slice.call(args, offset));
};

module.exports = Query;

/**
 * The resultList callback is called, when the asynchronous query operation completes successfully
 * @callback query.Query~resultListCallback
 * @param {Array<T>} result The query result list, an empty list if no match was found
 * @return {Promise<*>|*} A Promise, result or undefined
 */

/**
 * The singleResult callback is called, when the asynchronous query operation completes successfully
 * @callback query.Query~singleResultCallback
 * @param {T} entity The matching object or null id no matching object was found
 * @return {Promise<*>|*} A Promise, result or undefined
 */

/**
 * The count callback is called, when the asynchronous query operation completes successfully
 * @callback query.Query~countCallback
 * @param {number} count the matching object count
 * @return {Promise<*>|*} A Promise, result or undefined
 */

/**
 * The fail callback is called, when the asynchronous query operation is rejected by an error
 * @callback query.Query~failCallback
 * @param {error.PersistentError} error The error which reject the operation
 * @return {Promise<*>|*} A Promise, result or undefined
 */

/**
 * This callback is called whenever the result of the real-time query changes. The received event contains information
 * on how the query result changed.
 * @callback query.Query~nextEventCallback
 * @param {RealtimeEvent<T>} event The real-time query event
 * @return {*}
 */

/**
 * This callback is called whenever the result of the real-time query changes. The full result set is received.
 * @callback query.Query~nextResultCallback
 * @param {Array<T>} result The updated real-time query result
 * @return {*}
 */

/**
 * This callback is called when the network connection is closed (e.g. because of network timeout or lost Wi-Fi c
 * onnection)
 * @callback query.Query~completeCallback
 * @return {*}
 */