<commit_before>/**
 * Module dependencies
 */

var util = require('util');
var _ = require('@sailshq/lodash');
var flaverr = require('flaverr');
var getModel = require('../../ontology/get-model');
var getAttribute = require('../../ontology/get-attribute');
var isValidAttributeName = require('./is-valid-attribute-name');


/**
 * normalizeSortClause()
 *
 * Validate and normalize the `sort` clause, rejecting obviously unsupported usage,
 * and tolerating certain backwards-compatible things.
 *
 * --
 *
 * @param  {Ref} sortClause
 *         A hypothetically well-formed `sort` clause from a Waterline criteria.
 *         (i.e. in a "stage 1 query")
 *         > WARNING:
 *         > IN SOME CASES (BUT NOT ALL!), THE PROVIDED VALUE WILL
 *         > UNDERGO DESTRUCTIVE, IN-PLACE CHANGES JUST BY PASSING IT
 *         > IN TO THIS UTILITY.
 *
 * @param {String} modelIdentity
 *        The identity of the model this `sort` clause is referring to (e.g. "pet" or "user")
 *        > Useful for looking up the Waterline model and accessing its attribute definitions.
 *
 * @param {Ref} orm
 *        The Waterline ORM instance.
 *        > Useful for accessing the model definitions.
 * --
 *
 * @returns {Dictionary}
 *          The successfully-normalized `sort` clause, ready for use in a stage 2 query.
 *          > Note that the originally provided `sort` clause MAY ALSO HAVE BEEN
 *          > MUTATED IN PLACE!
 *
 *
 * @throws {Error} If it encounters irrecoverable problems or unsupported usage in
 *                 the provided `sort` clause.
 *         @property {String} code
 *                   - E_SORT_CLAUSE_UNUSABLE
 *
 *
 * @throws {Error} If anything else unexpected occurs.
 */

module.exports = function normalizeSortClause(sortClause, modelIdentity, orm) {

  // Look up the Waterline model for this query.
  // > This is so that we can reference the original model definition.
  var WLModel = getModel(modelIdentity, orm);

  // COMPATIBILITY
  // Tolerate empty array (`[]`), understanding it to mean the same thing as `undefined`.
  if (_.isArray(sortClause) && sortClause.length === 0) {
    sortClause = undefined;
    // Note that this will be further expanded momentarily.
  }//>-


  //  ╔╦╗╔═╗╔═╗╔═╗╦ ╦╦ ╔╦╗
  //   ║║║╣ ╠╣ ╠═╣║ ║║  ║
  //  ═╩╝╚═╝╚  ╩ ╩╚═╝╩═╝╩
  // If no `sort` clause was provided, give it a default value so that
  // this criteria indicates that matching records should be examined
  // in ascending order of their primary key values.
  // e.g. `[ { id: 'ASC' } ]`
  if (_.isUndefined(sortClause)) {
    sortClause = [ {} ];
    sortClause[0][WLModel.primaryKey] = 'ASC';
  }//>-

  // If `sort` was provided as a string, then expand it into an array.
  // (We'll continue cleaning it up down below-- this is just to get
  // it part of the way there-- e.g. we might end up with something like:
  // `[ 'name DESC' ]`)
  if (_.isString(sortClause)) {
    sortClause = [
      sortClause
    ];
  }//>-

  // If `sort` was provided as a dictionary...
  if (_.isObject(sortClause) && !_.isArray(sortClause) && !_.isFunction(sortClause)) {

    sortClause = _.reduce(_.keys(sortClause), function (memo, sortByKey) {

      var sortDirection = sortClause[sortByKey];

      // It this appears to be a well-formed comparator directive that was simply mistakenly
      // provided at the top level instead of being wrapped in an array, then throw an error
      // specifically mentioning that.
      if (_.isString(sortDirection) && _.keys(sortClause).length === 1) {
        throw flaverr('E_SORT_CLAUSE_UNUSABLE', new Error(
          'The `sort` clause in the provided criteria is invalid.  If specified, it should be either '+
          'a string like `\'fullName DESC\'`, or an array like `[ { fullName: \'DESC\' } ]`.  '+
          'But it looks like you might need to wrap this in an array, because instead, got: '+
          util.inspect(sortClause, {depth:5})+''
        ));
      }//-•


      // Otherwise, continue attempting to normalize this dictionary into array
      // format under the assumption that it was provided as a Mongo-style comparator
      // dictionary. (and freaking out if we see anything that makes us uncomfortable)
      var newComparatorDirective = {};
      if (sortDirection === 1) {
        newComparatorDirective[sortByKey] = 'ASC';
      }
      else if (sortDirection === -1) {
        newComparatorDirective[sortByKey] = 'DESC';
      }
      else {
        throw flaverr('E_SORT_CLAUSE_UNUSABLE', new Error(
          'The `sort` clause in the provided criteria is invalid.  If specified as a '+
          'dictionary, it should use Mongo-esque semantics, using -1 and 1 for the sort '+
          'direction  (something like `{ fullName: -1, rank: 1 }`).  But instead, got: '+
          util.inspect(sortClause, {depth:5})+''
        ));
      }
      memo.push(newComparatorDirective);

      return memo;

    }, []);//</_.reduce()>


  }//>-


  // If, by this time, `sort` is not an array...
  if (!_.isArray(sortClause)) {
    // Then the provided `sort` must have been highly irregular indeed.
    throw flaverr('E_SORT_CLAUSE_UNUSABLE', new Error(
      'The `sort` clause in the provided criteria is invalid.  If specified, it should be either '+
      'a string like `\'fullName DESC\'`, or an array like `[ { fullName: \'DESC\' } ]`.  '+
      'But instead, got: '+
      util.inspect(sortClause, {depth:5})+''
    ));
  }//-•



  // Ensure that each item in the array is a structurally-valid comparator directive:
  sortClause = _.map(sortClause, function (comparatorDirective){

    //  ┌┬┐┌─┐┬  ┌─┐┬─┐┌─┐┌┬┐┌─┐  ┌─┐┌┬┐┬─┐┬┌┐┌┌─┐  ┬ ┬┌─┐┌─┐┌─┐┌─┐
    //   │ │ ││  ├┤ ├┬┘├─┤ │ ├┤   └─┐ │ ├┬┘│││││ ┬  │ │└─┐├─┤│ ┬├┤
    //   ┴ └─┘┴─┘└─┘┴└─┴ ┴ ┴ └─┘  └─┘ ┴ ┴└─┴┘└┘└─┘  └─┘└─┘┴ ┴└─┘└─┘
    //  ┌─  ┌─┐ ┌─┐     ╔═╗╔╦╗╔═╗╦╦  ╔═╗╔╦╗╔╦╗╦═╗╔═╗╔═╗╔═╗  ╔═╗╔═╗╔═╗  ─┐
    //  │   ├┤  │ ┬     ║╣ ║║║╠═╣║║  ╠═╣ ║║ ║║╠╦╝║╣ ╚═╗╚═╗  ╠═╣╚═╗║     │
    //  └─  └─┘o└─┘o    ╚═╝╩ ╩╩ ╩╩╩═╝╩ ╩═╩╝═╩╝╩╚═╚═╝╚═╝╚═╝  ╩ ╩╚═╝╚═╝  ─┘
    // If this is a string, then morph it into a dictionary.
    //
    // > This is so that we tolerate syntax like `'name ASC'`
    // > at the top level (since we would have expanded it above)
    // > AND when provided within the array (e.g. `[ 'name ASC' ]`)
    if (_.isString(comparatorDirective)) {<commit_msg>If sort clause is provided as empty dictionary, handle it like []/undefined.  Also add deprecation message that is logged any time Mongo-esque sort semantics are used (i.e. a dictionary).<commit_after>/**
 * Module dependencies
 */

var util = require('util');
var _ = require('@sailshq/lodash');
var flaverr = require('flaverr');
var getModel = require('../../ontology/get-model');
var getAttribute = require('../../ontology/get-attribute');
var isValidAttributeName = require('./is-valid-attribute-name');


/**
 * normalizeSortClause()
 *
 * Validate and normalize the `sort` clause, rejecting obviously unsupported usage,
 * and tolerating certain backwards-compatible things.
 *
 * --
 *
 * @param  {Ref} sortClause
 *         A hypothetically well-formed `sort` clause from a Waterline criteria.
 *         (i.e. in a "stage 1 query")
 *         > WARNING:
 *         > IN SOME CASES (BUT NOT ALL!), THE PROVIDED VALUE WILL
 *         > UNDERGO DESTRUCTIVE, IN-PLACE CHANGES JUST BY PASSING IT
 *         > IN TO THIS UTILITY.
 *
 * @param {String} modelIdentity
 *        The identity of the model this `sort` clause is referring to (e.g. "pet" or "user")
 *        > Useful for looking up the Waterline model and accessing its attribute definitions.
 *
 * @param {Ref} orm
 *        The Waterline ORM instance.
 *        > Useful for accessing the model definitions.
 * --
 *
 * @returns {Dictionary}
 *          The successfully-normalized `sort` clause, ready for use in a stage 2 query.
 *          > Note that the originally provided `sort` clause MAY ALSO HAVE BEEN
 *          > MUTATED IN PLACE!
 *
 *
 * @throws {Error} If it encounters irrecoverable problems or unsupported usage in
 *                 the provided `sort` clause.
 *         @property {String} code
 *                   - E_SORT_CLAUSE_UNUSABLE
 *
 *
 * @throws {Error} If anything else unexpected occurs.
 */

module.exports = function normalizeSortClause(sortClause, modelIdentity, orm) {

  // Look up the Waterline model for this query.
  // > This is so that we can reference the original model definition.
  var WLModel = getModel(modelIdentity, orm);

  //  ╔═╗╔═╗╔╦╗╔═╗╔═╗╔╦╗╦╔╗ ╦╦  ╦╔╦╗╦ ╦
  //  ║  ║ ║║║║╠═╝╠═╣ ║ ║╠╩╗║║  ║ ║ ╚╦╝
  //  ╚═╝╚═╝╩ ╩╩  ╩ ╩ ╩ ╩╚═╝╩╩═╝╩ ╩  ╩
  // If `sort` was provided as a dictionary...
  if (_.isObject(sortClause) && !_.isArray(sortClause) && !_.isFunction(sortClause)) {

    sortClause = _.reduce(_.keys(sortClause), function (memo, sortByKey) {

      var sortDirection = sortClause[sortByKey];

      // It this appears to be a well-formed comparator directive that was simply mistakenly
      // provided at the top level instead of being wrapped in an array, then throw an error
      // specifically mentioning that.
      if (_.isString(sortDirection) && _.keys(sortClause).length === 1) {
        throw flaverr('E_SORT_CLAUSE_UNUSABLE', new Error(
          'The `sort` clause in the provided criteria is invalid.  If specified, it should be either '+
          'a string like `\'fullName DESC\'`, or an array like `[ { fullName: \'DESC\' } ]`.  '+
          'But it looks like you might need to wrap this in an array, because instead, got: '+
          util.inspect(sortClause, {depth:5})+''
        ));
      }//-•


      // Otherwise, continue attempting to normalize this dictionary into array
      // format under the assumption that it was provided as a Mongo-style comparator
      // dictionary. (and freaking out if we see anything that makes us uncomfortable)
      var newComparatorDirective = {};
      if (sortDirection === 1) {
        newComparatorDirective[sortByKey] = 'ASC';
      }
      else if (sortDirection === -1) {
        newComparatorDirective[sortByKey] = 'DESC';
      }
      else {
        throw flaverr('E_SORT_CLAUSE_UNUSABLE', new Error(
          'The `sort` clause in the provided criteria is invalid.  If specified as a '+
          'dictionary, it should use Mongo-esque semantics, using -1 and 1 for the sort '+
          'direction  (something like `{ fullName: -1, rank: 1 }`).  But instead, got: '+
          util.inspect(sortClause, {depth:5})+''
        ));
      }
      memo.push(newComparatorDirective);

      return memo;

    }, []);//</_.reduce()>

    // IWMIH, then we know a dictionary was provided that appears to be using valid Mongo-esque
    // semantics, or that is at least an empty dictionary.  Nonetheless, this usage is not recommended,
    // and might be removed in the future.  So log a warning:
    console.warn('\n'+
      'Warning: The `sort` clause in the provided criteria is specified as a dictionary (plain JS object),\n'+
      'meaning that it is presumably using Mongo-esque semantics (something like `{ fullName: -1, rank: 1 }`).\n'+
      'But as of Sails v1/Waterline 0.13, this is no longer the recommended usage.  Instead, please use either\n'+
      'a string like `\'fullName DESC\'`, or an array like `[ { fullName: \'DESC\' } ]`.\n'+
      '(Since I get what you mean, tolerating & remapping this usage for now...)\n'
    );

  }//>-


  // Tolerate empty array (`[]`), understanding it to mean the same thing as `undefined`.
  if (_.isArray(sortClause) && sortClause.length === 0) {
    sortClause = undefined;
    // Note that this will be further expanded momentarily.
  }//>-



  //  ╔╦╗╔═╗╔═╗╔═╗╦ ╦╦ ╔╦╗
  //   ║║║╣ ╠╣ ╠═╣║ ║║  ║
  //  ═╩╝╚═╝╚  ╩ ╩╚═╝╩═╝╩
  // If no `sort` clause was provided, give it a default value so that
  // this criteria indicates that matching records should be examined
  // in ascending order of their primary key values.
  // e.g. `[ { id: 'ASC' } ]`
  if (_.isUndefined(sortClause)) {
    sortClause = [ {} ];
    sortClause[0][WLModel.primaryKey] = 'ASC';
  }//>-

  // If `sort` was provided as a string, then expand it into an array.
  // (We'll continue cleaning it up down below-- this is just to get
  // it part of the way there-- e.g. we might end up with something like:
  // `[ 'name DESC' ]`)
  if (_.isString(sortClause)) {
    sortClause = [
      sortClause
    ];
  }//>-


  // If, by this time, `sort` is not an array...
  if (!_.isArray(sortClause)) {
    // Then the provided `sort` must have been highly irregular indeed.
    throw flaverr('E_SORT_CLAUSE_UNUSABLE', new Error(
      'The `sort` clause in the provided criteria is invalid.  If specified, it should be either '+
      'a string like `\'fullName DESC\'`, or an array like `[ { fullName: \'DESC\' } ]`.  '+
      'But instead, got: '+
      util.inspect(sortClause, {depth:5})+''
    ));
  }//-•



  // Ensure that each item in the array is a structurally-valid comparator directive:
  sortClause = _.map(sortClause, function (comparatorDirective){

    //  ┌┬┐┌─┐┬  ┌─┐┬─┐┌─┐┌┬┐┌─┐  ┌─┐┌┬┐┬─┐┬┌┐┌┌─┐  ┬ ┬┌─┐┌─┐┌─┐┌─┐
    //   │ │ ││  ├┤ ├┬┘├─┤ │ ├┤   └─┐ │ ├┬┘│││││ ┬  │ │└─┐├─┤│ ┬├┤
    //   ┴ └─┘┴─┘└─┘┴└─┴ ┴ ┴ └─┘  └─┘ ┴ ┴└─┴┘└┘└─┘  └─┘└─┘┴ ┴└─┘└─┘
    //  ┌─  ┌─┐ ┌─┐     ╔═╗╔╦╗╔═╗╦╦  ╔═╗╔╦╗╔╦╗╦═╗╔═╗╔═╗╔═╗  ╔═╗╔═╗╔═╗  ─┐
    //  │   ├┤  │ ┬     ║╣ ║║║╠═╣║║  ╠═╣ ║║ ║║╠╦╝║╣ ╚═╗╚═╗  ╠═╣╚═╗║     │
    //  └─  └─┘o└─┘o    ╚═╝╩ ╩╩ ╩╩╩═╝╩ ╩═╩╝═╩╝╩╚═╚═╝╚═╝╚═╝  ╩ ╩╚═╝╚═╝  ─┘
    // If this is a string, then morph it into a dictionary.
    //
    // > This is so that we tolerate syntax like `'name ASC'`
    // > at the top level (since we would have expanded it above)
    // > AND when provided within the array (e.g. `[ 'name ASC' ]`)
    if (_.isString(comparatorDirective)) {
{'fraction_non_alphanumeric': 0.1658426639523048, 'fraction_numerical': 0.002472008143085648, 'mean_word_length': 2.6055045871559632, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 37, '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': '4151204', 'n_tokens_mistral': 4970, 'n_tokens_neox': 5120, 'n_words': 1547}