filter (query, thisArg) {
    /**
     * Selection query as defined by JSData's [Query Syntax][querysyntax].
     *
     * [querysyntax]: http://www.js-data.io/v3.0/docs/query-syntax
     *
     * @example <caption>Empty "findAll" query</caption>
     * const JSData = require('js-data');
     * const { DataStore } = JSData;
     * console.log('Using JSData v' + JSData.version.full);
     *
     * const store = new DataStore();
     * store.defineMapper('post')
     * store.findAll('post').then((posts) => {
     *   console.log(posts); // [...]
     * });
     *
     * @example <caption>Empty "filter" query</caption>
     * const JSData = require('js-data');
     * const { DataStore } = JSData;
     * console.log('Using JSData v' + JSData.version.full);
     *
     * const store = new DataStore();
     * store.defineMapper('post');
     * const posts = store.filter('post');
     * console.log(posts); // [...]
     *
     * @example <caption>Complex "filter" query</caption>
     * const JSData = require('js-data');
     * const { DataStore } = JSData;
     * console.log('Using JSData v' + JSData.version.full);
     *
     * const store = new DataStore();
     * const PAGE_SIZE = 2;
     * let currentPage = 3;
     *
     * store.defineMapper('post');
     * const posts = [
     *   { author: 'John', age: 30, status: 'published', id: 1 },
     *   { author: 'Sally', age: 31, status: 'published', id: 2 },
     *   { author: 'Mike', age: 32, status: 'draft', id: 3 },
     *   { author: 'Adam', age: 33, status: 'deleted', id: 4 },
     *   { author: 'Adam', age: 33, status: 'published', id: 5 }
     *   { author: 'Peter', age: 25, status: 'deleted', id: 6 },
     *   { author: 'Sally', age: 21, status: 'draft', id: 7 },
     *   { author: 'Jim', age: 27, status: 'draft', id: 8 },
     *   { author: 'Jim', age: 27, status: 'published', id: 9 },
     *   { author: 'Jason', age: 55, status: 'published', id: 10 }
     * ];
     * store.add('post', posts);
     * // Retrieve a filtered page of blog posts
     * // Would typically replace filter with findAll
     * const results = store.filter('post', {
     *   where: {
     *     status: {
     *       // WHERE status = 'published'
     *       '==': 'published'
     *     },
     *     author: {
     *       // AND author IN ('bob', 'alice')
     *       'in': ['bob', 'alice'],
     *       // OR author IN ('karen')
     *       '|in': ['karen']
     *     }
     *   },
     *   orderBy: [
     *     // ORDER BY date_published DESC,
     *     ['date_published', 'DESC'],
     *     // ORDER BY title ASC
     *     ['title', 'ASC']
     *   ],
     *   // LIMIT 2
     *   limit: PAGE_SIZE,
     *   // SKIP 4
     *   offset: PAGE_SIZE * (currentPage - 1)
     * });
     * console.log(results);
     *
     * @namespace query
     * @property {number} [limit] See {@link query.limit}.
     * @property {number} [offset] See {@link query.offset}.
     * @property {string|Array[]} [orderBy] See {@link query.orderBy}.
     * @property {number} [skip] Alias for {@link query.offset}.
     * @property {string|Array[]} [sort] Alias for {@link query.orderBy}.
     * @property {Object} [where] See {@link query.where}.
     * @since 3.0.0
     * @tutorial ["http://www.js-data.io/v3.0/docs/query-syntax","JSData's Query Syntax"]
     */
    query || (query = {})
    this.getData()
    if (utils.isObject(query)) {
      let where = {}

      /**
       * Filtering criteria. Records that do not meet this criteria will be exluded
       * from the result.
       *
       * @example <caption>Return posts where author is at least 32 years old</caption>
       * const JSData = require('js-data');
       * const { DataStore } = JSData;
       * console.log('Using JSData v' + JSData.version.full);
       *
       * const store = new DataStore();
       * store.defineMapper('post')
       * const posts = [
       *   { author: 'John', age: 30, id: 5 },
       *   { author: 'Sally', age: 31, id: 6 },
       *   { author: 'Mike', age: 32, id: 7 },
       *   { author: 'Adam', age: 33, id: 8 },
       *   { author: 'Adam', age: 33, id: 9 }
       * ];
       * store.add('post', posts);
       * const results = store.filter('post', {
       *   where: {
       *     age: {
       *       '>=': 30
       *     }
       *   }
       * });
       * console.log(results);
       *
       * @name query.where
       * @type {Object}
       * @see http://www.js-data.io/v3.0/docs/query-syntax
       * @since 3.0.0
       */
      if (utils.isObject(query.where) || utils.isArray(query.where)) {
        where = query.where
      }
      utils.forOwn(query, function (value, key) {
        if (!(key in reserved) && !(key in where)) {
          where[key] = {
            '==': value
          }
        }
      })
      let groups

      // Apply filter for each field
      if (utils.isObject(where) && Object.keys(where).length !== 0) {
        groups = this._applyWhereFromArray([where])
      } else if (utils.isArray(where)) {
        groups = this._applyWhereFromArray(where)
      }

      if (groups) {
        this.data = this.data.filter((item, i) => this._testArrayGroup(true, true, groups, item).keep)
      }

      // Sort
      let orderBy = query.orderBy || query.sort

      if (utils.isString(orderBy)) {
        orderBy = [
          [orderBy, 'ASC']
        ]
      }
      if (!utils.isArray(orderBy)) {
        orderBy = null
      }

      /**
       * Determines how records should be ordered in the result.
       *
       * @example <caption>Order posts by `author` then by `id` descending </caption>
       * const JSData = require('js-data');
       * const { DataStore } = JSData;
       * console.log('Using JSData v' + JSData.version.full);
       *
       * const store = new DataStore();
       * store.defineMapper('post')
       * const posts = [
       *   { author: 'John', age: 30, id: 5 },
       *   { author: 'Sally', age: 31, id: 6 },
       *   { author: 'Mike', age: 32, id: 7 },
       *   { author: 'Adam', age: 33, id: 8 },
       *   { author: 'Adam', age: 33, id: 9 }
       * ];
       * store.add('post', posts);
       * const results = store.filter('post', {
       *     orderBy:[['author','ASC'],['id','DESC']]
       * });
       * console.log(results);
       *
       * @name query.orderBy
       * @type {string|Array[]}
       * @see http://www.js-data.io/v3.0/docs/query-syntax
       * @since 3.0.0
       */
      if (orderBy) {
        const index = 0
        orderBy.forEach(function (def, i) {
          if (utils.isString(def)) {
            orderBy[i] = [def, 'ASC']
          }
        })
        this.data.sort((a, b) => this.compare(orderBy, index, a, b))
      }

      /**
       * Number of records to skip.
       *
       * @example <caption>Retrieve the first "page" of blog posts using findAll</caption>
       * const JSData = require('js-data');
       * const { DataStore } = JSData;
       * console.log('Using JSData v' + JSData.version.full);
       *
       * const store = new DataStore();
       * store.defineMapper('post');
       * const PAGE_SIZE = 10;
       * let currentPage = 1;
       * store.findAll('post', {
       *   offset: PAGE_SIZE * (currentPage 1)
       *   limit: PAGE_SIZE
       * });
       *
       * @example <caption>Retrieve the last "page" of blog posts using filter</caption>
       * const JSData = require('js-data');
       * const { DataStore } = JSData;
       * console.log('Using JSData v' + JSData.version.full);
       *
       * const store = new DataStore();
       *
       * const PAGE_SIZE = 5;
       * let currentPage = 2;
       * store.defineMapper('post');
       * const posts = [
       *   { author: 'John', age: 30, id: 1 },
       *   { author: 'Sally', age: 31, id: 2 },
       *   { author: 'Mike', age: 32, id: 3 },
       *   { author: 'Adam', age: 33, id: 4 },
       *   { author: 'Adam', age: 33, id: 5 },
       *   { author: 'Peter', age: 25, id: 6 },
       *   { author: 'Sally', age: 21, id: 7 },
       *   { author: 'Jim', age: 27, id: 8 },
       *   { author: 'Jim', age: 27, id: 9 },
       *   { author: 'Jason', age: 55, id: 10 }
       * ];
       * store.add('post', posts);
       * const results = store.filter('post', {
       *   offset: PAGE_SIZE * (currentPage 1)
       *   limit: PAGE_SIZE
       * });
       * console.log(results)
       *
       * @name query.offset
       * @type {number}
       * @see http://www.js-data.io/v3.0/docs/query-syntax
       * @since 3.0.0
       */
      if (utils.isNumber(query.skip)) {
        this.skip(query.skip)
      } else if (utils.isNumber(query.offset)) {
        this.skip(query.offset)
      }

      /**
       * Maximum number of records to retrieve.
       *
       * @example <caption>Retrieve the first "page" of blog posts using findAll</caption>
       * const JSData = require('js-data');
       * const { DataStore } = JSData;
       * console.log('Using JSData v' + JSData.version.full);
       *
       * const store = new DataStore();
       * store.defineMapper('post');
       *
       * const PAGE_SIZE = 10
       * let currentPage = 1
       * store.findAll('post', {
       *   offset: PAGE_SIZE * (currentPage 1)
       *   limit: PAGE_SIZE
       * });
       *
       * @example <caption>Retrieve the last "page" of blog posts using filter</caption>
       * const JSData = require('js-data');
       * const { DataStore } = JSData;
       * console.log('Using JSData v' + JSData.version.full);
       *
       * const store = new DataStore();
       *
       * const PAGE_SIZE = 5
       * let currentPage = 2
       * store.defineMapper('post')
       * const posts = [
       *   { author: 'John', age: 30, id: 1 },
       *   { author: 'Sally', age: 31, id: 2 },
       *   { author: 'Mike', age: 32, id: 3 },
       *   { author: 'Adam', age: 33, id: 4 },
       *   { author: 'Adam', age: 33, id: 5 },
       *   { author: 'Peter', age: 25, id: 6 },
       *   { author: 'Sally', age: 21, id: 7 },
       *   { author: 'Jim', age: 27, id: 8 },
       *   { author: 'Jim', age: 27, id: 9 },
       *   { author: 'Jason', age: 55, id: 10 }
       * ];
       * store.add('post', posts);
       * const results = store.filter('post', {
       *   offset: PAGE_SIZE * (currentPage 1)
       *   limit: PAGE_SIZE
       * });
       * console.log(results)
       *
       * @name query.limit
       * @type {number}
       * @see http://www.js-data.io/v3.0/docs/query-syntax
       * @since 3.0.0
       */
      if (utils.isNumber(query.limit)) {
        this.limit(query.limit)
      }
    } else if (utils.isFunction(query)) {
      this.data = this.data.filter(query, thisArg)
    }
    return this
  }
Find the record or records that match the provided query or are accepted by
the provided filter function.

@example <caption>Get the draft posts by authors younger than 30</caption>
const JSData = require('js-data');
const { DataStore } = JSData;
console.log('Using JSData v' + JSData.version.full);

const store = new DataStore();
store.defineMapper('post')
const posts = [
  { author: 'John', age: 30, status: 'published', id: 1 },
  { author: 'Sally', age: 31, status: 'published', id: 2 },
  { author: 'Mike', age: 32, status: 'draft', id: 3 },
  { author: 'Adam', age: 33, status: 'deleted', id: 4 },
  { author: 'Adam', age: 33, status: 'published', id: 5 }
  { author: 'Peter', age: 25, status: 'deleted', id: 6 },
  { author: 'Sally', age: 21, status: 'draft', id: 7 },
  { author: 'Jim', age: 27, status: 'draft', id: 8 },
  { author: 'Jim', age: 27, status: 'published', id: 9 },
  { author: 'Jason', age: 55, status: 'published', id: 10 }
];
store.add('post', posts);
const results = store
  .query('post')
  .filter({
    where: {
      status: {
        '==': 'draft'
      },
      age: {
        '<': 30
      }
    }
  })
  .run();
console.log(results);

@example <caption>Use a custom filter function</caption>
const posts = query
  .filter(function (post) {
    return post.isReady();
  })
  .run();

@method Query#filter
@param {(Object|Function)} [queryOrFn={}] Selection query or filter
function.
@param {Function} [thisArg] Context to which to bind `queryOrFn` if
`queryOrFn` is a function.
@returns {Query} A reference to itself for chaining.
@since 3.0.0