<commit_before>if(!global._babelPolyfill) { require('babel-polyfill'); }

import Proto from 'uberproto';
import filter from 'feathers-query-filters';
import isPlainObject from 'is-plain-object';
import errorHandler from './error-handler';
import { errors } from 'feathers-errors';

const METHODS = {
  $or: 'orWhere',
  $ne: 'whereNot',
  $in: 'whereIn',
  $nin: 'whereNotIn'
};

const OPERATORS = {
  $lt: '<',
  $lte: '<=',
  $gt: '>',
  $gte: '>='
};

// Create the service.
class Service {
	constructor(options) {
    if (!options) {
      throw new Error('Knex options have to be provided');
    }

    if (!options.Model) {
      throw new Error('You must provide a Model (the initialized knex object)');
    }

    if (typeof options.name !== 'string') {
      throw new Error('No table name specified.');
    }

    this.knex = options.Model;
    this.id = options.id || 'id';
    this.paginate = options.paginate || {};
    this.table = options.name;
	}

  // NOTE (EK): We need this method so that we return a new query
  // instance each time, otherwise it will reuse the same query.
  db() {
    return this.knex(this.table);
  }

  extend(obj) {
		return Proto.extend(obj, this);
	}

  knexify(query, params, parentKey) {
    Object.keys(params || {}).forEach((key) => {
      const value = params[key];

      if (isPlainObject(value)) {
        return this.knexify(query, value, key);
      }

      const column = parentKey || key;
      const method = METHODS[key];
      const operator = OPERATORS[key] || '=';

      // TODO (EK): Handle $or queries with nested specials.
      // Right now they won't work and we'd need to start diving
      // into nested where conditions.
      if (method) {
        if (key === '$or') {
          return value.forEach(condition => query[method].call(query, condition));
        }

        return query[method].call(query, column, value);
      }

      return query.where(column, operator, value);
    });
  }

	_find(params, count, getFilter = filter) {
    let query = this.db().select(['*']);
    let filters = getFilter(params.query || {});

		// $select uses a specific find syntax, so it has to come first.
		if (filters.$select) {
      let fields = filters.$select;
      query = this.db().select(... fields);
		}

    // build up the knex query out of the query params
    this.knexify(query, params.query);

		// Handle $sort
		if (filters.$sort) {
      Object.keys(filters.$sort).forEach(key =>
        query = query.orderBy(key, parseInt(filters.$sort[key], 10) === 1 ? 'asc' : 'desc'));
		}

		// Handle $limit
		if (filters.$limit) {
      query.limit(filters.$limit);
		}

		// Handle $skip
		if (filters.$skip) {
      query.offset(filters.$skip);
		}

    const executeQuery = total => {
      return query.then(data => {
        return {
          total,
          limit: filters.$limit,
          skip: filters.$skip || 0,
          data
        };
      });
    };

    if(count) {
      let countQuery = this.db().count(`${this.id} as total`);

      this.knexify(countQuery, params.query);

      return countQuery.then(count => count[0].total).then(executeQuery);
    }

    return executeQuery();
	}

  find(params) {
    const paginate = !!this.paginate.default;
    const result = this._find(params, paginate, query => filter(query, this.paginate));

    if(!paginate) {
      return result.then(page => page.data);
    }

    return result;
  }

	_get(id, params) {
    params.query = params.query || {};
    params.query[this.id] = id;

    return this._find(params)
      .then(page => {
        if(page.data.length !== 1) {
          throw new errors.NotFound(`No record found for id '${id}'`);
        }

        return page.data[0];
      }).catch(errorHandler);
	}

  get(...args) {
    return this._get(...args);
  }

  _create(data, params) {
    return this.db().insert(data, this.id).then(rows => this.get(rows[0], params))
      .catch(errorHandler);
  }

	create(data, params) {
    if(Array.isArray(data)) {
      return Promise.all(data.map(current => this._create(current, params)));
    }

    return this._create(data, params);
	}

	patch(id, data, params) {
    params.query = params.query || {};
    data = Object.assign({}, data);

    if(id !== null) {
      params.query[this.id] = id;
    }
<commit_msg>Use internal methods in create (#51)<commit_after>if(!global._babelPolyfill) { require('babel-polyfill'); }

import Proto from 'uberproto';
import filter from 'feathers-query-filters';
import isPlainObject from 'is-plain-object';
import errorHandler from './error-handler';
import { errors } from 'feathers-errors';

const METHODS = {
  $or: 'orWhere',
  $ne: 'whereNot',
  $in: 'whereIn',
  $nin: 'whereNotIn'
};

const OPERATORS = {
  $lt: '<',
  $lte: '<=',
  $gt: '>',
  $gte: '>='
};

// Create the service.
class Service {
	constructor(options) {
    if (!options) {
      throw new Error('Knex options have to be provided');
    }

    if (!options.Model) {
      throw new Error('You must provide a Model (the initialized knex object)');
    }

    if (typeof options.name !== 'string') {
      throw new Error('No table name specified.');
    }

    this.knex = options.Model;
    this.id = options.id || 'id';
    this.paginate = options.paginate || {};
    this.table = options.name;
	}

  // NOTE (EK): We need this method so that we return a new query
  // instance each time, otherwise it will reuse the same query.
  db() {
    return this.knex(this.table);
  }

  extend(obj) {
		return Proto.extend(obj, this);
	}

  knexify(query, params, parentKey) {
    Object.keys(params || {}).forEach((key) => {
      const value = params[key];

      if (isPlainObject(value)) {
        return this.knexify(query, value, key);
      }

      const column = parentKey || key;
      const method = METHODS[key];
      const operator = OPERATORS[key] || '=';

      // TODO (EK): Handle $or queries with nested specials.
      // Right now they won't work and we'd need to start diving
      // into nested where conditions.
      if (method) {
        if (key === '$or') {
          return value.forEach(condition => query[method].call(query, condition));
        }

        return query[method].call(query, column, value);
      }

      return query.where(column, operator, value);
    });
  }

	_find(params, count, getFilter = filter) {
    let query = this.db().select(['*']);
    let filters = getFilter(params.query || {});

		// $select uses a specific find syntax, so it has to come first.
		if (filters.$select) {
      let fields = filters.$select;
      query = this.db().select(... fields);
		}

    // build up the knex query out of the query params
    this.knexify(query, params.query);

		// Handle $sort
		if (filters.$sort) {
      Object.keys(filters.$sort).forEach(key =>
        query = query.orderBy(key, parseInt(filters.$sort[key], 10) === 1 ? 'asc' : 'desc'));
		}

		// Handle $limit
		if (filters.$limit) {
      query.limit(filters.$limit);
		}

		// Handle $skip
		if (filters.$skip) {
      query.offset(filters.$skip);
		}

    const executeQuery = total => {
      return query.then(data => {
        return {
          total,
          limit: filters.$limit,
          skip: filters.$skip || 0,
          data
        };
      });
    };

    if(count) {
      let countQuery = this.db().count(`${this.id} as total`);

      this.knexify(countQuery, params.query);

      return countQuery.then(count => count[0].total).then(executeQuery);
    }

    return executeQuery();
	}

  find(params) {
    const paginate = !!this.paginate.default;
    const result = this._find(params, paginate, query => filter(query, this.paginate));

    if(!paginate) {
      return result.then(page => page.data);
    }

    return result;
  }

	_get(id, params) {
    params.query = params.query || {};
    params.query[this.id] = id;

    return this._find(params)
      .then(page => {
        if(page.data.length !== 1) {
          throw new errors.NotFound(`No record found for id '${id}'`);
        }

        return page.data[0];
      }).catch(errorHandler);
	}

  get(...args) {
    return this._get(...args);
  }

  _create(data, params) {
    return this.db().insert(data, this.id).then(rows => this._get(rows[0], params))
      .catch(errorHandler);
  }

	create(data, params) {
    if(Array.isArray(data)) {
      return Promise.all(data.map(current => this._create(current, params)));
    }

    return this._create(data, params);
	}

	patch(id, data, params) {
    params.query = params.query || {};
    data = Object.assign({}, data);

    if(id !== null) {
      params.query[this.id] = id;
    }

{'fraction_non_alphanumeric': 0.16227157066851725, 'fraction_numerical': 0.002081887578070784, 'mean_word_length': 2.5525883319638454, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2428098', 'n_tokens_mistral': 2862, 'n_tokens_neox': 2748, 'n_words': 689}