BVB Source Codes

waterline Show forge-stage-two-query.js Source code

Return Download waterline: download forge-stage-two-query.js Source code - Download waterline Source code - Type:.js
  1. /**
  2.  * Module dependencies
  3.  */
  4.  
  5. var assert = require('assert');
  6. var util = require('util');
  7. var _ = require('@sailshq/lodash');
  8. var getModel = require('../ontology/get-model');
  9. var getAttribute = require('../ontology/get-attribute');
  10. var isCapableOfOptimizedPopulate = require('../ontology/is-capable-of-optimized-populate');
  11. var isExclusive = require('../ontology/is-exclusive');
  12. var normalizePkValueOrValues = require('./private/normalize-pk-value-or-values');
  13. var normalizeCriteria = require('./private/normalize-criteria');
  14. var normalizeNewRecord = require('./private/normalize-new-record');
  15. var normalizeValueToSet = require('./private/normalize-value-to-set');
  16. var buildUsageError = require('./private/build-usage-error');
  17.  
  18.  
  19. /**
  20.  * forgeStageTwoQuery()
  21.  *
  22.  * Normalize and validate userland query keys (called a "stage 1 query" -- see `ARCHITECTURE.md`)
  23.  * i.e. these are things like `criteria` or `populates` that are passed in, either explicitly or
  24.  * implicitly, to a static model method (fka "collection method") such as `.find()`.
  25.  *
  26.  * > This DOES NOT RETURN ANYTHING!  Instead, it modifies the provided "stage 1 query" in-place.
  27.  * > And when this is finished, the provided "stage 1 query" will be a normalized, validated
  28.  * > "stage 2 query" - aka logical protostatement.
  29.  *
  30.  * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  31.  *
  32.  * @param {Dictionary} query   [A stage 1 query to destructively mutate into a stage 2 query.]
  33.  *   | @property {String} method
  34.  *   | @property {Dictionary} meta
  35.  *   | @property {String} using
  36.  *   |
  37.  *   |...PLUS a number of other potential properties, depending on the "method". (see below)
  38.  *
  39.  *
  40.  * @param {Ref} orm
  41.  *        The Waterline ORM instance.
  42.  *        > Useful for accessing the model definitions, datastore configurations, etc.
  43.  *
  44.  * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  45.  *
  46.  * @throws {Error} If it encounters irrecoverable problems or unsupported usage in the provided query keys.
  47.  *         @property {String} name        (Always "UsageError")
  48.  *         @property {String} code
  49.  *                   One of:
  50.  *                   - E_INVALID_META                    (universal)
  51.  *                   - E_INVALID_CRITERIA
  52.  *                   - E_INVALID_POPULATES
  53.  *                   - E_INVALID_NUMERIC_ATTR_NAME
  54.  *                   - E_INVALID_STREAM_ITERATEE         (for `eachBatchFn` & `eachRecordFn`)
  55.  *                   - E_INVALID_NEW_RECORD
  56.  *                   - E_INVALID_NEW_RECORDS
  57.  *                   - E_INVALID_VALUES_TO_SET
  58.  *                   - E_INVALID_TARGET_RECORD_IDS
  59.  *                   - E_INVALID_COLLECTION_ATTR_NAME
  60.  *                   - E_INVALID_ASSOCIATED_IDS
  61.  *                   - E_NOOP                            (relevant for various different methods, like find/count/addToCollection/etc.)
  62.  *         @property {String} details
  63.  *                   The lower-level, original error message, without any sort of "Invalid yada yada.  Details: ..." wrapping.
  64.  *                   Use this property to create custom messages -- for example:
  65.  *                   ```
  66.  *                   new Error(e.details);
  67.  *                   ```
  68.  *         @property {String} message
  69.  *                   The standard `message` property of any Error-- just note that this Error's `message` is composed
  70.  *                   from an original, lower-level error plus a template (see buildUsageError() for details.)
  71.  *         @property {String} stack
  72.  *                   The standard `stack` property, like any Error.  Combines name + message + stack trace.
  73.  *
  74.  * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  75.  *
  76.  * @throws {Error} If anything else unexpected occurs
  77.  *
  78.  * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  79.  */
  80. module.exports = function forgeStageTwoQuery(query, orm) {
  81.   // if (process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'test') {
  82.   //   console.time('forgeStageTwoQuery');
  83.   // }
  84.  
  85.  
  86.   // Create a JS timestamp to represent the current (timezone-agnostic) date+time.
  87.   var theMomentBeforeFS2Q = Date.now();
  88.   // ^^    --    --   --    --    --   --    --    --   --    --    --   --    --
  89.   // Since Date.now() has trivial performance impact, we generate our
  90.   // JS timestamp up here no matter what, just in case we end up needing
  91.   // it later for `autoCreatedAt` or `autoUpdatedAt`, in situations where
  92.   // we might need to automatically add it in multiple spots (such as
  93.   // in `newRecords`, when processing a `.createEach()`.)
  94.   //
  95.   // > Benchmark:
  96.   // >  鈥 Absolute: ~0.021ms
  97.   // >  鈥 Relative: http://jsben.ch/#/TOF9y    (vs. `(new Date()).getTime()`)
  98.   // --    --    --   --    --    --   --    --    --   --    --    --   --    --
  99.  
  100.  
  101.  
  102.   //   鈻堚枅鈻堚枅鈻堚枅鈺椻枅鈻堚晽  鈻堚枅鈺椻枅鈻堚枅鈻堚枅鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚枅鈺椻枅鈻堚晽  鈻堚枅鈺    鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈺椻枅鈻堚晽  鈻堚枅鈺椻枅鈻堚枅鈻堚枅鈻堚枅鈺
  103.   //  鈻堚枅鈺斺晲鈺愨晲鈺愨暆鈻堚枅鈺  鈻堚枅鈺戔枅鈻堚晹鈺愨晲鈺愨晲鈺濃枅鈻堚晹鈺愨晲鈺愨晲鈺濃枅鈻堚晳 鈻堚枅鈺斺暆    鈺氣晲鈺愨枅鈻堚晹鈺愨晲鈺濃枅鈻堚晳  鈻堚枅鈺戔枅鈻堚晹鈺愨晲鈺愨晲鈺
  104.   //  鈻堚枅鈺     鈻堚枅鈻堚枅鈻堚枅鈻堚晳鈻堚枅鈻堚枅鈻堚晽  鈻堚枅鈺     鈻堚枅鈻堚枅鈻堚晹鈺        鈻堚枅鈺   鈻堚枅鈻堚枅鈻堚枅鈻堚晳鈻堚枅鈻堚枅鈻堚晽
  105.   //  鈻堚枅鈺     鈻堚枅鈺斺晲鈺愨枅鈻堚晳鈻堚枅鈺斺晲鈺愨暆  鈻堚枅鈺     鈻堚枅鈺斺晲鈻堚枅鈺        鈻堚枅鈺   鈻堚枅鈺斺晲鈺愨枅鈻堚晳鈻堚枅鈺斺晲鈺愨暆
  106.   //  鈺氣枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈺  鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚枅鈺椻暁鈻堚枅鈻堚枅鈻堚枅鈺椻枅鈻堚晳  鈻堚枅鈺       鈻堚枅鈺   鈻堚枅鈺  鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚枅鈺
  107.   //   鈺氣晲鈺愨晲鈺愨晲鈺濃暁鈺愨暆  鈺氣晲鈺濃暁鈺愨晲鈺愨晲鈺愨晲鈺 鈺氣晲鈺愨晲鈺愨晲鈺濃暁鈺愨暆  鈺氣晲鈺       鈺氣晲鈺   鈺氣晲鈺  鈺氣晲鈺濃暁鈺愨晲鈺愨晲鈺愨晲鈺
  108.   //
  109.   //  鈻堚枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈻堚晽   鈻堚枅鈺椻枅鈻堚枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚晽 鈻堚枅鈺     鈻堚枅鈻堚枅鈻堚枅鈻堚晽
  110.   //  鈻堚枅鈺斺晲鈺愨晲鈺愨暆鈻堚枅鈺斺晲鈺愨晲鈺愨暆鈻堚枅鈺斺晲鈺愨晲鈺愨暆鈻堚枅鈺斺晲鈺愨晲鈺愨暆鈻堚枅鈻堚枅鈺  鈻堚枅鈺戔暁鈺愨晲鈻堚枅鈺斺晲鈺愨暆鈻堚枅鈺戔枅鈻堚晹鈺愨晲鈻堚枅鈺椻枅鈻堚晳     鈻堚枅鈺斺晲鈺愨晲鈺愨暆
  111.   //  鈻堚枅鈻堚枅鈻堚晽  鈻堚枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈻堚枅鈻堚晽  鈻堚枅鈺斺枅鈻堚晽 鈻堚枅鈺   鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚枅鈺戔枅鈻堚晳     鈻堚枅鈻堚枅鈻堚枅鈻堚晽
  112.   //  鈻堚枅鈺斺晲鈺愨暆  鈺氣晲鈺愨晲鈺愨枅鈻堚晳鈺氣晲鈺愨晲鈺愨枅鈻堚晳鈻堚枅鈺斺晲鈺愨暆  鈻堚枅鈺戔暁鈻堚枅鈺椻枅鈻堚晳   鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚晹鈺愨晲鈻堚枅鈺戔枅鈻堚晳     鈺氣晲鈺愨晲鈺愨枅鈻堚晳
  113.   //  鈻堚枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈻堚枅鈻堚枅鈻堚晳鈻堚枅鈻堚枅鈻堚枅鈻堚晳鈻堚枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈺 鈺氣枅鈻堚枅鈻堚晳   鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚晳  鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚枅鈺椻枅鈻堚枅鈻堚枅鈻堚枅鈺
  114.   //  鈺氣晲鈺愨晲鈺愨晲鈺愨暆鈺氣晲鈺愨晲鈺愨晲鈺愨暆鈺氣晲鈺愨晲鈺愨晲鈺愨暆鈺氣晲鈺愨晲鈺愨晲鈺愨暆鈺氣晲鈺  鈺氣晲鈺愨晲鈺   鈺氣晲鈺   鈺氣晲鈺濃暁鈺愨暆  鈺氣晲鈺濃暁鈺愨晲鈺愨晲鈺愨晲鈺濃暁鈺愨晲鈺愨晲鈺愨晲鈺
  115.  
  116.  
  117.   //  鈹屸攢鈹愨敩 鈹攲鈹€鈹愨攲鈹€鈹愨敩鈹屸攢  鈺 鈺︹晹鈺愨晽鈺︹晹鈺椻晹鈺斺晲鈺
  118.   //  鈹  鈹溾攢鈹も敎鈹 鈹  鈹溾敶鈹  鈺 鈺戔暁鈺愨晽鈺戔晳鈺戔晳鈺 鈺
  119.   //  鈹斺攢鈹樷敶 鈹粹敂鈹€鈹樷敂鈹€鈹樷敶 鈹  鈺氣晲鈺濃暁鈺愨暆鈺┾暆鈺氣暆鈺氣晲鈺
  120.   // Always check `using`.
  121.   if (!_.isString(query.using) || query.using === '') {
  122.     throw new Error(
  123.       'Consistency violation: Every stage 1 query should include a property called `using` as a non-empty string.'+
  124.       '  But instead, got: ' + util.inspect(query.using, {depth:5})
  125.     );
  126.   }//-鈥
  127.  
  128.  
  129.   // Look up the Waterline model for this query.
  130.   // > This is so that we can reference the original model definition.
  131.   var WLModel;
  132.   try {
  133.     WLModel = getModel(query.using, orm);
  134.   } catch (e) {
  135.     switch (e.code) {
  136.       case 'E_MODEL_NOT_REGISTERED': throw new Error('Consistency violation: The specified `using` ("'+query.using+'") does not match the identity of any registered model.');
  137.       default: throw e;
  138.     }
  139.   }//</catch>
  140.  
  141.  
  142.   //  鈹屸攢鈹愨敩 鈹攲鈹€鈹愨攲鈹€鈹愨敩鈹屸攢  鈺斺暒鈺椻晹鈺愨晽鈺斺暒鈺椻晹鈺愨晽    鈹屸攢  鈹攲鈹€鈹  鈹屸攢鈹愨敩鈹€鈹愨攲鈹€鈹愨敩  鈹敩鈹屸敩鈹愨攲鈹€鈹愨攲鈹攼  鈹€鈹
  143.   //  鈹  鈹溾攢鈹も敎鈹 鈹  鈹溾敶鈹  鈺戔晳鈺戔晳鈺  鈺 鈺犫晲鈺    鈹   鈹傗敎鈹   鈹溾攢鈹樷敎鈹敇鈹 鈹傗敂鈹愨攲鈹樷攤 鈹傗攤鈹溾敜  鈹傗攤   鈹
  144.   //  鈹斺攢鈹樷敶 鈹粹敂鈹€鈹樷敂鈹€鈹樷敶 鈹  鈺 鈺┾暁鈺愨暆 鈺 鈺 鈺    鈹斺攢  鈹粹敂    鈹  鈹粹敂鈹€鈹斺攢鈹 鈹斺敇 鈹粹攢鈹粹敇鈹斺攢鈹樷攢鈹粹敇  鈹€鈹
  145.   // If specified, check `meta`.
  146.   if (!_.isUndefined(query.meta)) {
  147.  
  148.     if (!_.isObject(query.meta) || _.isArray(query.meta) || _.isFunction(query.meta)) {
  149.       throw buildUsageError(
  150.         'E_INVALID_META',
  151.         'If `meta` is provided, it should be a dictionary (i.e. a plain JavaScript object).  '+
  152.         'But instead, got: ' + util.inspect(query.meta, {depth:5})+'',
  153.         query.using
  154.       );
  155.     }//-鈥
  156.  
  157.   }//>-鈥
  158.  
  159.  
  160.   //  鈹屸攢鈹愨敩 鈹攲鈹€鈹愨攲鈹€鈹愨敩鈹屸攢  鈺斺暒鈺椻晹鈺愨晽鈺斺暒鈺椻暒 鈺︹晹鈺愨晽鈺斺暒鈺
  161.   //  鈹  鈹溾攢鈹も敎鈹 鈹  鈹溾敶鈹  鈺戔晳鈺戔晳鈺  鈺 鈺犫晲鈺b晳 鈺 鈺戔晳
  162.   //  鈹斺攢鈹樷敶 鈹粹敂鈹€鈹樷敂鈹€鈹樷敶 鈹  鈺 鈺┾暁鈺愨暆 鈺 鈺 鈺┾暁鈺愨暆鈺愨暕鈺
  163.   //   鈹   鈹屸攢鈹愨敩 鈹攲鈹€鈹愨攲鈹€鈹愨敩鈹屸攢  鈹屸攢鈹愨攲鈹€鈹愨敩鈹€鈹  鈹屸攢鈹愨攢鈹 鈹攲鈹攼鈹攢鈹愨攲鈹€鈹愨攲鈹愨攲鈹屸攢鈹愨攲鈹€鈹愨敩 鈹攲鈹€鈹  鈹攲鈹€鈹屸攢鈹愨敩 鈹攲鈹€鈹
  164.   //  鈹屸敿鈹€  鈹  鈹溾攢鈹も敎鈹 鈹  鈹溾敶鈹  鈹溾敜 鈹 鈹傗敎鈹敇  鈹溾敜 鈹屸敶鈹敇 鈹 鈹溾敩鈹樷敎鈹€鈹も攤鈹傗攤鈹溾敜 鈹 鈹傗攤 鈹傗敂鈹€鈹  鈹溾敶鈹愨敎鈹 鈹斺敩鈹樷敂鈹€鈹
  165.   //  鈹斺敇   鈹斺攢鈹樷敶 鈹粹敂鈹€鈹樷敂鈹€鈹樷敶 鈹  鈹  鈹斺攢鈹樷敶鈹斺攢  鈹斺攢鈹樷敶 鈹斺攢 鈹 鈹粹敂鈹€鈹 鈹粹敇鈹斺敇鈹斺攢鈹樷敂鈹€鈹樷敂鈹€鈹樷敂鈹€鈹  鈹 鈹粹敂鈹€鈹 鈹 鈹斺攢鈹樷敇
  166.   //   鈹   鈹屸敩鈹愨攲鈹€鈹愨攲鈹攼鈹屸攢鈹愨敩鈹€鈹愨攲鈹攼鈹攲鈹愨攲鈹屸攢鈹  鈹屸攢鈹 鈹 鈹攲鈹€鈹愨敩鈹€鈹愨敩 鈹  鈹攲鈹€鈹屸攢鈹愨敩 鈹攲鈹€鈹
  167.   //  鈹屸敿鈹€   鈹傗攤鈹溾敜  鈹 鈹溾敜 鈹溾敩鈹樷攤鈹傗攤鈹傗攤鈹傗攤鈹溾敜   鈹傗攢鈹尖攼鈹 鈹傗敎鈹 鈹溾敩鈹樷敂鈹敇  鈹溾敶鈹愨敎鈹 鈹斺敩鈹樷敂鈹€鈹
  168.   //  鈹斺敇   鈹€鈹粹敇鈹斺攢鈹 鈹 鈹斺攢鈹樷敶鈹斺攢鈹 鈹粹敶鈹樷敂鈹樷敂鈹€鈹  鈹斺攢鈹樷敂鈹斺攢鈹樷敂鈹€鈹樷敶鈹斺攢 鈹   鈹 鈹粹敂鈹€鈹 鈹 鈹斺攢鈹
  169.   // Always check `method`.
  170.   if (!_.isString(query.method) || query.method === '') {
  171.     throw new Error(
  172.       'Consistency violation: Every stage 1 query should include a property called `method` as a non-empty string.'+
  173.       '  But instead, got: ' + util.inspect(query.method, {depth:5})
  174.     );
  175.   }//-鈥
  176.  
  177.  
  178.   // Now check a few different model settings, and set `meta` keys accordingly.
  179.   //
  180.   // > Remember, we rely on waterline-schema to have already validated
  181.   // > these model settings when the ORM was first initialized.
  182.  
  183.   //  鈹屸攢鈹愨攲鈹€鈹愨攲鈹€鈹愨攲鈹€鈹愨攲鈹€鈹愨攲鈹攼鈹屸攢鈹  鈹屸攢鈹愨攲鈹愨攲  鈹屸敩鈹愨攲鈹€鈹愨攲鈹€鈹愨攲鈹攼鈹攢鈹愨攲鈹€鈹愨敩 鈹攲鈹€鈹
  184.   //  鈹  鈹溾攢鈹も敂鈹€鈹愨攤  鈹溾攢鈹 鈹傗攤鈹溾敜   鈹 鈹傗攤鈹傗攤   鈹傗攤鈹溾敜 鈹斺攢鈹 鈹 鈹溾敩鈹樷攤 鈹傗敂鈹敇 鈹屸敇
  185.   //  鈹斺攢鈹樷敶 鈹粹敂鈹€鈹樷敂鈹€鈹樷敶 鈹粹攢鈹粹敇鈹斺攢鈹  鈹斺攢鈹樷敇鈹斺敇  鈹€鈹粹敇鈹斺攢鈹樷敂鈹€鈹 鈹 鈹粹敂鈹€鈹斺攢鈹 鈹  o
  186.   if (query.method === 'destroy' && !_.isUndefined(WLModel.cascadeOnDestroy)) {
  187.     assert(_.isBoolean(WLModel.cascadeOnDestroy), 'If specified, expecting `cascadeOnDestroy` model setting to be `true` or `false`.  But instead, got: '+util.inspect(WLModel.cascadeOnDestroy, {depth:5})+'');
  188.  
  189.     // Only bother setting the `cascade` meta key if the model setting is `true`.
  190.     // (because otherwise it's `false`, which is the default anyway)
  191.     if (WLModel.cascadeOnDestroy) {
  192.       query.meta = query.meta || {};
  193.       query.meta.cascade = WLModel.cascadeOnDestroy;
  194.     }
  195.  
  196.   }//>-
  197.  
  198.  
  199.   //  鈹屸攢鈹愨攲鈹€鈹愨攲鈹攼鈹屸攢鈹愨敩 鈹  鈹攢鈹愨攲鈹€鈹愨攲鈹€鈹愨攲鈹€鈹愨敩鈹€鈹愨攲鈹攼鈹屸攢鈹  鈹屸攢鈹愨攲鈹愨攲  鈹 鈹攲鈹€鈹愨攲鈹攼鈹屸攢鈹愨攲鈹攼鈹屸攢鈹愨攲鈹€鈹
  200.   //  鈹溾敜 鈹溾敜  鈹 鈹  鈹溾攢鈹  鈹溾敩鈹樷敎鈹 鈹  鈹 鈹傗敎鈹敇 鈹傗攤鈹斺攢鈹  鈹 鈹傗攤鈹傗攤  鈹 鈹傗敎鈹€鈹 鈹傗攤鈹溾攢鈹 鈹 鈹溾敜  鈹屸敇
  201.   //  鈹  鈹斺攢鈹 鈹 鈹斺攢鈹樷敶 鈹  鈹粹敂鈹€鈹斺攢鈹樷敂鈹€鈹樷敂鈹€鈹樷敶鈹斺攢鈹€鈹粹敇鈹斺攢鈹  鈹斺攢鈹樷敇鈹斺敇  鈹斺攢鈹樷敶  鈹€鈹粹敇鈹 鈹 鈹 鈹斺攢鈹 o
  202.   if (query.method === 'update' && !_.isUndefined(WLModel.fetchRecordsOnUpdate)) {
  203.     assert(_.isBoolean(WLModel.fetchRecordsOnUpdate), 'If specified, expecting `fetchRecordsOnUpdate` model setting to be `true` or `false`.  But instead, got: '+util.inspect(WLModel.fetchRecordsOnUpdate, {depth:5})+'');
  204.  
  205.     // Only bother setting the `fetch` meta key if the model setting is `true`.
  206.     // (because otherwise it's `false`, which is the default anyway)
  207.     if (WLModel.fetchRecordsOnUpdate) {
  208.       query.meta = query.meta || {};
  209.       query.meta.fetch = WLModel.fetchRecordsOnUpdate;
  210.     }
  211.  
  212.   }//>-
  213.  
  214.   //  鈹屸攢鈹愨攲鈹€鈹愨攲鈹攼鈹屸攢鈹愨敩 鈹  鈹攢鈹愨攲鈹€鈹愨攲鈹€鈹愨攲鈹€鈹愨敩鈹€鈹愨攲鈹攼鈹屸攢鈹  鈹屸攢鈹愨攲鈹愨攲  鈹屸敩鈹愨攲鈹€鈹愨攲鈹€鈹愨攲鈹攼鈹攢鈹愨攲鈹€鈹愨敩 鈹攲鈹€鈹
  215.   //  鈹溾敜 鈹溾敜  鈹 鈹  鈹溾攢鈹  鈹溾敩鈹樷敎鈹 鈹  鈹 鈹傗敎鈹敇 鈹傗攤鈹斺攢鈹  鈹 鈹傗攤鈹傗攤   鈹傗攤鈹溾敜 鈹斺攢鈹 鈹 鈹溾敩鈹樷攤 鈹傗敂鈹敇 鈹屸敇
  216.   //  鈹  鈹斺攢鈹 鈹 鈹斺攢鈹樷敶 鈹  鈹粹敂鈹€鈹斺攢鈹樷敂鈹€鈹樷敂鈹€鈹樷敶鈹斺攢鈹€鈹粹敇鈹斺攢鈹  鈹斺攢鈹樷敇鈹斺敇  鈹€鈹粹敇鈹斺攢鈹樷敂鈹€鈹 鈹 鈹粹敂鈹€鈹斺攢鈹 鈹  o
  217.   if (query.method === 'destroy' && !_.isUndefined(WLModel.fetchRecordsOnDestroy)) {
  218.     assert(_.isBoolean(WLModel.fetchRecordsOnDestroy), 'If specified, expecting `fetchRecordsOnDestroy` model setting to be `true` or `false`.  But instead, got: '+util.inspect(WLModel.fetchRecordsOnDestroy, {depth:5})+'');
  219.  
  220.     // Only bother setting the `fetch` meta key if the model setting is `true`.
  221.     // (because otherwise it's `false`, which is the default anyway)
  222.     if (WLModel.fetchRecordsOnDestroy) {
  223.       query.meta = query.meta || {};
  224.       query.meta.fetch = WLModel.fetchRecordsOnDestroy;
  225.     }
  226.  
  227.   }//>-
  228.  
  229.   //  鈹屸攢鈹愨攲鈹€鈹愨攲鈹攼鈹屸攢鈹愨敩 鈹  鈹攢鈹愨攲鈹€鈹愨攲鈹€鈹愨攲鈹€鈹愨敩鈹€鈹愨攲鈹攼鈹屸攢鈹  鈹屸攢鈹愨攲鈹愨攲  鈹屸攢鈹愨敩鈹€鈹愨攲鈹€鈹愨攲鈹€鈹愨攲鈹攼鈹屸攢鈹愨攲鈹€鈹
  230.   //  鈹溾敜 鈹溾敜  鈹 鈹  鈹溾攢鈹  鈹溾敩鈹樷敎鈹 鈹  鈹 鈹傗敎鈹敇 鈹傗攤鈹斺攢鈹  鈹 鈹傗攤鈹傗攤  鈹  鈹溾敩鈹樷敎鈹 鈹溾攢鈹 鈹 鈹溾敜  鈹屸敇
  231.   //  鈹  鈹斺攢鈹 鈹 鈹斺攢鈹樷敶 鈹  鈹粹敂鈹€鈹斺攢鈹樷敂鈹€鈹樷敂鈹€鈹樷敶鈹斺攢鈹€鈹粹敇鈹斺攢鈹  鈹斺攢鈹樷敇鈹斺敇  鈹斺攢鈹樷敶鈹斺攢鈹斺攢鈹樷敶 鈹 鈹 鈹斺攢鈹 o
  232.   if (query.method === 'create' && !_.isUndefined(WLModel.fetchRecordsOnCreate)) {
  233.     assert(_.isBoolean(WLModel.fetchRecordsOnCreate), 'If specified, expecting `fetchRecordsOnCreate` model setting to be `true` or `false`.  But instead, got: '+util.inspect(WLModel.fetchRecordsOnCreate, {depth:5})+'');
  234.  
  235.     // Only bother setting the `fetch` meta key if the model setting is `true`.
  236.     // (because otherwise it's `false`, which is the default anyway)
  237.     if (WLModel.fetchRecordsOnCreate) {
  238.       query.meta = query.meta || {};
  239.       query.meta.fetch = WLModel.fetchRecordsOnCreate;
  240.     }
  241.  
  242.   }//>-
  243.  
  244.   //  鈹屸攢鈹愨攲鈹€鈹愨攲鈹攼鈹屸攢鈹愨敩 鈹  鈹攢鈹愨攲鈹€鈹愨攲鈹€鈹愨攲鈹€鈹愨敩鈹€鈹愨攲鈹攼鈹屸攢鈹  鈹屸攢鈹愨攲鈹愨攲  鈹屸攢鈹愨敩鈹€鈹愨攲鈹€鈹愨攲鈹€鈹愨攲鈹攼鈹屸攢鈹  鈹屸攢鈹愨攲鈹€鈹愨攲鈹€鈹愨敩 鈹攲鈹€鈹
  245.   //  鈹溾敜 鈹溾敜  鈹 鈹  鈹溾攢鈹  鈹溾敩鈹樷敎鈹 鈹  鈹 鈹傗敎鈹敇 鈹傗攤鈹斺攢鈹  鈹 鈹傗攤鈹傗攤  鈹  鈹溾敩鈹樷敎鈹 鈹溾攢鈹 鈹 鈹溾敜   鈹溾敜 鈹溾攢鈹も攤  鈹溾攢鈹 鈹屸敇
  246.   //  鈹  鈹斺攢鈹 鈹 鈹斺攢鈹樷敶 鈹  鈹粹敂鈹€鈹斺攢鈹樷敂鈹€鈹樷敂鈹€鈹樷敶鈹斺攢鈹€鈹粹敇鈹斺攢鈹  鈹斺攢鈹樷敇鈹斺敇  鈹斺攢鈹樷敶鈹斺攢鈹斺攢鈹樷敶 鈹 鈹 鈹斺攢鈹  鈹斺攢鈹樷敶 鈹粹敂鈹€鈹樷敶 鈹 o
  247.   if (query.method === 'createEach' && !_.isUndefined(WLModel.fetchRecordsOnCreateEach)) {
  248.     assert(_.isBoolean(WLModel.fetchRecordsOnCreateEach), 'If specified, expecting `fetchRecordsOnCreateEach` model setting to be `true` or `false`.  But instead, got: '+util.inspect(WLModel.fetchRecordsOnCreateEach, {depth:5})+'');
  249.  
  250.     // Only bother setting the `fetch` meta key if the model setting is `true`.
  251.     // (because otherwise it's `false`, which is the default anyway)
  252.     if (WLModel.fetchRecordsOnCreateEach) {
  253.       query.meta = query.meta || {};
  254.       query.meta.fetch = WLModel.fetchRecordsOnCreateEach;
  255.     }
  256.  
  257.   }//>-
  258.  
  259.   // Determine the set of acceptable query keys for the specified `method`.
  260.   // (and, in the process, verify that we recognize this method in the first place)
  261.   var queryKeys = (function _getQueryKeys (){
  262.  
  263.     switch(query.method) {
  264.  
  265.       case 'find':                 return [ 'criteria', 'populates' ];
  266.       case 'findOne':              return [ 'criteria', 'populates' ];
  267.       case 'stream':               return [ 'criteria', 'populates', 'eachRecordFn', 'eachBatchFn' ];
  268.       case 'count':                return [ 'criteria' ];
  269.       case 'sum':                  return [ 'numericAttrName', 'criteria' ];
  270.       case 'avg':                  return [ 'numericAttrName', 'criteria' ];
  271.  
  272.       case 'create':               return [ 'newRecord' ];
  273.       case 'createEach':           return [ 'newRecords' ];
  274.       case 'findOrCreate':         return [ 'criteria', 'newRecord' ];
  275.  
  276.       case 'update':               return [ 'criteria', 'valuesToSet' ];
  277.       case 'destroy':              return [ 'criteria' ];
  278.       case 'addToCollection':      return [ 'targetRecordIds', 'collectionAttrName', 'associatedIds' ];
  279.       case 'removeFromCollection': return [ 'targetRecordIds', 'collectionAttrName', 'associatedIds' ];
  280.       case 'replaceCollection':    return [ 'targetRecordIds', 'collectionAttrName', 'associatedIds' ];
  281.  
  282.       default:
  283.         throw new Error('Consistency violation: Unrecognized `method` ("'+query.method+'")');
  284.  
  285.     }
  286.  
  287.   })();//</self-calling function :: _getQueryKeys()>
  288.  
  289.  
  290.   // > Note:
  291.   // >
  292.   // > It's OK if keys are missing at this point.  We'll do our best to
  293.   // > infer a reasonable default, when possible.  In some cases, it'll
  294.   // > still fail validation later, but in other cases, it'll pass.
  295.   // >
  296.   // > Anyway, that's all handled below.
  297.  
  298.  
  299.   // Now check that we see ONLY the expected keys for that method.
  300.   // (i.e. there should never be any miscellaneous stuff hanging out on the stage1 query dictionary)
  301.  
  302.   // We start off by building up an array of legal keys, starting with the universally-legal ones.
  303.   var allowedKeys = [
  304.     'meta',
  305.     'using',
  306.     'method'
  307.   ].concat(queryKeys);
  308.  
  309.  
  310.   // Then finally, we check that no extraneous keys are present.
  311.   var extraneousKeys = _.difference(_.keys(query), allowedKeys);
  312.   if (extraneousKeys.length > 0) {
  313.     throw new Error('Consistency violation: Provided "stage 1 query" contains extraneous top-level keys: '+extraneousKeys);
  314.   }
  315.  
  316.  
  317.  
  318.   //-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
  319.   // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  320.   //- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  321.   //-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
  322.   // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  323.   //- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  324.   //-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
  325.  
  326.  
  327.   //   鈻堚枅鈻堚枅鈻堚枅鈺椻枅鈻堚枅鈻堚枅鈻堚晽 鈻堚枅鈺椻枅鈻堚枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈻堚枅鈻堚枅鈺 鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚晽
  328.   //  鈻堚枅鈺斺晲鈺愨晲鈺愨暆鈻堚枅鈺斺晲鈺愨枅鈻堚晽鈻堚枅鈺戔暁鈺愨晲鈻堚枅鈺斺晲鈺愨暆鈻堚枅鈺斺晲鈺愨晲鈺愨暆鈻堚枅鈺斺晲鈺愨枅鈻堚晽鈻堚枅鈺戔枅鈻堚晹鈺愨晲鈻堚枅鈺
  329.   //  鈻堚枅鈺     鈻堚枅鈻堚枅鈻堚枅鈺斺暆鈻堚枅鈺   鈻堚枅鈺   鈻堚枅鈻堚枅鈻堚晽  鈻堚枅鈻堚枅鈻堚枅鈺斺暆鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚枅鈺
  330.   //  鈻堚枅鈺     鈻堚枅鈺斺晲鈺愨枅鈻堚晽鈻堚枅鈺   鈻堚枅鈺   鈻堚枅鈺斺晲鈺愨暆  鈻堚枅鈺斺晲鈺愨枅鈻堚晽鈻堚枅鈺戔枅鈻堚晹鈺愨晲鈻堚枅鈺
  331.   //  鈺氣枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈺  鈻堚枅鈺戔枅鈻堚晳   鈻堚枅鈺   鈻堚枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈺  鈻堚枅鈺戔枅鈻堚晳鈻堚枅鈺  鈻堚枅鈺
  332.   //   鈺氣晲鈺愨晲鈺愨晲鈺濃暁鈺愨暆  鈺氣晲鈺濃暁鈺愨暆   鈺氣晲鈺   鈺氣晲鈺愨晲鈺愨晲鈺愨暆鈺氣晲鈺  鈺氣晲鈺濃暁鈺愨暆鈺氣晲鈺  鈺氣晲鈺
  333.   //
  334.   if (_.contains(queryKeys, 'criteria')) {
  335.  
  336.     //  鈺斺暒鈺椻晹鈺愨晽鈺斺晲鈺椻晹鈺愨晽鈺 鈺︹暒 鈺斺暒鈺
  337.     //   鈺戔晳鈺戔暎 鈺犫暎 鈺犫晲鈺b晳 鈺戔晳  鈺
  338.     //  鈺愨暕鈺濃暁鈺愨暆鈺  鈺 鈺┾暁鈺愨暆鈺┾晲鈺濃暕
  339.     // Tolerate this being left undefined by inferring a reasonable default.
  340.     // (This will be further processed below.)
  341.     if (_.isUndefined(query.criteria)) {
  342.       query.criteria = {};
  343.     }//>-
  344.  
  345.  
  346.     //  鈺斺晲鈺椻晹鈺愨晽鈺斺晲鈺椻晹鈺愨晽鈺︹晹鈺愨晽鈺    鈺斺晲鈺椻晹鈺愨晽鈺斺晲鈺椻晹鈺愨晽鈺斺晲鈺
  347.     //  鈺氣晲鈺椻暊鈺愨暆鈺戔暎 鈺  鈺戔暊鈺愨暎鈺    鈺  鈺犫晲鈺b暁鈺愨晽鈺戔暎 鈺氣晲鈺
  348.     //  鈺氣晲鈺濃暕  鈺氣晲鈺濃暁鈺愨暆鈺┾暕 鈺┾暕鈺愨暆  鈺氣晲鈺濃暕 鈺┾暁鈺愨暆鈺氣晲鈺濃暁鈺愨暆
  349.     //  鈹屸攢    鈹 鈹屸攢鈹   鈹 鈹攲鈹愨攲鈹屸攢鈹愨敩 鈹攲鈹€鈹愨攲鈹€鈹愨攲鈹€鈹愨敩鈹€鈹愨攲鈹攼鈹屸攢鈹愨攲鈹攼  鈹屸攢鈹愨攲鈹€鈹愨攲鈹攼鈹屸攼 鈹攲鈹愨攲鈹屸攢鈹愨攲鈹攼鈹攲鈹€鈹愨攲鈹愨攲鈹屸攢鈹  鈹屸攢鈹愨攲鈹€鈹
  350.     //  鈹傗攢鈹€鈹€  鈹 鈹溾敜    鈹 鈹傗攤鈹傗攤鈹斺攢鈹愨攤 鈹傗敎鈹€鈹樷敎鈹€鈹樷攤 鈹傗敎鈹敇 鈹 鈹溾敜  鈹傗攤  鈹  鈹 鈹傗攤鈹傗攤鈹溾敶鈹愨攤鈹傗攤鈹傗敎鈹€鈹 鈹 鈹傗攤 鈹傗攤鈹傗攤鈹斺攢鈹  鈹 鈹傗敎鈹
  351.     //  鈹斺攢    鈹磑鈹斺攢鈹榦  鈹斺攢鈹樷敇鈹斺敇鈹斺攢鈹樷敂鈹€鈹樷敶  鈹  鈹斺攢鈹樷敶鈹斺攢 鈹 鈹斺攢鈹樷攢鈹粹敇  鈹斺攢鈹樷敂鈹€鈹樷敶 鈹粹敂鈹€鈹樷敶鈹樷敂鈹樷敶 鈹 鈹 鈹粹敂鈹€鈹樷敇鈹斺敇鈹斺攢鈹  鈹斺攢鈹樷敂
  352.     //        鈹屸攢鈹愨攲鈹€鈹愨敩鈹€鈹愨攲鈹攼鈹屸攢鈹愨敩鈹屸攼鈹  鈹屸攢鈹愨敩鈹€鈹愨敩鈹屸敩鈹愨攲鈹€鈹愨敩鈹€鈹愨敩鈹屸攢鈹  鈹屸攢鈹愨敩  鈹屸攢鈹愨敩 鈹攲鈹€鈹愨攲鈹€鈹愨攲鈹€鈹  鈹屸攢鈹愨攲鈹€鈹愨敩鈹€鈹
  353.     //        鈹  鈹溾敜 鈹溾敩鈹 鈹 鈹溾攢鈹も攤鈹傗攤鈹  鈹  鈹溾敩鈹樷攤 鈹 鈹溾敜 鈹溾敩鈹樷攤鈹溾攢鈹  鈹  鈹  鈹溾攢鈹も攤 鈹傗敂鈹€鈹愨敎鈹 鈹斺攢鈹  鈹溾敜 鈹 鈹傗敎鈹敇
  354.     //        鈹斺攢鈹樷敂鈹€鈹樷敶鈹斺攢 鈹 鈹 鈹粹敶鈹樷敂鈹  鈹斺攢鈹樷敶鈹斺攢鈹 鈹 鈹斺攢鈹樷敶鈹斺攢鈹粹敶 鈹  鈹斺攢鈹樷敶鈹€鈹樷敶 鈹粹敂鈹€鈹樷敂鈹€鈹樷敂鈹€鈹樷敂鈹€鈹  鈹  鈹斺攢鈹樷敶鈹斺攢
  355.     //        鈹屸攢鈹愨攲鈹€鈹愨攲鈹€鈹愨攲鈹€鈹愨敩鈹屸攢鈹愨敩鈹屸攢鈹  鈹屸敩鈹愨攲鈹€鈹愨攲鈹攼鈹屸攢鈹愨敩    鈹屸敩鈹愨攲鈹€鈹愨攲鈹攼鈹 鈹攲鈹€鈹愨攲鈹攼鈹屸攢鈹    鈹€鈹
  356.     //        鈹斺攢鈹愨敎鈹€鈹樷敎鈹 鈹  鈹傗敎鈹 鈹傗攤    鈹傗攤鈹傗攤 鈹 鈹傗攤鈹溾敜 鈹    鈹傗攤鈹傗敎鈹  鈹 鈹溾攢鈹も攤 鈹 鈹傗攤鈹斺攢鈹  鈹€鈹€鈹€鈹
  357.     //        鈹斺攢鈹樷敶  鈹斺攢鈹樷敂鈹€鈹樷敶鈹  鈹粹敂鈹€鈹  鈹 鈹粹敂鈹€鈹樷攢鈹粹敇鈹斺攢鈹樷敶鈹€鈹  鈹 鈹粹敂鈹€鈹 鈹 鈹 鈹粹敂鈹€鈹樷攢鈹粹敇鈹斺攢鈹    鈹€鈹
  358.     //
  359.     // Next, handle a few special cases that we are careful to fail loudly about.
  360.     //
  361.     // > Because if we don't, it can cause major confusion.  Think about it: in some cases,
  362.     // > certain usage can seem intuitive, and like a reasonable enough thing to try out...
  363.     // > ...but it might actually be unsupported.
  364.     // >
  365.     // > When you do try it out, unless it fails LOUDLY, then you could easily end
  366.     // > up believing that it is actually doing something.  And then, as is true when
  367.     // > working w/ any library or framework, you end up with all sorts of weird superstitions
  368.     // > and false assumptions that take a long time to wring out of your code base.
  369.     // > So let's do our best to prevent that.
  370.  
  371.     //
  372.     // > WARNING:
  373.     // > It is really important that we do this BEFORE we normalize the criteria!
  374.     // > (Because by then, it'll be too late to tell what was and wasn't included
  375.     // >  in the original, unnormalized criteria dictionary.)
  376.     //
  377.  
  378.     // If the criteria explicitly specifies `select` or `omit`, then make sure the query method
  379.     // is actually compatible with those clauses.
  380.     if (_.isObject(query.criteria) && (!_.isUndefined(query.criteria.select) || !_.isUndefined(query.criteria.omit))) {
  381.  
  382.       var PROJECTION_COMPATIBLE_METHODS = ['find', 'findOne', 'stream'];
  383.       var isCompatibleWithProjections = _.contains(PROJECTION_COMPATIBLE_METHODS, query.method);
  384.       if (!isCompatibleWithProjections) {
  385.         throw buildUsageError('E_INVALID_CRITERIA', 'Cannot use `select`/`omit` with this query method (`'+query.method+'`).', query.using);
  386.       }
  387.  
  388.     }//>-鈥
  389.  
  390.     // If the criteria explicitly specifies `limit`, then make sure the query method
  391.     // is actually compatible with that clause.
  392.     if (_.isObject(query.criteria) && !_.isUndefined(query.criteria.limit)) {
  393.  
  394.       var LIMIT_COMPATIBLE_METHODS = ['find', 'stream', 'sum', 'avg', 'update', 'destroy'];
  395.       var isCompatibleWithLimit = _.contains(LIMIT_COMPATIBLE_METHODS, query.method);
  396.       if (!isCompatibleWithLimit) {
  397.         throw buildUsageError('E_INVALID_CRITERIA', 'Cannot use `limit` with this query method (`'+query.method+'`).', query.using);
  398.       }
  399.  
  400.     }//>-鈥
  401.  
  402.  
  403.  
  404.  
  405.     //  鈺斺晽鈺斺晹鈺愨晽鈺︹晲鈺椻晹鈺︹晽鈺斺晲鈺椻暒  鈺︹晹鈺愨晽鈺斺晲鈺   鈹   鈺  鈺︹晹鈺愨晽鈺  鈺︹晹鈺︹晽鈺斺晲鈺椻晹鈺︹晽鈺斺晲鈺
  406.     //  鈺戔晳鈺戔晳 鈺戔暊鈺︹暆鈺戔晳鈺戔暊鈺愨暎鈺  鈺戔晹鈺愨暆鈺戔暎   鈹屸敿鈹€  鈺氣晽鈺斺暆鈺犫晲鈺b晳  鈺 鈺戔晳鈺犫晲鈺 鈺 鈺戔暎
  407.     //  鈺濃暁鈺濃暁鈺愨暆鈺┾暁鈺愨暕 鈺┾暕 鈺┾暕鈺愨暆鈺┾暁鈺愨暆鈺氣晲鈺  鈹斺敇    鈺氣暆 鈺 鈺┾暕鈺愨暆鈺┾晲鈺┾暆鈺 鈺 鈺 鈺氣晲鈺
  408.     // Validate and normalize the provided `criteria`.
  409.     try {
  410.       query.criteria = normalizeCriteria(query.criteria, query.using, orm);
  411.     } catch (e) {
  412.       switch (e.code) {
  413.  
  414.         case 'E_HIGHLY_IRREGULAR':
  415.           throw buildUsageError('E_INVALID_CRITERIA', e.message, query.using);
  416.  
  417.         case 'E_WOULD_RESULT_IN_NOTHING':
  418.           throw buildUsageError('E_NOOP', 'The provided criteria would not match any records.  '+e.message, query.using);
  419.  
  420.         // If no error code (or an unrecognized error code) was specified,
  421.         // then we assume that this was a spectacular failure do to some
  422.         // kind of unexpected, internal error on our part.
  423.         default:
  424.           throw new Error('Consistency violation: Encountered unexpected internal error when attempting to normalize/validate the provided criteria:\n```\n'+util.inspect(query.criteria, {depth:5})+'\n```\nAnd here is the actual error itself:\n```\n'+e.stack+'\n```');
  425.       }
  426.     }//>-鈥
  427.  
  428.  
  429.     //  鈹屸攢鈹愨敩  鈹 鈹攲鈹€鈹愨敩 鈹攲鈹€鈹  鈹屸攢鈹愨攲鈹€鈹愨敩鈹€鈹愨攲鈹€鈹愨攲鈹€鈹  鈺  鈺︹晹鈺︹晽鈺︹晹鈺︹晽  鈹屸敩鈹愨攲鈹€鈹  鈺斺暒鈺椻暒 鈺︹晹鈺愨晽
  430.     //  鈹溾攢鈹も攤  鈹傗攤鈹傗敎鈹€鈹も敂鈹敇鈹斺攢鈹  鈹溾敜 鈹 鈹傗敎鈹敇鈹  鈹溾敜   鈺  鈺戔晳鈺戔晳鈺 鈺    鈹 鈹 鈹   鈺 鈺戔晳鈺戔晳 鈺
  431.     //  鈹 鈹粹敶鈹€鈹樷敂鈹粹敇鈹 鈹 鈹 鈹斺攢鈹  鈹  鈹斺攢鈹樷敶鈹斺攢鈹斺攢鈹樷敂鈹€鈹  鈺┾晲鈺濃暕鈺 鈺┾暕 鈺    鈹 鈹斺攢鈹   鈺 鈺氣暕鈺濃暁鈺愨暆
  432.     //  鈹屸攢    鈹攲鈹€鈹  鈹屸敩鈹愨敩 鈹敩鈹屸攢鈹  鈹攲鈹€鈹  鈹屸攢鈹  鈺斺晲鈺椻暒鈺斺晽鈺斺晹鈺︹晽  鈺斺晲鈺椻晹鈺椻晹鈺斺晲鈺  鈹屸攢鈹 鈹 鈹攲鈹€鈹愨敩鈹€鈹愨敩 鈹    鈹€鈹
  433.     //  鈹傗攢鈹€鈹€  鈹傗敎鈹    鈹 鈹溾攢鈹も攤鈹斺攢鈹  鈹傗敂鈹€鈹  鈹溾攢鈹  鈺犫暎 鈺戔晳鈺戔晳 鈺戔晳  鈺 鈺戔晳鈺戔晳鈺戔暎   鈹傗攢鈹尖攼鈹 鈹傗敎鈹 鈹溾敩鈹樷敂鈹敇  鈹€鈹€鈹€鈹
  434.     //  鈹斺攢    鈹粹敂     鈹 鈹 鈹粹敶鈹斺攢鈹  鈹粹敂鈹€鈹  鈹 鈹  鈺  鈺┾暆鈺氣暆鈺愨暕鈺  鈺氣晲鈺濃暆鈺氣暆鈺氣晲鈺  鈹斺攢鈹樷敂鈹斺攢鈹樷敂鈹€鈹樷敶鈹斺攢 鈹     鈹€鈹
  435.     // Last but not least, if the current method is `findOne`, then set `limit: 2`.
  436.     //
  437.     // > This is a performance/stability check that prevents accidentally fetching the entire database
  438.     // > with queries like `.findOne({})`.  If > 1 record is found, the findOne will fail w/ an error
  439.     // > anyway, so it only makes sense to fetch _just enough_.
  440.     if (query.method === 'findOne') {
  441.  
  442.       query.criteria.limit = 2;
  443.  
  444.     }//>-
  445.  
  446.  
  447.   }// >-鈥
  448.  
  449.  
  450.  
  451.  
  452.   //  鈻堚枅鈻堚枅鈻堚枅鈺  鈻堚枅鈻堚枅鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚枅鈺 鈻堚枅鈺   鈻堚枅鈺椻枅鈻堚晽      鈻堚枅鈻堚枅鈻堚晽 鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈺椻枅鈻堚枅鈻堚枅鈻堚枅鈺椻枅鈻堚枅鈻堚枅鈻堚枅鈺
  453.   //  鈻堚枅鈺斺晲鈺愨枅鈻堚晽鈻堚枅鈺斺晲鈺愨晲鈻堚枅鈺椻枅鈻堚晹鈺愨晲鈻堚枅鈺椻枅鈻堚晳   鈻堚枅鈺戔枅鈻堚晳     鈻堚枅鈺斺晲鈺愨枅鈻堚晽鈺氣晲鈺愨枅鈻堚晹鈺愨晲鈺濃枅鈻堚晹鈺愨晲鈺愨晲鈺濃枅鈻堚晹鈺愨晲鈺愨晲鈺
  454.   //  鈻堚枅鈻堚枅鈻堚枅鈺斺暆鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚晹鈺濃枅鈻堚晳   鈻堚枅鈺戔枅鈻堚晳     鈻堚枅鈻堚枅鈻堚枅鈻堚晳   鈻堚枅鈺   鈻堚枅鈻堚枅鈻堚晽  鈻堚枅鈻堚枅鈻堚枅鈻堚晽
  455.   //  鈻堚枅鈺斺晲鈺愨晲鈺 鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚晹鈺愨晲鈺愨暆 鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚晳     鈻堚枅鈺斺晲鈺愨枅鈻堚晳   鈻堚枅鈺   鈻堚枅鈺斺晲鈺愨暆  鈺氣晲鈺愨晲鈺愨枅鈻堚晳
  456.   //  鈻堚枅鈺     鈺氣枅鈻堚枅鈻堚枅鈻堚晹鈺濃枅鈻堚晳     鈺氣枅鈻堚枅鈻堚枅鈻堚晹鈺濃枅鈻堚枅鈻堚枅鈻堚枅鈺椻枅鈻堚晳  鈻堚枅鈺   鈻堚枅鈺   鈻堚枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈻堚枅鈻堚枅鈻堚晳
  457.   //  鈺氣晲鈺      鈺氣晲鈺愨晲鈺愨晲鈺 鈺氣晲鈺      鈺氣晲鈺愨晲鈺愨晲鈺 鈺氣晲鈺愨晲鈺愨晲鈺愨暆鈺氣晲鈺  鈺氣晲鈺   鈺氣晲鈺   鈺氣晲鈺愨晲鈺愨晲鈺愨暆鈺氣晲鈺愨晲鈺愨晲鈺愨暆
  458.   //
  459.   // Validate/normalize the `populates` query key.
  460.   //
  461.   // > NOTE: At this point, we know that the `criteria` query key has already been checked/normalized.
  462.   if (_.contains(queryKeys, 'populates')) {
  463.  
  464.     // Tolerate this being left undefined by inferring a reasonable default.
  465.     if (_.isUndefined(query.populates)) {
  466.       query.populates = {};
  467.     }//>-
  468.  
  469.     // Verify that `populates` is a dictionary.
  470.     if (!_.isObject(query.populates) || _.isArray(query.populates) || _.isFunction(query.populates)) {
  471.       throw buildUsageError(
  472.         'E_INVALID_POPULATES',
  473.         '`populates` must be a dictionary.  But instead, got: '+util.inspect(query.populates, {depth: 1}),
  474.         query.using
  475.       );
  476.     }//-鈥
  477.  
  478.  
  479.     // For each key in our `populates` dictionary...
  480.     _.each(_.keys(query.populates), function (populateAttrName) {
  481.  
  482.       // For convenience/consistency, if the RHS of this "populate" directive was set
  483.       // to `false`/`undefined`, understand it to mean the same thing as if this particular
  484.       // populate directive wasn't included in the first place.  In other words, strip
  485.       // this key from the `populates` dictionary and just return early.
  486.       if (query.populates[populateAttrName] === false || _.isUndefined(query.populates[populateAttrName])) {
  487.         delete query.populates[populateAttrName];
  488.         return;
  489.       }//-鈥
  490.  
  491.       // If trying to populate an association that is ALSO being omitted (in the primary criteria),
  492.       // then we say this is invalid.
  493.       //
  494.       // > We know that the primary criteria has been normalized already at this point.
  495.       // > Note: You can NEVER `select` or `omit` plural associations anyway, but that's
  496.       // > already been dealt with above from when we normalized the criteria.
  497.       if (_.contains(query.criteria.omit, populateAttrName)) {
  498.         throw buildUsageError(
  499.           'E_INVALID_POPULATES',
  500.           'Could not populate `'+populateAttrName+'`.  '+
  501.           'This query also indicates that this attribute should be omitted.  '+
  502.           'Cannot populate AND omit an association at the same time!',
  503.           query.using
  504.         );
  505.       }//-鈥
  506.  
  507.       // If trying to populate an association that was included in an explicit `select` clause
  508.       // in the primary criteria, then gracefully modify that select clause so that it is NOT included.
  509.       // (An explicit `select` clause is only used for singular associations that AREN'T populated.)
  510.       //
  511.       // > We know that the primary criteria has been normalized already at this point.
  512.       if (query.criteria.select[0] !== '*' && _.contains(query.criteria.select, populateAttrName)) {
  513.         _.remove(query.criteria.select, populateAttrName);
  514.       }//>-
  515.  
  516.  
  517.       // If trying to populate an association that was ALSO included in an explicit
  518.       // `sort` clause in the primary criteria, then don't allow this to be populated.
  519.       //
  520.       // > We know that the primary criteria has been normalized already at this point.
  521.       var isMentionedInPrimarySort = _.any(query.criteria.sort, function (comparatorDirective){
  522.         var sortBy = _.keys(comparatorDirective)[0];
  523.         return (sortBy === populateAttrName);
  524.       });
  525.       if (isMentionedInPrimarySort) {
  526.         throw buildUsageError(
  527.           'E_INVALID_POPULATES',
  528.           'Could not populate `'+populateAttrName+'`.  '+
  529.           'Cannot populate AND sort by an association at the same time!',
  530.           query.using
  531.         );
  532.       }//>-
  533.  
  534.  
  535.       // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  536.       // FUTURE: Prevent (probably) trying to populate a association that was ALSO referenced somewhere
  537.       // from within the `where` clause in the primary criteria.
  538.       // > If you have a use case for why you want to be able to do this, please open an issue in the
  539.       // > main Sails repo and at-mention @mikermcneil, @particlebanana, or another core team member.
  540.       // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  541.       // > Note that we already throw out any attempts to filter based on a plural ("collection")
  542.       // > association, whether it's populated or not-- but that's taken care of separately in
  543.       // > normalizeCriteria().
  544.       // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  545.  
  546.  
  547.       //  鈹  鈹屸攢鈹愨攲鈹€鈹愨敩鈹屸攢  鈹 鈹攲鈹€鈹  鈺斺晲鈺椻晹鈺︹晽鈺斺暒鈺椻暒鈺愨晽  鈺斺暒鈺椻晹鈺愨晽鈺斺晲鈺  鈹屸攢鈹愨攲鈹€鈹愨敩鈹€鈹  鈹屸攢鈹愨攲鈹€鈹愨攲鈹€鈹愨攲鈹€鈹愨攲鈹€鈹愨敩鈹屸攢鈹愨攲鈹攼鈹攲鈹€鈹愨攲鈹愨攲
  548.       //  鈹  鈹 鈹傗攤 鈹傗敎鈹粹攼  鈹 鈹傗敎鈹€鈹  鈺犫晲鈺 鈺  鈺 鈺犫暒鈺   鈺戔晳鈺戔暎 鈺犫暎   鈹溾敜 鈹 鈹傗敎鈹敇  鈹溾攢鈹も敂鈹€鈹愨敂鈹€鈹愨攤 鈹傗攤  鈹傗敎鈹€鈹 鈹 鈹傗攤 鈹傗攤鈹傗攤
  549.       //  鈹粹攢鈹樷敂鈹€鈹樷敂鈹€鈹樷敶 鈹  鈹斺攢鈹樷敶    鈺 鈺 鈺  鈺 鈺┾暁鈺  鈺愨暕鈺濃暁鈺愨暆鈺    鈹  鈹斺攢鈹樷敶鈹斺攢  鈹 鈹粹敂鈹€鈹樷敂鈹€鈹樷敂鈹€鈹樷敂鈹€鈹樷敶鈹 鈹 鈹 鈹粹敂鈹€鈹樷敇鈹斺敇
  550.       // Look up the attribute definition for the association being populated.
  551.       // (at the same time, validating that an association by this name actually exists in this model definition.)
  552.       var populateAttrDef;
  553.       try {
  554.         populateAttrDef = getAttribute(populateAttrName, query.using, orm);
  555.       } catch (e) {
  556.         switch (e.code) {
  557.           case 'E_ATTR_NOT_REGISTERED':
  558.             throw buildUsageError(
  559.               'E_INVALID_POPULATES',
  560.               'Could not populate `'+populateAttrName+'`.  '+
  561.               'There is no attribute named `'+populateAttrName+'` defined in this model.',
  562.               query.using
  563.             );
  564.           default: throw new Error('Consistency violation: When attempting to populate `'+populateAttrName+'` for this model (`'+query.using+'`), an unexpected error occurred looking up the association\'s definition.  This SHOULD never happen.  Here is the original error:\n```\n'+e.stack+'\n```');
  565.         }
  566.       }//</catch>
  567.  
  568.  
  569.       //  鈹  鈹屸攢鈹愨攲鈹€鈹愨敩鈹屸攢  鈹 鈹攲鈹€鈹  鈹攲鈹愨攲鈹屸攢鈹愨攲鈹€鈹  鈹屸攢鈹愨攲鈹愨攲  鈹屸敩鈹愨敩 鈹攲鈹€鈹  鈺斺晲鈺椻晹鈺︹晽鈺 鈺︹晹鈺愨晽鈺︹晲鈺  鈺斺暒鈺椻晹鈺愨晽鈺斺暒鈺椻晹鈺愨晽鈺
  570.       //  鈹  鈹 鈹傗攤 鈹傗敎鈹粹攼  鈹 鈹傗敎鈹€鈹  鈹傗攤鈹傗攤鈹溾敜 鈹 鈹  鈹 鈹傗攤鈹傗攤   鈹 鈹溾攢鈹も敎鈹   鈺 鈺 鈺 鈺犫晲鈺b晳鈺 鈺犫暒鈺  鈺戔晳鈺戔晳 鈺 鈺戔晳鈺戔暎 鈺
  571.       //  鈹粹攢鈹樷敂鈹€鈹樷敂鈹€鈹樷敶 鈹  鈹斺攢鈹樷敶    鈹粹敇鈹斺敇鈹  鈹斺攢鈹  鈹斺攢鈹樷敇鈹斺敇   鈹 鈹 鈹粹敂鈹€鈹  鈺氣晲鈺 鈺 鈺 鈺┾暁鈺愨暆鈺┾暁鈺  鈺 鈺┾暁鈺愨暆鈺愨暕鈺濃暁鈺愨暆鈺┾晲鈺
  572.       // Determine the identity of the other (associated) model, then use that to make
  573.       // sure that the other model's definition is actually registered in our `orm`.
  574.       var otherModelIdentity;
  575.       if (populateAttrDef.model) {
  576.         otherModelIdentity = populateAttrDef.model;
  577.       }//鈥
  578.       else if (populateAttrDef.collection) {
  579.         otherModelIdentity = populateAttrDef.collection;
  580.       }//鈥
  581.       // Otherwise, this query is invalid, since the attribute with this name is
  582.       // neither a "collection" nor a "model" association.
  583.       else {
  584.         throw buildUsageError(
  585.           'E_INVALID_POPULATES',
  586.           'Could not populate `'+populateAttrName+'`.  '+
  587.           'The attribute named `'+populateAttrName+'` defined in this model (`'+query.using+'`)'+
  588.           'is not defined as a "collection" or "model" association, and thus cannot '+
  589.           'be populated.  Instead, its definition looks like this:\n'+
  590.           util.inspect(populateAttrDef, {depth: 1}),
  591.           query.using
  592.         );
  593.       }//>-鈥
  594.  
  595.  
  596.  
  597.       //  鈹屸攢鈹愨敩 鈹攲鈹€鈹愨攲鈹€鈹愨敩鈹屸攢  鈹屸敩鈹愨敩 鈹攲鈹€鈹  鈺︹晲鈺椻暒 鈺︹晹鈺愨晽
  598.       //  鈹  鈹溾攢鈹も敎鈹 鈹  鈹溾敶鈹   鈹 鈹溾攢鈹も敎鈹   鈺犫暒鈺濃暊鈺愨暎鈺氣晲鈺
  599.       //  鈹斺攢鈹樷敶 鈹粹敂鈹€鈹樷敂鈹€鈹樷敶 鈹   鈹 鈹 鈹粹敂鈹€鈹  鈺┾暁鈺愨暕 鈺┾暁鈺愨暆
  600.  
  601.       // If this is a singular ("model") association, then it should always have
  602.       // an empty dictionary on the RHS.  (For this type of association, there is
  603.       // always either exactly one associated record, or none of them.)
  604.       if (populateAttrDef.model) {
  605.  
  606.         // Tolerate a subcriteria of `{}`, interpreting it to mean that there is
  607.         // really no criteria at all, and that we should just use `true` (the
  608.         // default "enabled" value for singular "model" associations.)
  609.         if (_.isEqual(query.populates[populateAttrName], {})) {
  610.           query.populates[populateAttrName] = true;
  611.         }
  612.         // Otherwise, this simply must be `true`.  Otherwise it's invalid.
  613.         else {
  614.  
  615.           if (query.populates[populateAttrName] !== true) {
  616.             throw buildUsageError(
  617.               'E_INVALID_POPULATES',
  618.               'Could not populate `'+populateAttrName+'` because of ambiguous usage.  '+
  619.               'This is a singular ("model") association, which means it never refers to '+
  620.               'more than _one_ associated record.  So passing in subcriteria (i.e. as '+
  621.               'the second argument to `.populate()`) is not supported for this association, '+
  622.               'since it wouldn\'t make any sense.  But that\'s the trouble-- it looks like '+
  623.               'some sort of a subcriteria (or something) _was_ provided!\n'+
  624.               '\n'+
  625.               'Here\'s what was passed in:\n'+
  626.               util.inspect(query.populates[populateAttrName], {depth: 5}),
  627.               query.using
  628.             );
  629.           }//-鈥
  630.  
  631.         }//>-鈥
  632.  
  633.       }
  634.       // Otherwise, this is a plural ("collection") association, so we'll need to
  635.       // validate and fully-normalize the provided subcriteria.
  636.       else {
  637.  
  638.         // For compatibility, interpet a subcriteria of `true` to mean that there
  639.         // is really no subcriteria at all, and that we should just use the default (`{}`).
  640.         // > This will be further expanded into a fully-formed criteria dictionary shortly.
  641.         if (query.populates[populateAttrName] === true) {
  642.           query.populates[populateAttrName] = {};
  643.         }//>-
  644.  
  645.         // Track whether `sort` was omitted from the subcriteria.
  646.         // (this is used just a little ways down below.)
  647.         //
  648.         // > Be sure to see "FUTURE (1)" for details about how we might improve this in
  649.         // > the future-- it's not a 100% accurate or clean check right now!!
  650.         var wasSubcriteriaSortOmitted = (
  651.           !_.isObject(query.populates[populateAttrName]) ||
  652.           _.isUndefined(query.populates[populateAttrName].sort) ||
  653.           _.isEqual(query.populates[populateAttrName].sort, [])
  654.         );
  655.  
  656.         // Validate and normalize the provided criteria.
  657.         try {
  658.           query.populates[populateAttrName] = normalizeCriteria(query.populates[populateAttrName], otherModelIdentity, orm);
  659.         } catch (e) {
  660.           switch (e.code) {
  661.  
  662.             case 'E_HIGHLY_IRREGULAR':
  663.               throw buildUsageError(
  664.                 'E_INVALID_POPULATES',
  665.                 'Could not use the specified subcriteria for populating `'+populateAttrName+'`: '+e.message,
  666.                 // (Tip: Instead of that ^^^, when debugging Waterline itself, replace `e.message` with `e.stack`)
  667.                 query.using
  668.               );
  669.  
  670.             case 'E_WOULD_RESULT_IN_NOTHING':
  671.               // If the criteria indicates this populate would result in nothing, then set it to
  672.               // `false` - a special value indicating that it is a no-op.
  673.               // > 鈥 In Waterline's operation builder, whenever we see a subcriteria of `false`,
  674.               // >   we simply skip the populate (i.e. don't factor it in to our stage 3 queries)
  675.               // > 鈥 And in the transformer, whenever we're putting back together a result set,
  676.               // >   and we see a subcriteria of `false` from the original stage 2 query, then
  677.               // >   we ensure that the virtual attributes comes back set to `[]` in the resulting
  678.               // >   record.
  679.               query.populates[populateAttrName] = false;
  680.  
  681.               // And then return early from this loop to skip further checks,
  682.               // which won't be relevant.
  683.               return;
  684.  
  685.             // If no error code (or an unrecognized error code) was specified,
  686.             // then we assume that this was a spectacular failure do to some
  687.             // kind of unexpected, internal error on our part.
  688.             default:
  689.               throw new Error('Consistency violation: Encountered unexpected internal error when attempting to normalize/validate the provided criteria for populating `'+populateAttrName+'`:\n```\n'+util.inspect(query.populates[populateAttrName], {depth:5})+'\n```\nThe following error occurred:\n```\n'+e.stack+'\n```');
  690.           }
  691.         }//>-鈥
  692.  
  693.  
  694.         //  鈹屸攢鈹愨敩鈹€鈹愨攲鈹€鈹愨攲鈹攼鈹 鈹攲鈹€鈹愨攲鈹攼鈹攲鈹€鈹愨攲鈹愨攲  鈹屸攢鈹愨敩 鈹攲鈹€鈹愨攲鈹€鈹愨敩鈹屸攢
  695.         //  鈹溾攢鈹樷敎鈹敇鈹 鈹 鈹傗攤鈹 鈹傗攤   鈹 鈹傗攤 鈹傗攤鈹傗攤  鈹  鈹溾攢鈹も敎鈹 鈹  鈹溾敶鈹
  696.         //  鈹  鈹粹敂鈹€鈹斺攢鈹樷攢鈹粹敇鈹斺攢鈹樷敂鈹€鈹 鈹 鈹粹敂鈹€鈹樷敇鈹斺敇  鈹斺攢鈹樷敶 鈹粹敂鈹€鈹樷敂鈹€鈹樷敶 鈹
  697.         //  鈹屸攢鈹愨攲鈹€鈹愨敩鈹€鈹  鈺斺晽鈺斺晹鈺愨晽鈺斺晽鈺   鈺斺晲鈺椻晹鈺愨晽鈺斺暒鈺椻暒鈺斺暒鈺椻暒鈺斺晲鈺椻晹鈺愨晽鈺斺暒鈺  鈹屸攢鈹愨攲鈹€鈹愨攲鈹€鈹愨敩 鈹敩  鈹屸攢鈹愨攲鈹攼鈹屸攢鈹愨攲鈹€鈹
  698.         //  鈹溾敜 鈹 鈹傗敎鈹敇  鈺戔晳鈺戔晳 鈺戔晳鈺戔晳鈹€鈹€鈹€鈺 鈺戔暊鈺愨暆 鈺 鈺戔晳鈺戔晳鈺戔晹鈺愨暆鈺戔暎  鈺戔晳  鈹溾攢鈹樷攤 鈹傗敎鈹€鈹樷攤 鈹傗攤  鈹溾攢鈹 鈹 鈹溾敜 鈹斺攢鈹
  699.         //  鈹  鈹斺攢鈹樷敶鈹斺攢  鈺濃暁鈺濃暁鈺愨暆鈺濃暁鈺   鈺氣晲鈺濃暕   鈺 鈺┾暕 鈺┾暕鈺氣晲鈺濃暁鈺愨暆鈺愨暕鈺  鈹  鈹斺攢鈹樷敶  鈹斺攢鈹樷敶鈹€鈹樷敶 鈹 鈹 鈹斺攢鈹樷敂鈹€鈹
  700.         //  鈹屸敩鈹愨敩 鈹攲鈹€鈹愨攲鈹攼  鈺斺晲鈺椻暒  鈺斺晲鈺椻晹鈺愨晽  鈺 鈺︹晹鈺愨晽鈺斺晲鈺  鈺斺晲鈺椻暒 鈺︹晹鈺 鈺斺晲鈺椻暒鈺愨晽鈺︹晹鈺︹晽鈺斺晲鈺椻暒鈺愨晽鈺︹晹鈺愨晽
  701.         //   鈹 鈹溾攢鈹も敎鈹€鈹 鈹   鈺犫晲鈺b晳  鈺氣晲鈺椻晳 鈺  鈺 鈺戔暁鈺愨晽鈺戔暎   鈺氣晲鈺椻晳 鈺戔暊鈺┾晽鈺  鈺犫暒鈺濃晳 鈺 鈺戔暎 鈺犫暒鈺濃晳鈺犫晲鈺
  702.         //   鈹 鈹 鈹粹敶 鈹 鈹   鈺 鈺┾暕鈺愨暆鈺氣晲鈺濃暁鈺愨暆  鈺氣晲鈺濃暁鈺愨暆鈺氣晲鈺  鈺氣晲鈺濃暁鈺愨暆鈺氣晲鈺濃暁鈺愨暆鈺┾暁鈺愨暕 鈺 鈺氣晲鈺濃暕鈺氣晲鈺┾暕 鈺
  703.         // In production, do an additional check:
  704.         if (process.env.NODE_ENV === 'production') {
  705.  
  706.           // Determine if we are populating an association that does not support a fully-optimized populate.
  707.           var isAssociationFullyCapable = isCapableOfOptimizedPopulate(populateAttrName, query.using, orm);
  708.  
  709.           // If so, then make sure we are not attempting to perform a "dangerous" populate--
  710.           // that is, one that is not currently safe using our built-in joining shim.
  711.           // (This is related to memory usage, and is a result of the shim's implementation.)
  712.           if (!isAssociationFullyCapable) {
  713.  
  714.             var subcriteria = query.populates[populateAttrName];
  715.             var isPotentiallyDangerous = (
  716.               subcriteria.skip !== 0 ||
  717.               subcriteria.limit !== (Number.MAX_SAFE_INTEGER||9007199254740991) ||
  718.               !wasSubcriteriaSortOmitted
  719.             );
  720.             // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  721.             // > FUTURE (1): instead of the overly-simplistic "wasSubcriteriaSortOmitted" check, compare vs
  722.             // > the default. Currently, if you explicitly provide the default `sort`, you'll see this
  723.             // > warning (even though using the default `sort` represents exactly the same subcriteria as if
  724.             // > you'd omitted it entirely).
  725.             // >
  726.             // > e.g.
  727.             // > ```
  728.             //   var isPotentiallyDangerous = (
  729.             //     subcriteria.skip !== 0 ||
  730.             //     subcriteria.limit !== (Number.MAX_SAFE_INTEGER||9007199254740991) ||
  731.             //     !_.isEqual(subcriteria.sort, defaultSort)
  732.             //                                   //^^^ the hard part-- see normalizeSortClause() for why
  733.             //   );
  734.             // > ```
  735.             // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  736.             // > FUTURE (2): make this check more restrictive-- not EVERYTHING it prevents is actually
  737.             // > dangerous given the current implementation of the shim.  But in the mean time,
  738.             // > better to err on the safe side.
  739.             // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  740.             // > FUTURE (3): overcome this by implementing a more complicated batching strategy-- however,
  741.             // > this is not a priority right now, since this is only an issue for xD/A associations,
  742.             // > which will likely never come up for the majority of applications.  Our focus is on the
  743.             // > much more common real-world scenario of populating across associations in the same database.
  744.             // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  745.  
  746.             if (isPotentiallyDangerous) {
  747.               console.warn('\n'+
  748.                 'Warning: Attempting to populate `'+populateAttrName+'` with the specified subcriteria,\n'+
  749.                 'but this MAY NOT BE SAFE, depending on the number of records stored in your models.\n'+
  750.                 'Since this association does not support optimized populates (i.e. it spans multiple '+'\n'+
  751.                 'datastores, or uses an adapter that does not support native joins), it is not a good '+'\n'+
  752.                 'idea to populate it along with a subcriteria that uses `limit`, `skip`, and/or `sort`-- '+'\n'+
  753.                 'at least not in a production environment.\n'+
  754.                 '\n'+
  755.                 'This is because, to satisfy the specified `limit`/`skip`/`sort`, many additional records\n'+
  756.                 'may need to be fetched along the way -- perhaps enough of them to overflow RAM on your server.\n'+
  757.                 '\n'+
  758.                 'If you are just using sails-disk during development, or are certain this is not a problem\n'+
  759.                 'based on your application\'s requirements, then you can safely ignore this message.\n'+
  760.                 'But otherwise, to overcome this, either (A) remove or change this subcriteria, or\n'+
  761.                 '(B) configure all involved models to use the same datastore, and/or switch to an adapter\n'+
  762.                 'like sails-mysql or sails-postgresql that supports native joins.\n'
  763.               );
  764.             }//>-   </ if populating would be potentially- dangerous as far as process memory consumption >
  765.  
  766.           }//>-鈥  </ if association is NOT fully capable of being populated in a fully-optimized way >
  767.  
  768.         }//>-鈥  </ if production >
  769.  
  770.  
  771.       }//</else :: this is a plural ("collection") association>
  772.  
  773.  
  774.     });//</_.each() key in the `populates` dictionary>
  775.  
  776.   }//>-鈥
  777.  
  778.  
  779.  
  780.  
  781.  
  782.  
  783.  
  784.  
  785.  
  786.   //  鈻堚枅鈻堚晽   鈻堚枅鈺椻枅鈻堚晽   鈻堚枅鈺椻枅鈻堚枅鈺   鈻堚枅鈻堚晽鈻堚枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈻堚枅鈻堚枅鈺 鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚枅鈺
  787.   //  鈻堚枅鈻堚枅鈺  鈻堚枅鈺戔枅鈻堚晳   鈻堚枅鈺戔枅鈻堚枅鈻堚晽 鈻堚枅鈻堚枅鈺戔枅鈻堚晹鈺愨晲鈺愨晲鈺濃枅鈻堚晹鈺愨晲鈻堚枅鈺椻枅鈻堚晳鈻堚枅鈺斺晲鈺愨晲鈺愨暆
  788.   //  鈻堚枅鈺斺枅鈻堚晽 鈻堚枅鈺戔枅鈻堚晳   鈻堚枅鈺戔枅鈻堚晹鈻堚枅鈻堚枅鈺斺枅鈻堚晳鈻堚枅鈻堚枅鈻堚晽  鈻堚枅鈻堚枅鈻堚枅鈺斺暆鈻堚枅鈺戔枅鈻堚晳
  789.   //  鈻堚枅鈺戔暁鈻堚枅鈺椻枅鈻堚晳鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚晳鈺氣枅鈻堚晹鈺濃枅鈻堚晳鈻堚枅鈺斺晲鈺愨暆  鈻堚枅鈺斺晲鈺愨枅鈻堚晽鈻堚枅鈺戔枅鈻堚晳
  790.   //  鈻堚枅鈺 鈺氣枅鈻堚枅鈻堚晳鈺氣枅鈻堚枅鈻堚枅鈻堚晹鈺濃枅鈻堚晳 鈺氣晲鈺 鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚枅鈺椻枅鈻堚晳  鈻堚枅鈺戔枅鈻堚晳鈺氣枅鈻堚枅鈻堚枅鈻堚晽
  791.   //  鈺氣晲鈺  鈺氣晲鈺愨晲鈺 鈺氣晲鈺愨晲鈺愨晲鈺 鈺氣晲鈺     鈺氣晲鈺濃暁鈺愨晲鈺愨晲鈺愨晲鈺濃暁鈺愨暆  鈺氣晲鈺濃暁鈺愨暆 鈺氣晲鈺愨晲鈺愨晲鈺
  792.   //
  793.   //   鈻堚枅鈻堚枅鈻堚晽 鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈺椻枅鈻堚枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈻堚枅鈻堚枅鈺     鈻堚枅鈻堚晽   鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚晽 鈻堚枅鈻堚晽   鈻堚枅鈻堚晽鈻堚枅鈻堚枅鈻堚枅鈻堚晽
  794.   //  鈻堚枅鈺斺晲鈺愨枅鈻堚晽鈺氣晲鈺愨枅鈻堚晹鈺愨晲鈺濃暁鈺愨晲鈻堚枅鈺斺晲鈺愨暆鈻堚枅鈺斺晲鈺愨枅鈻堚晽    鈻堚枅鈻堚枅鈺  鈻堚枅鈺戔枅鈻堚晹鈺愨晲鈻堚枅鈺椻枅鈻堚枅鈻堚晽 鈻堚枅鈻堚枅鈺戔枅鈻堚晹鈺愨晲鈺愨晲鈺
  795.   //  鈻堚枅鈻堚枅鈻堚枅鈻堚晳   鈻堚枅鈺      鈻堚枅鈺   鈻堚枅鈻堚枅鈻堚枅鈺斺暆    鈻堚枅鈺斺枅鈻堚晽 鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚枅鈺戔枅鈻堚晹鈻堚枅鈻堚枅鈺斺枅鈻堚晳鈻堚枅鈻堚枅鈻堚晽
  796.   //  鈻堚枅鈺斺晲鈺愨枅鈻堚晳   鈻堚枅鈺      鈻堚枅鈺   鈻堚枅鈺斺晲鈺愨枅鈻堚晽    鈻堚枅鈺戔暁鈻堚枅鈺椻枅鈻堚晳鈻堚枅鈺斺晲鈺愨枅鈻堚晳鈻堚枅鈺戔暁鈻堚枅鈺斺暆鈻堚枅鈺戔枅鈻堚晹鈺愨晲鈺
  797.   //  鈻堚枅鈺  鈻堚枅鈺   鈻堚枅鈺      鈻堚枅鈺   鈻堚枅鈺  鈻堚枅鈺    鈻堚枅鈺 鈺氣枅鈻堚枅鈻堚晳鈻堚枅鈺  鈻堚枅鈺戔枅鈻堚晳 鈺氣晲鈺 鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚枅鈺
  798.   //  鈺氣晲鈺  鈺氣晲鈺   鈺氣晲鈺      鈺氣晲鈺   鈺氣晲鈺  鈺氣晲鈺    鈺氣晲鈺  鈺氣晲鈺愨晲鈺濃暁鈺愨暆  鈺氣晲鈺濃暁鈺愨暆     鈺氣晲鈺濃暁鈺愨晲鈺愨晲鈺愨晲鈺
  799.   //
  800.   if (_.contains(queryKeys, 'numericAttrName')) {
  801.  
  802.     if (_.isUndefined(query.numericAttrName)) {
  803.       throw buildUsageError(
  804.         'E_INVALID_NUMERIC_ATTR_NAME',
  805.         'Please specify `numericAttrName` (required for this variety of query).',
  806.         query.using
  807.       );
  808.     }
  809.  
  810.     if (!_.isString(query.numericAttrName)) {
  811.       throw buildUsageError(
  812.         'E_INVALID_NUMERIC_ATTR_NAME',
  813.         'Instead of a string, got: '+util.inspect(query.numericAttrName,{depth:5}),
  814.         query.using
  815.       );
  816.     }
  817.  
  818.     // Validate that an attribute by this name actually exists in this model definition.
  819.     var numericAttrDef;
  820.     try {
  821.       numericAttrDef = getAttribute(query.numericAttrName, query.using, orm);
  822.     } catch (e) {
  823.       switch (e.code) {
  824.         case 'E_ATTR_NOT_REGISTERED':
  825.           throw buildUsageError(
  826.             'E_INVALID_NUMERIC_ATTR_NAME',
  827.             'There is no attribute named `'+query.numericAttrName+'` defined in this model.',
  828.             query.using
  829.           );
  830.         default: throw e;
  831.       }
  832.     }//</catch>
  833.  
  834.  
  835.     // If this attempts to use a singular (`model`) association that happens to also
  836.     // correspond with an associated model that has a `type: 'number'` primary key, then
  837.     // STILL THROW -- but just use a more explicit error message explaining the reason this
  838.     // is not allowed (i.e. because it doesn't make any sense to get the sum or average of
  839.     // a bunch of ids... and more often than not, this scenario happens due to mistakes in
  840.     // userland code.  We have yet to see a use case where this is necessary.)
  841.     var isSingularAssociationToModelWithNumericPk = numericAttrDef.model && (getAttribute(getModel(numericAttrDef.model, orm).primaryKey, numericAttrDef.model, orm).type === 'number');
  842.     if (isSingularAssociationToModelWithNumericPk) {
  843.       throw buildUsageError(
  844.         'E_INVALID_NUMERIC_ATTR_NAME',
  845.         'While the attribute named `'+query.numericAttrName+'` defined in this model IS guaranteed '+
  846.         'to be a number (because it is a singular association to a model w/ a numeric primary key), '+
  847.         'it almost certainly shouldn\'t be used for this purpose.  If you are seeing this error message, '+
  848.         'it is likely due to a mistake in userland code, so please check your query.',
  849.         query.using
  850.       );
  851.     }//-鈥
  852.  
  853.     // Validate that the attribute with this name is a number.
  854.     if (numericAttrDef.type !== 'number') {
  855.       throw buildUsageError(
  856.         'E_INVALID_NUMERIC_ATTR_NAME',
  857.         'The attribute named `'+query.numericAttrName+'` defined in this model is not guaranteed to be a number '+
  858.         '(it should declare `type: \'number\'`).',
  859.         query.using
  860.       );
  861.     }
  862.  
  863.   }//>-鈥
  864.  
  865.  
  866.  
  867.  
  868.  
  869.   //-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
  870.   // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  871.   //- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  872.   //-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
  873.   // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  874.   //- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  875.   //-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
  876.  
  877.  
  878.  
  879.   //  鈻堚枅鈻堚枅鈻堚枅鈻堚晽 鈻堚枅鈻堚枅鈻堚晽  鈻堚枅鈻堚枅鈻堚枅鈺椻枅鈻堚晽  鈻堚枅鈺    鈻堚枅鈻堚枅鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚枅鈻堚晽 鈻堚枅鈻堚枅鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚枅鈺
  880.   //  鈻堚枅鈺斺晲鈺愨晲鈺愨暆鈻堚枅鈺斺晲鈺愨枅鈻堚晽鈻堚枅鈺斺晲鈺愨晲鈺愨暆鈻堚枅鈺  鈻堚枅鈺    鈻堚枅鈺斺晲鈺愨枅鈻堚晽鈻堚枅鈺斺晲鈺愨晲鈺愨暆鈻堚枅鈺斺晲鈺愨晲鈺愨暆鈻堚枅鈺斺晲鈺愨晲鈻堚枅鈺椻枅鈻堚晹鈺愨晲鈻堚枅鈺椻枅鈻堚晹鈺愨晲鈻堚枅鈺
  881.   //  鈻堚枅鈻堚枅鈻堚晽  鈻堚枅鈻堚枅鈻堚枅鈻堚晳鈻堚枅鈺     鈻堚枅鈻堚枅鈻堚枅鈻堚晳    鈻堚枅鈻堚枅鈻堚枅鈺斺暆鈻堚枅鈻堚枅鈻堚晽  鈻堚枅鈺     鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚晹鈺濃枅鈻堚晳  鈻堚枅鈺
  882.   //  鈻堚枅鈺斺晲鈺愨暆  鈻堚枅鈺斺晲鈺愨枅鈻堚晳鈻堚枅鈺     鈻堚枅鈺斺晲鈺愨枅鈻堚晳    鈻堚枅鈺斺晲鈺愨枅鈻堚晽鈻堚枅鈺斺晲鈺愨暆  鈻堚枅鈺     鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚晹鈺愨晲鈻堚枅鈺椻枅鈻堚晳  鈻堚枅鈺
  883.   //  鈻堚枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈺  鈻堚枅鈺戔暁鈻堚枅鈻堚枅鈻堚枅鈺椻枅鈻堚晳  鈻堚枅鈺    鈻堚枅鈺  鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚枅鈺椻暁鈻堚枅鈻堚枅鈻堚枅鈺椻暁鈻堚枅鈻堚枅鈻堚枅鈺斺暆鈻堚枅鈺  鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚晹鈺
  884.   //  鈺氣晲鈺愨晲鈺愨晲鈺愨暆鈺氣晲鈺  鈺氣晲鈺 鈺氣晲鈺愨晲鈺愨晲鈺濃暁鈺愨暆  鈺氣晲鈺    鈺氣晲鈺  鈺氣晲鈺濃暁鈺愨晲鈺愨晲鈺愨晲鈺 鈺氣晲鈺愨晲鈺愨晲鈺 鈺氣晲鈺愨晲鈺愨晲鈺 鈺氣晲鈺  鈺氣晲鈺濃暁鈺愨晲鈺愨晲鈺愨暆
  885.   //
  886.   //      鈻堚枅鈺    鈻堚枅鈻堚枅鈻堚枅鈻堚晽 鈻堚枅鈻堚枅鈻堚晽  鈻堚枅鈻堚枅鈻堚枅鈺椻枅鈻堚晽  鈻堚枅鈺    鈻堚枅鈻堚枅鈻堚枅鈺  鈻堚枅鈻堚枅鈻堚晽 鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚枅鈺椻枅鈻堚晽  鈻堚枅鈺
  887.   //     鈻堚枅鈺斺暆    鈻堚枅鈺斺晲鈺愨晲鈺愨暆鈻堚枅鈺斺晲鈺愨枅鈻堚晽鈻堚枅鈺斺晲鈺愨晲鈺愨暆鈻堚枅鈺  鈻堚枅鈺    鈻堚枅鈺斺晲鈺愨枅鈻堚晽鈻堚枅鈺斺晲鈺愨枅鈻堚晽鈺氣晲鈺愨枅鈻堚晹鈺愨晲鈺濃枅鈻堚晹鈺愨晲鈺愨晲鈺濃枅鈻堚晳  鈻堚枅鈺
  888.   //    鈻堚枅鈺斺暆     鈻堚枅鈻堚枅鈻堚晽  鈻堚枅鈻堚枅鈻堚枅鈻堚晳鈻堚枅鈺     鈻堚枅鈻堚枅鈻堚枅鈻堚晳    鈻堚枅鈻堚枅鈻堚枅鈺斺暆鈻堚枅鈻堚枅鈻堚枅鈻堚晳   鈻堚枅鈺   鈻堚枅鈺     鈻堚枅鈻堚枅鈻堚枅鈻堚晳
  889.   //   鈻堚枅鈺斺暆      鈻堚枅鈺斺晲鈺愨暆  鈻堚枅鈺斺晲鈺愨枅鈻堚晳鈻堚枅鈺     鈻堚枅鈺斺晲鈺愨枅鈻堚晳    鈻堚枅鈺斺晲鈺愨枅鈻堚晽鈻堚枅鈺斺晲鈺愨枅鈻堚晳   鈻堚枅鈺   鈻堚枅鈺     鈻堚枅鈺斺晲鈺愨枅鈻堚晳
  890.   //  鈻堚枅鈺斺暆       鈻堚枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈺  鈻堚枅鈺戔暁鈻堚枅鈻堚枅鈻堚枅鈺椻枅鈻堚晳  鈻堚枅鈺    鈻堚枅鈻堚枅鈻堚枅鈺斺暆鈻堚枅鈺  鈻堚枅鈺   鈻堚枅鈺   鈺氣枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈺  鈻堚枅鈺
  891.   //  鈺氣晲鈺        鈺氣晲鈺愨晲鈺愨晲鈺愨暆鈺氣晲鈺  鈺氣晲鈺 鈺氣晲鈺愨晲鈺愨晲鈺濃暁鈺愨暆  鈺氣晲鈺    鈺氣晲鈺愨晲鈺愨晲鈺 鈺氣晲鈺  鈺氣晲鈺   鈺氣晲鈺    鈺氣晲鈺愨晲鈺愨晲鈺濃暁鈺愨暆  鈺氣晲鈺
  892.   //
  893.   //   鈻堚枅鈺椻枅鈻堚枅鈻堚枅鈻堚枅鈺椻枅鈻堚晽   鈻堚枅鈺椻枅鈻堚枅鈺   鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚枅鈺椻枅鈻堚枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚枅鈺 鈻堚枅鈻堚晽   鈻堚枅鈺椻枅鈻堚枅鈻堚枅鈻堚枅鈺椻枅鈻堚晽
  894.   //  鈻堚枅鈺斺暆鈻堚枅鈺斺晲鈺愨晲鈺愨暆鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚枅鈻堚晽  鈻堚枅鈺戔枅鈻堚晹鈺愨晲鈺愨晲鈺濃暁鈺愨晲鈻堚枅鈺斺晲鈺愨暆鈻堚枅鈺戔枅鈻堚晹鈺愨晲鈺愨枅鈻堚晽鈻堚枅鈻堚枅鈺  鈻堚枅鈺戔枅鈻堚晹鈺愨晲鈺愨晲鈺濃暁鈻堚枅鈺
  895.   //  鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚晽  鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚晹鈻堚枅鈺 鈻堚枅鈺戔枅鈻堚晳        鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚晳   鈻堚枅鈺戔枅鈻堚晹鈻堚枅鈺 鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚枅鈺 鈻堚枅鈺
  896.   //  鈻堚枅鈺 鈻堚枅鈺斺晲鈺愨暆  鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚晳鈺氣枅鈻堚晽鈻堚枅鈺戔枅鈻堚晳        鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚晳   鈻堚枅鈺戔枅鈻堚晳鈺氣枅鈻堚晽鈻堚枅鈺戔暁鈺愨晲鈺愨晲鈻堚枅鈺 鈻堚枅鈺
  897.   //  鈺氣枅鈻堚晽鈻堚枅鈺     鈺氣枅鈻堚枅鈻堚枅鈻堚晹鈺濃枅鈻堚晳 鈺氣枅鈻堚枅鈻堚晳鈺氣枅鈻堚枅鈻堚枅鈻堚晽   鈻堚枅鈺   鈻堚枅鈺戔暁鈻堚枅鈻堚枅鈻堚枅鈺斺暆鈻堚枅鈺 鈺氣枅鈻堚枅鈻堚晳鈻堚枅鈻堚枅鈻堚枅鈻堚晳鈻堚枅鈺斺暆
  898.   //   鈺氣晲鈺濃暁鈺愨暆      鈺氣晲鈺愨晲鈺愨晲鈺 鈺氣晲鈺  鈺氣晲鈺愨晲鈺 鈺氣晲鈺愨晲鈺愨晲鈺   鈺氣晲鈺   鈺氣晲鈺 鈺氣晲鈺愨晲鈺愨晲鈺 鈺氣晲鈺  鈺氣晲鈺愨晲鈺濃暁鈺愨晲鈺愨晲鈺愨晲鈺濃暁鈺愨暆
  899.   //
  900.   // If we are expecting either eachBatchFn or eachRecordFn, then make sure
  901.   // one or the other is set... but not both!  And make sure that, whichever
  902.   // one is specified, it is a function.
  903.   //
  904.   // > This is only a problem if BOTH `eachRecordFn` and `eachBatchFn` are
  905.   // > left undefined, or if they are BOTH set.  (i.e. xor)
  906.   // > See https://gist.github.com/mikermcneil/d1e612cd1a8564a79f61e1f556fc49a6#edge-cases--details
  907.   if (_.contains(queryKeys, 'eachRecordFn') || _.contains(queryKeys, 'eachBatchFn')) {
  908.  
  909.     // -> Both functions were defined
  910.     if (!_.isUndefined(query.eachRecordFn) && !_.isUndefined(query.eachBatchFn)) {
  911.  
  912.       throw buildUsageError(
  913.         'E_INVALID_STREAM_ITERATEE',
  914.         'An iteratee function should be passed in to `.stream()` via either ' +
  915.         '`.eachRecord()` or `.eachBatch()` -- but never both.  Please set one or the other.',
  916.         query.using
  917.       );
  918.  
  919.     }
  920.     // -> Only `eachRecordFn` was defined
  921.     else if (!_.isUndefined(query.eachRecordFn)) {
  922.  
  923.       if (!_.isFunction(query.eachRecordFn)) {
  924.         throw buildUsageError(
  925.           'E_INVALID_STREAM_ITERATEE',
  926.           'For `eachRecordFn`, instead of a function, got: '+util.inspect(query.eachRecordFn,{depth:5}),
  927.           query.using
  928.         );
  929.       }
  930.  
  931.     }
  932.     // -> Only `eachBatchFn` was defined
  933.     else if (!_.isUndefined(query.eachBatchFn)) {
  934.  
  935.       if (!_.isFunction(query.eachBatchFn)) {
  936.         throw buildUsageError(
  937.           'E_INVALID_STREAM_ITERATEE',
  938.           'For `eachBatchFn`, instead of a function, got: '+util.inspect(query.eachBatchFn,{depth:5}),
  939.           query.using
  940.         );
  941.       }
  942.  
  943.     }
  944.     // -> Both were left undefined
  945.     else {
  946.  
  947.       throw buildUsageError(
  948.         'E_INVALID_STREAM_ITERATEE',
  949.         'Either `eachRecordFn` or `eachBatchFn` should be defined, but neither of them are.',
  950.         query.using
  951.       );
  952.  
  953.     }
  954.  
  955.   }//>-鈥
  956.  
  957.  
  958.  
  959.  
  960.  
  961.   //-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
  962.   // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  963.   //- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  964.   //-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
  965.   // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  966.   //- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  967.   //-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
  968.  
  969.  
  970.  
  971.  
  972.   //  鈻堚枅鈻堚晽   鈻堚枅鈺椻枅鈻堚枅鈻堚枅鈻堚枅鈺椻枅鈻堚晽    鈻堚枅鈺    鈻堚枅鈻堚枅鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚枅鈻堚晽 鈻堚枅鈻堚枅鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚枅鈺
  973.   //  鈻堚枅鈻堚枅鈺  鈻堚枅鈺戔枅鈻堚晹鈺愨晲鈺愨晲鈺濃枅鈻堚晳    鈻堚枅鈺    鈻堚枅鈺斺晲鈺愨枅鈻堚晽鈻堚枅鈺斺晲鈺愨晲鈺愨暆鈻堚枅鈺斺晲鈺愨晲鈺愨暆鈻堚枅鈺斺晲鈺愨晲鈻堚枅鈺椻枅鈻堚晹鈺愨晲鈻堚枅鈺椻枅鈻堚晹鈺愨晲鈻堚枅鈺
  974.   //  鈻堚枅鈺斺枅鈻堚晽 鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈺  鈻堚枅鈺 鈻堚晽 鈻堚枅鈺    鈻堚枅鈻堚枅鈻堚枅鈺斺暆鈻堚枅鈻堚枅鈻堚晽  鈻堚枅鈺     鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚晹鈺濃枅鈻堚晳  鈻堚枅鈺
  975.   //  鈻堚枅鈺戔暁鈻堚枅鈺椻枅鈻堚晳鈻堚枅鈺斺晲鈺愨暆  鈻堚枅鈺戔枅鈻堚枅鈺椻枅鈻堚晳    鈻堚枅鈺斺晲鈺愨枅鈻堚晽鈻堚枅鈺斺晲鈺愨暆  鈻堚枅鈺     鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚晹鈺愨晲鈻堚枅鈺椻枅鈻堚晳  鈻堚枅鈺
  976.   //  鈻堚枅鈺 鈺氣枅鈻堚枅鈻堚晳鈻堚枅鈻堚枅鈻堚枅鈻堚晽鈺氣枅鈻堚枅鈺斺枅鈻堚枅鈺斺暆    鈻堚枅鈺  鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚枅鈺椻暁鈻堚枅鈻堚枅鈻堚枅鈺椻暁鈻堚枅鈻堚枅鈻堚枅鈺斺暆鈻堚枅鈺  鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚晹鈺
  977.   //  鈺氣晲鈺  鈺氣晲鈺愨晲鈺濃暁鈺愨晲鈺愨晲鈺愨晲鈺 鈺氣晲鈺愨暆鈺氣晲鈺愨暆     鈺氣晲鈺  鈺氣晲鈺濃暁鈺愨晲鈺愨晲鈺愨晲鈺 鈺氣晲鈺愨晲鈺愨晲鈺 鈺氣晲鈺愨晲鈺愨晲鈺 鈺氣晲鈺  鈺氣晲鈺濃暁鈺愨晲鈺愨晲鈺愨暆
  978.   if (_.contains(queryKeys, 'newRecord')) {
  979.  
  980.     // If this was provided as an array, apprehend it before calling our `normalizeNewRecord()` ,
  981.     // in order to log a slightly more specific error message.
  982.     if (_.isArray(query.newRecord)) {
  983.       throw buildUsageError(
  984.         'E_INVALID_NEW_RECORD',
  985.         'Got an array, but expected new record to be provided as a dictionary (plain JavaScript object).  '+
  986.         'Array usage is no longer supported as of Sails v1.0 / Waterline 0.13.  Instead, please explicitly '+
  987.         'call `.createEach()`.',
  988.         query.using
  989.       );
  990.     }//-鈥
  991.  
  992.  
  993.     try {
  994.       query.newRecord = normalizeNewRecord(query.newRecord, query.using, orm, theMomentBeforeFS2Q);
  995.     } catch (e) {
  996.       switch (e.code){
  997.  
  998.         case 'E_TYPE':
  999.         case 'E_REQUIRED':
  1000.         case 'E_VIOLATES_RULES':
  1001.           throw buildUsageError('E_INVALID_NEW_RECORD', e.message, query.using);
  1002.  
  1003.         case 'E_HIGHLY_IRREGULAR':
  1004.           throw buildUsageError('E_INVALID_NEW_RECORD', e.message, query.using);
  1005.  
  1006.         default: throw e;
  1007.       }
  1008.     }//</catch>
  1009.  
  1010.   }//>-鈥
  1011.  
  1012.  
  1013.  
  1014.  
  1015.  
  1016.   //  鈻堚枅鈻堚晽   鈻堚枅鈺椻枅鈻堚枅鈻堚枅鈻堚枅鈺椻枅鈻堚晽    鈻堚枅鈺    鈻堚枅鈻堚枅鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚枅鈻堚晽 鈻堚枅鈻堚枅鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚枅鈻堚晽
  1017.   //  鈻堚枅鈻堚枅鈺  鈻堚枅鈺戔枅鈻堚晹鈺愨晲鈺愨晲鈺濃枅鈻堚晳    鈻堚枅鈺    鈻堚枅鈺斺晲鈺愨枅鈻堚晽鈻堚枅鈺斺晲鈺愨晲鈺愨暆鈻堚枅鈺斺晲鈺愨晲鈺愨暆鈻堚枅鈺斺晲鈺愨晲鈻堚枅鈺椻枅鈻堚晹鈺愨晲鈻堚枅鈺椻枅鈻堚晹鈺愨晲鈻堚枅鈺椻枅鈻堚晹鈺愨晲鈺愨晲鈺
  1018.   //  鈻堚枅鈺斺枅鈻堚晽 鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈺  鈻堚枅鈺 鈻堚晽 鈻堚枅鈺    鈻堚枅鈻堚枅鈻堚枅鈺斺暆鈻堚枅鈻堚枅鈻堚晽  鈻堚枅鈺     鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚晹鈺濃枅鈻堚晳  鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚枅鈺
  1019.   //  鈻堚枅鈺戔暁鈻堚枅鈺椻枅鈻堚晳鈻堚枅鈺斺晲鈺愨暆  鈻堚枅鈺戔枅鈻堚枅鈺椻枅鈻堚晳    鈻堚枅鈺斺晲鈺愨枅鈻堚晽鈻堚枅鈺斺晲鈺愨暆  鈻堚枅鈺     鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚晹鈺愨晲鈻堚枅鈺椻枅鈻堚晳  鈻堚枅鈺戔暁鈺愨晲鈺愨晲鈻堚枅鈺
  1020.   //  鈻堚枅鈺 鈺氣枅鈻堚枅鈻堚晳鈻堚枅鈻堚枅鈻堚枅鈻堚晽鈺氣枅鈻堚枅鈺斺枅鈻堚枅鈺斺暆    鈻堚枅鈺  鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚枅鈺椻暁鈻堚枅鈻堚枅鈻堚枅鈺椻暁鈻堚枅鈻堚枅鈻堚枅鈺斺暆鈻堚枅鈺  鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚晹鈺濃枅鈻堚枅鈻堚枅鈻堚枅鈺
  1021.   //  鈺氣晲鈺  鈺氣晲鈺愨晲鈺濃暁鈺愨晲鈺愨晲鈺愨晲鈺 鈺氣晲鈺愨暆鈺氣晲鈺愨暆     鈺氣晲鈺  鈺氣晲鈺濃暁鈺愨晲鈺愨晲鈺愨晲鈺 鈺氣晲鈺愨晲鈺愨晲鈺 鈺氣晲鈺愨晲鈺愨晲鈺 鈺氣晲鈺  鈺氣晲鈺濃暁鈺愨晲鈺愨晲鈺愨暆 鈺氣晲鈺愨晲鈺愨晲鈺愨暆
  1022.   if (_.contains(queryKeys, 'newRecords')) {
  1023.  
  1024.     if (_.isUndefined(query.newRecords)) {
  1025.       throw buildUsageError('E_INVALID_NEW_RECORDS', 'Please specify `newRecords`.', query.using);
  1026.     }//-鈥
  1027.  
  1028.     if (!_.isArray(query.newRecords)) {
  1029.       throw buildUsageError(
  1030.         'E_INVALID_NEW_RECORDS',
  1031.         'Expecting an array but instead, got: '+util.inspect(query.newRecords,{depth:5}),
  1032.         query.using
  1033.       );
  1034.     }//-鈥
  1035.  
  1036.     // If the array of new records contains any `undefined` items, strip them out.
  1037.     //
  1038.     // > Note that this does not work:
  1039.     // > ```
  1040.     // > _.remove(query.newRecords, undefined);
  1041.     // > ```
  1042.     _.remove(query.newRecords, function (newRecord){
  1043.       return _.isUndefined(newRecord);
  1044.     })
  1045. ;
  1046.  
  1047.     // Validate and normalize each new record in the provided array.
  1048.     query.newRecords = _.map(query.newRecords, function (newRecord){
  1049.  
  1050.       try {
  1051.         return normalizeNewRecord(newRecord, query.using, orm, theMomentBeforeFS2Q);
  1052.       } catch (e) {
  1053.         switch (e.code){
  1054.  
  1055.           case 'E_TYPE':
  1056.           case 'E_REQUIRED':
  1057.           case 'E_VIOLATES_RULES':
  1058.             throw buildUsageError(
  1059.               'E_INVALID_NEW_RECORDS',
  1060.               'Could not use one of the provided new records: '+e.message,
  1061.               query.using
  1062.             );
  1063.  
  1064.           case 'E_HIGHLY_IRREGULAR':
  1065.             throw buildUsageError(
  1066.               'E_INVALID_NEW_RECORDS',
  1067.               'Could not use one of the provided new records: '+e.message,
  1068.               query.using
  1069.             );
  1070.  
  1071.           default: throw e;
  1072.         }
  1073.       }//</catch>
  1074.  
  1075.     });//</_.each()>
  1076.  
  1077.   }//>-鈥
  1078.  
  1079.  
  1080.  
  1081.  
  1082.  
  1083.   //-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
  1084.   // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  1085.   //- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  1086.   //-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
  1087.   // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  1088.   //- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  1089.   //-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
  1090.  
  1091.  
  1092.   //  鈻堚枅鈺   鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚晽 鈻堚枅鈺     鈻堚枅鈺   鈻堚枅鈺椻枅鈻堚枅鈻堚枅鈻堚枅鈺椻枅鈻堚枅鈻堚枅鈻堚枅鈺
  1093.   //  鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚晹鈺愨晲鈻堚枅鈺椻枅鈻堚晳     鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚晹鈺愨晲鈺愨晲鈺濃枅鈻堚晹鈺愨晲鈺愨晲鈺
  1094.   //  鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚枅鈺戔枅鈻堚晳     鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈺  鈻堚枅鈻堚枅鈻堚枅鈻堚晽
  1095.   //  鈺氣枅鈻堚晽 鈻堚枅鈺斺暆鈻堚枅鈺斺晲鈺愨枅鈻堚晳鈻堚枅鈺     鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚晹鈺愨晲鈺  鈺氣晲鈺愨晲鈺愨枅鈻堚晳
  1096.   //   鈺氣枅鈻堚枅鈻堚晹鈺 鈻堚枅鈺  鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚枅鈺椻暁鈻堚枅鈻堚枅鈻堚枅鈺斺暆鈻堚枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈻堚枅鈻堚枅鈻堚晳
  1097.   //    鈺氣晲鈺愨晲鈺  鈺氣晲鈺  鈺氣晲鈺濃暁鈺愨晲鈺愨晲鈺愨晲鈺 鈺氣晲鈺愨晲鈺愨晲鈺 鈺氣晲鈺愨晲鈺愨晲鈺愨暆鈺氣晲鈺愨晲鈺愨晲鈺愨暆
  1098.   //
  1099.   //  鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚枅鈺     鈻堚枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈺
  1100.   //  鈺氣晲鈺愨枅鈻堚晹鈺愨晲鈺濃枅鈻堚晹鈺愨晲鈺愨枅鈻堚晽    鈻堚枅鈺斺晲鈺愨晲鈺愨暆鈻堚枅鈺斺晲鈺愨晲鈺愨暆鈺氣晲鈺愨枅鈻堚晹鈺愨晲鈺
  1101.   //     鈻堚枅鈺   鈻堚枅鈺   鈻堚枅鈺    鈻堚枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈻堚枅鈻堚晽     鈻堚枅鈺
  1102.   //     鈻堚枅鈺   鈻堚枅鈺   鈻堚枅鈺    鈺氣晲鈺愨晲鈺愨枅鈻堚晳鈻堚枅鈺斺晲鈺愨暆     鈻堚枅鈺
  1103.   //     鈻堚枅鈺   鈺氣枅鈻堚枅鈻堚枅鈻堚晹鈺    鈻堚枅鈻堚枅鈻堚枅鈻堚晳鈻堚枅鈻堚枅鈻堚枅鈻堚晽   鈻堚枅鈺
  1104.   //     鈺氣晲鈺    鈺氣晲鈺愨晲鈺愨晲鈺     鈺氣晲鈺愨晲鈺愨晲鈺愨暆鈺氣晲鈺愨晲鈺愨晲鈺愨暆   鈺氣晲鈺
  1105.   if (_.contains(queryKeys, 'valuesToSet')) {
  1106.  
  1107.     if (!_.isObject(query.valuesToSet) || _.isFunction(query.valuesToSet) || _.isArray(query.valuesToSet)) {
  1108.       throw buildUsageError(
  1109.         'E_INVALID_VALUES_TO_SET',
  1110.         'Expecting a dictionary (plain JavaScript object) but instead, got: '+util.inspect(query.valuesToSet,{depth:5}),
  1111.         query.using
  1112.       );
  1113.     }//-鈥
  1114.  
  1115.     // Now loop over and check every key specified in `valuesToSet`.
  1116.     _.each(_.keys(query.valuesToSet), function (attrNameToSet){
  1117.  
  1118.       // Validate & normalize this value.
  1119.       // > Note that we explicitly DO NOT allow values to be provided for
  1120.       // > collection attributes (plural associations) -- by passing in `false`
  1121.       try {
  1122.         query.valuesToSet[attrNameToSet] = normalizeValueToSet(query.valuesToSet[attrNameToSet], attrNameToSet, query.using, orm, false);
  1123.       } catch (e) {
  1124.         switch (e.code) {
  1125.  
  1126.           // If its RHS should be ignored (e.g. because it is `undefined`), then delete this key and bail early.
  1127.           case 'E_SHOULD_BE_IGNORED':
  1128.             delete query.valuesToSet[attrNameToSet];
  1129.             return;
  1130.  
  1131.  
  1132.           case 'E_TYPE':
  1133.           case 'E_REQUIRED':
  1134.           case 'E_VIOLATES_RULES':
  1135.             throw buildUsageError(
  1136.               'E_INVALID_VALUES_TO_SET',
  1137.               'Could not use specified `'+attrNameToSet+'`.  '+e.message,
  1138.               query.using
  1139.             );
  1140.  
  1141.           // For future reference, here are the additional properties we might expose:
  1142.           // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1143.           // 鈥 For E_TYPE:
  1144.           // ```
  1145.           // throw flaverr({
  1146.           //   code: 'E_TYPE',
  1147.           //   attrName: attrNameToSet,
  1148.           //   expectedType: e.expectedType
  1149.           // }, new Error(
  1150.           //   'The wrong type of data was specified for `'+attrNameToSet+'`.  '+e.message
  1151.           // ));
  1152.           // ```
  1153.           //
  1154.           // 鈥 For E_VIOLATES_RULES:
  1155.           // ```
  1156.           // assert(_.isArray(e.ruleViolations) && e.ruleViolations.length > 0, 'This error should ALWAYS have a non-empty array as its `ruleViolations` property.  But instead, its `ruleViolations` property is: '+util.inspect(e.ruleViolations, {depth: 5})+'\nAlso, for completeness/context, here is the error\'s complete stack: '+e.stack);
  1157.           // throw flaverr({
  1158.           //   code: 'E_VIOLATES_RULES',
  1159.           //   attrName: attrNameToSet,
  1160.           //   ruleViolations: ruleViolations
  1161.           // }, new Error(
  1162.           //   'Could not use specified `'+attrNameToSet+'`.  '+e.message
  1163.           // ));
  1164.           // ```
  1165.           // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1166.  
  1167.           case 'E_HIGHLY_IRREGULAR':
  1168.             throw buildUsageError(
  1169.               'E_INVALID_VALUES_TO_SET',
  1170.               'Could not use specified `'+attrNameToSet+'`.  '+e.message,
  1171.               query.using
  1172.             );
  1173.  
  1174.           default:
  1175.             throw e;
  1176.         }
  1177.       }//</catch>
  1178.  
  1179.     });//</_.each() key in the new record>
  1180.  
  1181.  
  1182.     // Now, for each `autoUpdatedAt` attribute, check if there was a corresponding value provided.
  1183.     // If not, then set the current timestamp as the value being set on the RHS.
  1184.     _.each(WLModel.attributes, function (attrDef, attrName) {
  1185.       if (!attrDef.autoUpdatedAt) { return; }
  1186.       if (!_.isUndefined(query.valuesToSet[attrName])) { return; }
  1187.  
  1188.       // -鈥 IWMIH, this is an attribute that has `autoUpdatedAt: true`,
  1189.       // and no value was explicitly provided for it.
  1190.       assert(attrDef.type === 'number' || attrDef.type === 'string', 'If an attribute has `autoUpdatedAt: true`, then it should always have either `type: \'string\'` or `type: \'number\'`.  But the definition for attribute (`'+attrName+'`) has somehow gotten into this state!  This should be impossible, but it has both `autoUpdatedAt: true` AND `type: \''+attrDef.type+'\'`');
  1191.  
  1192.       // Set the value equal to the current timestamp, using the appropriate format.
  1193.       if (attrDef.type === 'string') {
  1194.         query.valuesToSet[attrName] = (new Date(theMomentBeforeFS2Q)).toJSON();
  1195.       }
  1196.       else {
  1197.         query.valuesToSet[attrName] = theMomentBeforeFS2Q;
  1198.       }
  1199.  
  1200.     });//</_.each() : looping over autoUpdatedAt attributes >
  1201.  
  1202.   }//>-鈥
  1203.  
  1204.  
  1205.  
  1206.  
  1207.  
  1208.  
  1209.   //-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
  1210.   // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  1211.   //- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  1212.   //-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
  1213.   // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  1214.   //- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  1215.   //-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
  1216.  
  1217.  
  1218.  
  1219.  
  1220.  
  1221.  
  1222.   //   鈻堚枅鈻堚枅鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚枅鈺 鈻堚枅鈺     鈻堚枅鈺     鈻堚枅鈻堚枅鈻堚枅鈻堚晽 鈻堚枅鈻堚枅鈻堚枅鈺椻枅鈻堚枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚枅鈺 鈻堚枅鈻堚晽   鈻堚枅鈺
  1223.   //  鈻堚枅鈺斺晲鈺愨晲鈺愨暆鈻堚枅鈺斺晲鈺愨晲鈻堚枅鈺椻枅鈻堚晳     鈻堚枅鈺     鈻堚枅鈺斺晲鈺愨晲鈺愨暆鈻堚枅鈺斺晲鈺愨晲鈺愨暆鈺氣晲鈺愨枅鈻堚晹鈺愨晲鈺濃枅鈻堚晳鈻堚枅鈺斺晲鈺愨晲鈻堚枅鈺椻枅鈻堚枅鈻堚晽  鈻堚枅鈺
  1224.   //  鈻堚枅鈺     鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚晳     鈻堚枅鈺     鈻堚枅鈻堚枅鈻堚晽  鈻堚枅鈺        鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚晳   鈻堚枅鈺戔枅鈻堚晹鈻堚枅鈺 鈻堚枅鈺
  1225.   //  鈻堚枅鈺     鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚晳     鈻堚枅鈺     鈻堚枅鈺斺晲鈺愨暆  鈻堚枅鈺        鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚晳   鈻堚枅鈺戔枅鈻堚晳鈺氣枅鈻堚晽鈻堚枅鈺
  1226.   //  鈺氣枅鈻堚枅鈻堚枅鈻堚晽鈺氣枅鈻堚枅鈻堚枅鈻堚晹鈺濃枅鈻堚枅鈻堚枅鈻堚枅鈺椻枅鈻堚枅鈻堚枅鈻堚枅鈺椻枅鈻堚枅鈻堚枅鈻堚枅鈺椻暁鈻堚枅鈻堚枅鈻堚枅鈺   鈻堚枅鈺   鈻堚枅鈺戔暁鈻堚枅鈻堚枅鈻堚枅鈺斺暆鈻堚枅鈺 鈺氣枅鈻堚枅鈻堚晳
  1227.   //   鈺氣晲鈺愨晲鈺愨晲鈺 鈺氣晲鈺愨晲鈺愨晲鈺 鈺氣晲鈺愨晲鈺愨晲鈺愨暆鈺氣晲鈺愨晲鈺愨晲鈺愨暆鈺氣晲鈺愨晲鈺愨晲鈺愨暆 鈺氣晲鈺愨晲鈺愨晲鈺   鈺氣晲鈺   鈺氣晲鈺 鈺氣晲鈺愨晲鈺愨晲鈺 鈺氣晲鈺  鈺氣晲鈺愨晲鈺
  1228.   //
  1229.   //   鈻堚枅鈻堚枅鈻堚晽 鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈺椻枅鈻堚枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈻堚枅鈻堚枅鈺     鈻堚枅鈻堚晽   鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚晽 鈻堚枅鈻堚晽   鈻堚枅鈻堚晽鈻堚枅鈻堚枅鈻堚枅鈻堚晽
  1230.   //  鈻堚枅鈺斺晲鈺愨枅鈻堚晽鈺氣晲鈺愨枅鈻堚晹鈺愨晲鈺濃暁鈺愨晲鈻堚枅鈺斺晲鈺愨暆鈻堚枅鈺斺晲鈺愨枅鈻堚晽    鈻堚枅鈻堚枅鈺  鈻堚枅鈺戔枅鈻堚晹鈺愨晲鈻堚枅鈺椻枅鈻堚枅鈻堚晽 鈻堚枅鈻堚枅鈺戔枅鈻堚晹鈺愨晲鈺愨晲鈺
  1231.   //  鈻堚枅鈻堚枅鈻堚枅鈻堚晳   鈻堚枅鈺      鈻堚枅鈺   鈻堚枅鈻堚枅鈻堚枅鈺斺暆    鈻堚枅鈺斺枅鈻堚晽 鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚枅鈺戔枅鈻堚晹鈻堚枅鈻堚枅鈺斺枅鈻堚晳鈻堚枅鈻堚枅鈻堚晽
  1232.   //  鈻堚枅鈺斺晲鈺愨枅鈻堚晳   鈻堚枅鈺      鈻堚枅鈺   鈻堚枅鈺斺晲鈺愨枅鈻堚晽    鈻堚枅鈺戔暁鈻堚枅鈺椻枅鈻堚晳鈻堚枅鈺斺晲鈺愨枅鈻堚晳鈻堚枅鈺戔暁鈻堚枅鈺斺暆鈻堚枅鈺戔枅鈻堚晹鈺愨晲鈺
  1233.   //  鈻堚枅鈺  鈻堚枅鈺   鈻堚枅鈺      鈻堚枅鈺   鈻堚枅鈺  鈻堚枅鈺    鈻堚枅鈺 鈺氣枅鈻堚枅鈻堚晳鈻堚枅鈺  鈻堚枅鈺戔枅鈻堚晳 鈺氣晲鈺 鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚枅鈺
  1234.   //  鈺氣晲鈺  鈺氣晲鈺   鈺氣晲鈺      鈺氣晲鈺   鈺氣晲鈺  鈺氣晲鈺    鈺氣晲鈺  鈺氣晲鈺愨晲鈺濃暁鈺愨暆  鈺氣晲鈺濃暁鈺愨暆     鈺氣晲鈺濃暁鈺愨晲鈺愨晲鈺愨晲鈺
  1235.   // Look up the association by this name in this model definition.
  1236.   if (_.contains(queryKeys, 'collectionAttrName')) {
  1237.  
  1238.     if (!_.isString(query.collectionAttrName)) {
  1239.       throw buildUsageError(
  1240.         'E_INVALID_COLLECTION_ATTR_NAME',
  1241.         'Instead of a string, got: '+util.inspect(query.collectionAttrName,{depth:5}),
  1242.         query.using
  1243.       );
  1244.     }
  1245.  
  1246.     // Validate that an association by this name actually exists in this model definition.
  1247.     var associationDef;
  1248.     try {
  1249.       associationDef = getAttribute(query.collectionAttrName, query.using, orm);
  1250.     } catch (e) {
  1251.       switch (e.code) {
  1252.         case 'E_ATTR_NOT_REGISTERED':
  1253.           throw buildUsageError(
  1254.             'E_INVALID_COLLECTION_ATTR_NAME',
  1255.             'There is no attribute named `'+query.collectionAttrName+'` defined in this model.',
  1256.             query.using
  1257.           );
  1258.         default: throw e;
  1259.       }
  1260.     }//</catch>
  1261.  
  1262.     // Validate that the association with this name is a plural ("collection") association.
  1263.     if (!associationDef.collection) {
  1264.       throw buildUsageError(
  1265.         'E_INVALID_COLLECTION_ATTR_NAME',
  1266.         'The attribute named `'+query.collectionAttrName+'` defined in this model is not a plural ("collection") association.',
  1267.         query.using
  1268.       );
  1269.     }
  1270.  
  1271.   }//>-鈥
  1272.  
  1273.  
  1274.  
  1275.  
  1276.  
  1277.  
  1278.   //  鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚晽 鈻堚枅鈻堚枅鈻堚枅鈺  鈻堚枅鈻堚枅鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈺
  1279.   //  鈺氣晲鈺愨枅鈻堚晹鈺愨晲鈺濃枅鈻堚晹鈺愨晲鈻堚枅鈺椻枅鈻堚晹鈺愨晲鈻堚枅鈺椻枅鈻堚晹鈺愨晲鈺愨晲鈺 鈻堚枅鈺斺晲鈺愨晲鈺愨暆鈺氣晲鈺愨枅鈻堚晹鈺愨晲鈺
  1280.   //     鈻堚枅鈺   鈻堚枅鈻堚枅鈻堚枅鈻堚晳鈻堚枅鈻堚枅鈻堚枅鈺斺暆鈻堚枅鈺  鈻堚枅鈻堚晽鈻堚枅鈻堚枅鈻堚晽     鈻堚枅鈺
  1281.   //     鈻堚枅鈺   鈻堚枅鈺斺晲鈺愨枅鈻堚晳鈻堚枅鈺斺晲鈺愨枅鈻堚晽鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚晹鈺愨晲鈺     鈻堚枅鈺
  1282.   //     鈻堚枅鈺   鈻堚枅鈺  鈻堚枅鈺戔枅鈻堚晳  鈻堚枅鈺戔暁鈻堚枅鈻堚枅鈻堚枅鈺斺暆鈻堚枅鈻堚枅鈻堚枅鈻堚晽   鈻堚枅鈺
  1283.   //     鈺氣晲鈺   鈺氣晲鈺  鈺氣晲鈺濃暁鈺愨暆  鈺氣晲鈺 鈺氣晲鈺愨晲鈺愨晲鈺 鈺氣晲鈺愨晲鈺愨晲鈺愨暆   鈺氣晲鈺
  1284.   //
  1285.   //  鈻堚枅鈻堚枅鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚枅鈻堚晽 鈻堚枅鈻堚枅鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚枅鈺 鈻堚枅鈻堚枅鈻堚枅鈺     鈻堚枅鈺椻枅鈻堚枅鈻堚枅鈻堚晽 鈻堚枅鈻堚枅鈻堚枅鈻堚晽
  1286.   //  鈻堚枅鈺斺晲鈺愨枅鈻堚晽鈻堚枅鈺斺晲鈺愨晲鈺愨暆鈻堚枅鈺斺晲鈺愨晲鈺愨暆鈻堚枅鈺斺晲鈺愨晲鈻堚枅鈺椻枅鈻堚晹鈺愨晲鈻堚枅鈺椻枅鈻堚晹鈺愨晲鈻堚枅鈺    鈻堚枅鈺戔枅鈻堚晹鈺愨晲鈻堚枅鈺椻枅鈻堚晹鈺愨晲鈺愨晲鈺
  1287.   //  鈻堚枅鈻堚枅鈻堚枅鈺斺暆鈻堚枅鈻堚枅鈻堚晽  鈻堚枅鈺     鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚晹鈺濃枅鈻堚晳  鈻堚枅鈺    鈻堚枅鈺戔枅鈻堚晳  鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚枅鈺
  1288.   //  鈻堚枅鈺斺晲鈺愨枅鈻堚晽鈻堚枅鈺斺晲鈺愨暆  鈻堚枅鈺     鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚晹鈺愨晲鈻堚枅鈺椻枅鈻堚晳  鈻堚枅鈺    鈻堚枅鈺戔枅鈻堚晳  鈻堚枅鈺戔暁鈺愨晲鈺愨晲鈻堚枅鈺
  1289.   //  鈻堚枅鈺  鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚枅鈺椻暁鈻堚枅鈻堚枅鈻堚枅鈺椻暁鈻堚枅鈻堚枅鈻堚枅鈺斺暆鈻堚枅鈺  鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚晹鈺    鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚晹鈺濃枅鈻堚枅鈻堚枅鈻堚枅鈺
  1290.   //  鈺氣晲鈺  鈺氣晲鈺濃暁鈺愨晲鈺愨晲鈺愨晲鈺 鈺氣晲鈺愨晲鈺愨晲鈺 鈺氣晲鈺愨晲鈺愨晲鈺 鈺氣晲鈺  鈺氣晲鈺濃暁鈺愨晲鈺愨晲鈺愨暆     鈺氣晲鈺濃暁鈺愨晲鈺愨晲鈺愨暆 鈺氣晲鈺愨晲鈺愨晲鈺愨暆
  1291.   if (_.contains(queryKeys, 'targetRecordIds')) {
  1292.  
  1293.  
  1294.     //  鈺斺晽鈺斺晹鈺愨晽鈺︹晲鈺椻晹鈺︹晽鈺斺晲鈺椻暒  鈺︹晹鈺愨晽鈺斺晲鈺   鈹   鈺  鈺︹晹鈺愨晽鈺  鈺︹晹鈺︹晽鈺斺晲鈺椻晹鈺︹晽鈺斺晲鈺  鈹屸攢鈹愨攲鈹€鈹  鈹屸攢鈹愨敩鈹屸攢  鈹  鈹攲鈹€鈹愨敩  鈹屸攢鈹
  1295.     //  鈺戔晳鈺戔晳 鈺戔暊鈺︹暆鈺戔晳鈺戔暊鈺愨暎鈺  鈺戔晹鈺愨暆鈺戔暎   鈹屸敿鈹€  鈺氣晽鈺斺暆鈺犫晲鈺b晳  鈺 鈺戔晳鈺犫晲鈺 鈺 鈺戔暎   鈹溾攢鈹も敂鈹€鈹  鈹溾攢鈹樷敎鈹粹攼  鈹斺攼鈹屸敇鈹溾攢鈹も攤  鈹斺攢鈹
  1296.     //  鈺濃暁鈺濃暁鈺愨暆鈺┾暁鈺愨暕 鈺┾暕 鈺┾暕鈺愨暆鈺┾暁鈺愨暆鈺氣晲鈺  鈹斺敇    鈺氣暆 鈺 鈺┾暕鈺愨暆鈺┾晲鈺┾暆鈺 鈺 鈺 鈺氣晲鈺  鈹 鈹粹敂鈹€鈹  鈹  鈹 鈹   鈹斺敇 鈹 鈹粹敶鈹€鈹樷敂鈹€鈹
  1297.     // Normalize (and validate) the specified target record pk values.
  1298.     // (if a singular string or number was provided, this converts it into an array.)
  1299.     //
  1300.     // > Note that this ensures that they match the expected type indicated by this
  1301.     // > model's primary key attribute.
  1302.     try {
  1303.       var pkAttrDef = getAttribute(WLModel.primaryKey, query.using, orm);
  1304.       query.targetRecordIds = normalizePkValueOrValues(query.targetRecordIds, pkAttrDef.type);
  1305.     } catch(e) {
  1306.       switch (e.code) {
  1307.  
  1308.         case 'E_INVALID_PK_VALUE':
  1309.           throw buildUsageError(
  1310.             'E_INVALID_TARGET_RECORD_IDS',
  1311.             e.message,
  1312.             query.using
  1313.           );
  1314.  
  1315.         default:
  1316.           throw e;
  1317.  
  1318.       }
  1319.     }//< / catch : normalizePkValueOrValues >
  1320.  
  1321.  
  1322.     //  鈹 鈹攲鈹€鈹愨攲鈹愨攲鈹屸敩鈹愨敩  鈹屸攢鈹  鈺斺晽鈺斺晹鈺愨晽   鈺斺晲鈺椻晹鈺愨晽
  1323.     //  鈹溾攢鈹も敎鈹€鈹も攤鈹傗攤 鈹傗攤鈹  鈹溾敜   鈺戔晳鈺戔晳 鈺戔攢鈹€鈹€鈺 鈺戔暊鈺愨暆
  1324.     //  鈹 鈹粹敶 鈹粹敇鈹斺敇鈹€鈹粹敇鈹粹攢鈹樷敂鈹€鈹  鈺濃暁鈺濃暁鈺愨暆   鈺氣晲鈺濃暕
  1325.     // No query that takes target record ids is meaningful without any of said ids.
  1326.     if (query.targetRecordIds.length === 0) {
  1327.       throw buildUsageError('E_NOOP', 'No target record ids were provided.', query.using);
  1328.     }//-鈥
  1329.  
  1330.  
  1331.     //  鈹 鈹攲鈹€鈹愨攲鈹愨攲鈹屸敩鈹愨敩  鈹屸攢鈹  鈺斺晲鈺椻晹鈺愨晽鈺斺晲鈺椻晹鈺愨晽鈺︹晹鈺愨晽鈺    鈺斺晲鈺椻晹鈺愨晽鈺斺晲鈺椻晹鈺愨晽鈺斺晲鈺
  1332.     //  鈹溾攢鈹も敎鈹€鈹も攤鈹傗攤 鈹傗攤鈹  鈹溾敜   鈺氣晲鈺椻暊鈺愨暆鈺戔暎 鈺  鈺戔暊鈺愨暎鈺    鈺  鈺犫晲鈺b暁鈺愨晽鈺戔暎 鈺氣晲鈺
  1333.     //  鈹 鈹粹敶 鈹粹敇鈹斺敇鈹€鈹粹敇鈹粹攢鈹樷敂鈹€鈹  鈺氣晲鈺濃暕  鈺氣晲鈺濃暁鈺愨暆鈺┾暕 鈺┾暕鈺愨暆  鈺氣晲鈺濃暕 鈺┾暁鈺愨暆鈺氣晲鈺濃暁鈺愨暆
  1334.     //  鈹屸攢鈹愨攲鈹€鈹愨敩鈹€鈹  鈺斺晲鈺椻晲鈺 鈺︹晹鈺愨晽鈺  鈺 鈺︹晹鈺愨晽鈺︹暒  鈺︹晹鈺愨晽   鈹屸敩鈹愨敩 鈹攲鈹€鈹   鈹 鈹攲鈹€鈹愨敩 鈹  鈹屸攢鈹愨攲鈹€鈹愨攲鈹€鈹愨攲鈹€鈹愨攲鈹€鈹愨敩鈹屸攢鈹愨攲鈹攼鈹攲鈹€鈹愨攲鈹愨攲鈹屸攢鈹
  1335.     //  鈹溾敜 鈹 鈹傗敎鈹敇  鈺戔暎 鈺斺暕鈺︹暆鈺  鈺  鈺 鈺戔暁鈺愨晽鈺戔暁鈺椻晹鈺濃晳鈺     鈹 鈹傗攤鈹傗攤 鈹傗攢鈹€鈹€鈹傗攤鈹傗敎鈹€鈹も敂鈹敇  鈹溾攢鈹も敂鈹€鈹愨敂鈹€鈹愨攤 鈹傗攤  鈹傗敎鈹€鈹 鈹 鈹傗攤 鈹傗攤鈹傗攤鈹斺攢鈹
  1336.     //  鈹  鈹斺攢鈹樷敶鈹斺攢  鈺氣晲鈺濃暕 鈺氣晲鈺氣晲鈺濃暕鈺愨暆鈺氣晲鈺濃暁鈺愨暆鈺 鈺氣暆 鈺氣晲鈺濃敇   鈹 鈹斺敶鈹樷敂鈹€鈹   鈹斺敶鈹樷敶 鈹 鈹   鈹 鈹粹敂鈹€鈹樷敂鈹€鈹樷敂鈹€鈹樷敂鈹€鈹樷敶鈹 鈹 鈹 鈹粹敂鈹€鈹樷敇鈹斺敇鈹斺攢鈹
  1337.     // Next, handle a few special cases that we are careful to fail loudly about.
  1338.  
  1339.     // If this query's method is `addToCollection` or `replaceCollection`, and if there is MORE THAN ONE target record...
  1340.     var isRelevantMethod = (query.method === 'addToCollection' || query.method === 'replaceCollection');
  1341.     if (query.targetRecordIds.length > 1 && isRelevantMethod) {
  1342.  
  1343.       // Now check to see if this is a two-way, exclusive association.
  1344.       // If so, then this query is impossible.
  1345.       //
  1346.       // > Note that, IWMIH, we already know this association is plural
  1347.       // > (we checked that above when validating `collectionAttrName`)
  1348.       var isAssociationExclusive = isExclusive(query.collectionAttrName, query.using, orm);
  1349.  
  1350.       if (isAssociationExclusive) {
  1351.         throw buildUsageError(
  1352.           'E_INVALID_TARGET_RECORD_IDS',
  1353.           'The  `'+query.collectionAttrName+'` association of the `'+query.using+'` model is exclusive, therefore you cannot '+
  1354.           'add to or replace the `'+query.collectionAttrName+'` for _multiple_ records in this model at the same time (because '+
  1355.           'doing so would mean linking the _same set_ of one or more child records with _multiple target records_.)  You are seeing '+
  1356.           'this error because this query provided >1 target record ids.  To resolve, change the query, or change your models to '+
  1357.           'make this association shared (use `collection` + `via` instead of `model` on the other side).',
  1358.           query.using
  1359.         );
  1360.       }//-鈥
  1361.  
  1362.     }//>-鈥
  1363.  
  1364.  
  1365.   }//>-鈥
  1366.  
  1367.  
  1368.  
  1369.  
  1370.  
  1371.  
  1372.  
  1373.  
  1374.  
  1375.   //   鈻堚枅鈻堚枅鈻堚晽 鈻堚枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈻堚枅鈻堚枅鈻堚晽 鈻堚枅鈻堚枅鈻堚枅鈺  鈻堚枅鈻堚枅鈻堚枅鈺椻枅鈻堚晽 鈻堚枅鈻堚枅鈻堚晽 鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈺椻枅鈻堚枅鈻堚枅鈻堚枅鈺椻枅鈻堚枅鈻堚枅鈻堚晽
  1376.   //  鈻堚枅鈺斺晲鈺愨枅鈻堚晽鈻堚枅鈺斺晲鈺愨晲鈺愨暆鈻堚枅鈺斺晲鈺愨晲鈺愨暆鈻堚枅鈺斺晲鈺愨晲鈻堚枅鈺椻枅鈻堚晹鈺愨晲鈺愨晲鈺濃枅鈻堚晳鈻堚枅鈺斺晲鈺愨枅鈻堚晽鈺氣晲鈺愨枅鈻堚晹鈺愨晲鈺濃枅鈻堚晹鈺愨晲鈺愨晲鈺濃枅鈻堚晹鈺愨晲鈻堚枅鈺
  1377.   //  鈻堚枅鈻堚枅鈻堚枅鈻堚晳鈻堚枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚晳     鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚枅鈺   鈻堚枅鈺   鈻堚枅鈻堚枅鈻堚晽  鈻堚枅鈺  鈻堚枅鈺
  1378.   //  鈻堚枅鈺斺晲鈺愨枅鈻堚晳鈺氣晲鈺愨晲鈺愨枅鈻堚晳鈺氣晲鈺愨晲鈺愨枅鈻堚晳鈻堚枅鈺   鈻堚枅鈺戔枅鈻堚晳     鈻堚枅鈺戔枅鈻堚晹鈺愨晲鈻堚枅鈺   鈻堚枅鈺   鈻堚枅鈺斺晲鈺愨暆  鈻堚枅鈺  鈻堚枅鈺
  1379.   //  鈻堚枅鈺  鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚枅鈺戔暁鈻堚枅鈻堚枅鈻堚枅鈺斺暆鈺氣枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈺戔枅鈻堚晳  鈻堚枅鈺   鈻堚枅鈺   鈻堚枅鈻堚枅鈻堚枅鈻堚晽鈻堚枅鈻堚枅鈻堚枅鈺斺暆
  1380.   //  鈺氣晲鈺  鈺氣晲鈺濃暁鈺愨晲鈺愨晲鈺愨晲鈺濃暁鈺愨晲鈺愨晲鈺愨晲鈺 鈺氣晲鈺愨晲鈺愨晲鈺  鈺氣晲鈺愨晲鈺愨晲鈺濃暁鈺愨暆鈺氣晲鈺  鈺氣晲鈺   鈺氣晲鈺   鈺氣晲鈺愨晲鈺愨晲鈺愨暆鈺氣晲鈺愨晲鈺愨晲鈺
  1381.   //
  1382.   //  鈻堚枅鈺椻枅鈻堚枅鈻堚枅鈻堚晽 鈻堚枅鈻堚枅鈻堚枅鈻堚晽
  1383.   //  鈻堚枅鈺戔枅鈻堚晹鈺愨晲鈻堚枅鈺椻枅鈻堚晹鈺愨晲鈺愨晲鈺
  1384.   //  鈻堚枅鈺戔枅鈻堚晳  鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚枅鈺
  1385.   //  鈻堚枅鈺戔枅鈻堚晳  鈻堚枅鈺戔暁鈺愨晲鈺愨晲鈻堚枅鈺
  1386.   //  鈻堚枅鈺戔枅鈻堚枅鈻堚枅鈻堚晹鈺濃枅鈻堚枅鈻堚枅鈻堚枅鈺
  1387.   //  鈺氣晲鈺濃暁鈺愨晲鈺愨晲鈺愨暆 鈺氣晲鈺愨晲鈺愨晲鈺愨暆
  1388.   if (_.contains(queryKeys, 'associatedIds')) {
  1389.  
  1390.     // Look up the ASSOCIATED Waterline model for this query, based on the `collectionAttrName`.
  1391.     // Then use that to look up the declared type of its primary key.
  1392.     //
  1393.     // > Note that, if there are any problems that would prevent us from doing this, they
  1394.     // > should have already been caught above, and we should never have made it to this point
  1395.     // > in the code.  So i.e. we can proceed with certainty that the model will exist.
  1396.     // > And since its definition will have already been verified for correctness when
  1397.     // > initializing Waterline, we can safely assume that it has a primary key, etc.
  1398.     var associatedPkType = (function(){
  1399.       var _associationDef = getAttribute(query.collectionAttrName, query.using, orm);
  1400.       var _otherModelIdentity = _associationDef.collection;
  1401.       var AssociatedModel = getModel(_otherModelIdentity, orm);
  1402.       var _associatedPkDef = getAttribute(AssociatedModel.primaryKey, _otherModelIdentity, orm);
  1403.       return _associatedPkDef.type;
  1404.     })();
  1405.  
  1406.  
  1407.     //  鈺斺晽鈺斺晹鈺愨晽鈺︹晲鈺椻晹鈺︹晽鈺斺晲鈺椻暒  鈺︹晹鈺愨晽鈺斺晲鈺   鈹   鈺  鈺︹晹鈺愨晽鈺  鈺︹晹鈺︹晽鈺斺晲鈺椻晹鈺︹晽鈺斺晲鈺  鈹屸攢鈹愨攲鈹€鈹  鈹屸攢鈹愨敩鈹屸攢  鈹  鈹攲鈹€鈹愨敩  鈹屸攢鈹
  1408.     //  鈺戔晳鈺戔晳 鈺戔暊鈺︹暆鈺戔晳鈺戔暊鈺愨暎鈺  鈺戔晹鈺愨暆鈺戔暎   鈹屸敿鈹€  鈺氣晽鈺斺暆鈺犫晲鈺b晳  鈺 鈺戔晳鈺犫晲鈺 鈺 鈺戔暎   鈹溾攢鈹も敂鈹€鈹  鈹溾攢鈹樷敎鈹粹攼  鈹斺攼鈹屸敇鈹溾攢鈹も攤  鈹斺攢鈹
  1409.     //  鈺濃暁鈺濃暁鈺愨暆鈺┾暁鈺愨暕 鈺┾暕 鈺┾暕鈺愨暆鈺┾暁鈺愨暆鈺氣晲鈺  鈹斺敇    鈺氣暆 鈺 鈺┾暕鈺愨暆鈺┾晲鈺┾暆鈺 鈺 鈺 鈺氣晲鈺  鈹 鈹粹敂鈹€鈹  鈹  鈹 鈹   鈹斺敇 鈹 鈹粹敶鈹€鈹樷敂鈹€鈹
  1410.     // Validate the provided set of associated record ids.
  1411.     // (if a singular string or number was provided, this converts it into an array.)
  1412.     //
  1413.     // > Note that this ensures that they match the expected type indicated by this
  1414.     // > model's primary key attribute.
  1415.     try {
  1416.       query.associatedIds = normalizePkValueOrValues(query.associatedIds, associatedPkType);
  1417.     } catch(e) {
  1418.       switch (e.code) {
  1419.  
  1420.         case 'E_INVALID_PK_VALUE':
  1421.           throw buildUsageError('E_INVALID_ASSOCIATED_IDS', e.message, query.using);
  1422.  
  1423.         default:
  1424.           throw e;
  1425.  
  1426.       }
  1427.     }//< / catch :: normalizePkValueOrValues >
  1428.  
  1429.  
  1430.     //  鈺斺晲鈺椻晹鈺愨晽鈺斺晲鈺椻晹鈺愨晽鈺︹晹鈺愨晽鈺    鈺斺晲鈺椻晹鈺愨晽鈺斺晲鈺椻晹鈺愨晽鈺斺晲鈺
  1431.     //  鈺氣晲鈺椻暊鈺愨暆鈺戔暎 鈺  鈺戔暊鈺愨暎鈺    鈺  鈺犫晲鈺b暁鈺愨晽鈺戔暎 鈺氣晲鈺
  1432.     //  鈺氣晲鈺濃暕  鈺氣晲鈺濃暁鈺愨暆鈺┾暕 鈺┾暕鈺愨暆  鈺氣晲鈺濃暕 鈺┾暁鈺愨暆鈺氣晲鈺濃暁鈺愨暆
  1433.     //  鈹屸攢    鈹 鈹屸攢鈹   鈹 鈹攲鈹愨攲鈹屸攢鈹愨敩 鈹攲鈹€鈹愨攲鈹€鈹愨攲鈹€鈹愨敩鈹€鈹愨攲鈹攼鈹屸攢鈹愨攲鈹攼  鈹屸攢鈹愨攲鈹€鈹愨攲鈹攼鈹屸攼 鈹攲鈹愨攲鈹屸攢鈹愨攲鈹攼鈹攲鈹€鈹愨攲鈹愨攲鈹屸攢鈹
  1434.     //  鈹傗攢鈹€鈹€  鈹 鈹溾敜    鈹 鈹傗攤鈹傗攤鈹斺攢鈹愨攤 鈹傗敎鈹€鈹樷敎鈹€鈹樷攤 鈹傗敎鈹敇 鈹 鈹溾敜  鈹傗攤  鈹  鈹 鈹傗攤鈹傗攤鈹溾敶鈹愨攤鈹傗攤鈹傗敎鈹€鈹 鈹 鈹傗攤 鈹傗攤鈹傗攤鈹斺攢鈹
  1435.     //  鈹斺攢    鈹磑鈹斺攢鈹榦  鈹斺攢鈹樷敇鈹斺敇鈹斺攢鈹樷敂鈹€鈹樷敶  鈹  鈹斺攢鈹樷敶鈹斺攢 鈹 鈹斺攢鈹樷攢鈹粹敇  鈹斺攢鈹樷敂鈹€鈹樷敶 鈹粹敂鈹€鈹樷敶鈹樷敂鈹樷敶 鈹 鈹 鈹粹敂鈹€鈹樷敇鈹斺敇鈹斺攢鈹
  1436.     //        鈹屸攢鈹愨攲鈹€鈹愨敩鈹€鈹  鈹屸攢鈹愨攲鈹€鈹愨敩鈹€鈹愨攲鈹攼鈹屸攢鈹愨敩鈹屸攼鈹  鈹屸敩鈹愨攲鈹€鈹愨攲鈹攼鈹屸攢鈹愨敩    鈹屸敩鈹愨攲鈹€鈹愨攲鈹攼鈹 鈹攲鈹€鈹愨攲鈹攼鈹屸攢鈹    鈹€鈹
  1437.     //        鈹溾敜 鈹 鈹傗敎鈹敇  鈹  鈹溾敜 鈹溾敩鈹 鈹 鈹溾攢鈹も攤鈹傗攤鈹  鈹傗攤鈹傗攤 鈹 鈹傗攤鈹溾敜 鈹    鈹傗攤鈹傗敎鈹  鈹 鈹溾攢鈹も攤 鈹 鈹傗攤鈹斺攢鈹  鈹€鈹€鈹€鈹
  1438.     //        鈹  鈹斺攢鈹樷敶鈹斺攢  鈹斺攢鈹樷敂鈹€鈹樷敶鈹斺攢 鈹 鈹 鈹粹敶鈹樷敂鈹  鈹 鈹粹敂鈹€鈹樷攢鈹粹敇鈹斺攢鈹樷敶鈹€鈹  鈹 鈹粹敂鈹€鈹 鈹 鈹 鈹粹敂鈹€鈹樷攢鈹粹敇鈹斺攢鈹    鈹€鈹
  1439.     //
  1440.     // Handle the case where this is a no-op.
  1441.     // An empty array is only a no-op if this query's method is `removeFromCollection` or `addToCollection`.
  1442.     var isQueryMeaningfulWithNoAssociatedIds = (query.method === 'removeFromCollection' || query.method === 'addToCollection');
  1443.     if (query.associatedIds.length === 0 && isQueryMeaningfulWithNoAssociatedIds) {
  1444.       throw buildUsageError('E_NOOP', 'No associated ids were provided.', query.using);
  1445.     }//-鈥
  1446.  
  1447.   }//>-鈥
  1448.  
  1449.  
  1450.  
  1451.  
  1452.   //-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
  1453.   // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  1454.   //- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  1455.   //-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
  1456.   // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  1457.   //- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  1458.   //-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
  1459.   // if (process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'test') {
  1460.   //   console.timeEnd('forgeStageTwoQuery');
  1461.   // }
  1462.  
  1463.  
  1464.   // --
  1465.   // The provided "stage 1 query guts" dictionary is now a logical protostatement ("stage 2 query").
  1466.   //
  1467.   // Do not return anything.
  1468.   return;
  1469.  
  1470. };
  1471.  
  1472.  
  1473.  
  1474.  
  1475.  
  1476.  
  1477.  
  1478. /**
  1479.  * To quickly do an ad-hoc test of this utility from the Node REPL...
  1480.  * (~7ms latency, Nov 22, 2016)
  1481.  */
  1482.  
  1483. /*```
  1484. q = { using: 'user', method: 'find', criteria: {where: {id: '3d'}, limit: 3} };  require('./lib/waterline/utils/query/forge-stage-two-query')(q, { collections: { user: { attributes: { id: { type: 'string', required: true, unique: true } }, primaryKey: 'id', hasSchema: false } } }); console.log(util.inspect(q,{depth:5}));
  1485. ```*/
  1486.  
  1487.  
  1488.  
  1489. /**
  1490.  * Now a slightly more complex example...
  1491.  * (~8ms latency, Nov 22, 2016)
  1492.  */
  1493.  
  1494. /*```
  1495. q = { using: 'user', method: 'find', populates: {pets: {}}, criteria: {where: {id: '3d'}, limit: 3} };  require('./lib/waterline/utils/query/forge-stage-two-query')(q, { collections: { user: { attributes: { id: { type: 'string', required: true, unique: true }, pets: { collection: 'pet' } }, primaryKey: 'id', hasSchema: false }, pet: { attributes: { id: { type:'number', required: true, unique: true } }, primaryKey: 'id', hasSchema: true } } }); console.log(util.inspect(q,{depth:5}));
  1496. ```*/
  1497.  
  1498.  
  1499.  
  1500. /**
  1501.  * Now a simple `create`...
  1502.  * (also demonstrates behavior of createdAt/updatedAt on create)
  1503.  */
  1504.  
  1505. /*```
  1506. q = { using: 'user', method: 'create', newRecord: { id: 3, age: 32, foo: 4 } };  require('./lib/waterline/utils/query/forge-stage-two-query')(q, { collections: { user: { attributes: { id: { type: 'string', required: true, unique: true }, createdAt: { autoCreatedAt: true, type: 'string' }, updatedAt: { autoUpdatedAt: true, type: 'number' }, age: { type: 'number', required: false }, foo: { type: 'string', required: true }, pets: { collection: 'pet' } }, primaryKey: 'id', hasSchema: true}, pet: { attributes: { id: { type:'number', required: true, unique: true } }, primaryKey: 'id', hasSchema: true } } }); console.log(util.inspect(q,{depth:5}));
  1507. ```*/
  1508.  
  1509.  
  1510.  
  1511. /**
  1512.  * Now a simple `update`...
  1513.  * (also demonstrates behavior of updatedAt on update)
  1514.  */
  1515.  
  1516. /*```
  1517. q = { using: 'user', method: 'update', valuesToSet: { id: 'asdfasdf', age: 32, foo: 4 } };  require('./lib/waterline/utils/query/forge-stage-two-query')(q, { collections: { user: { attributes: { id: { type: 'string', required: true, unique: true }, createdAt: { autoCreatedAt: true, required: false, type: 'string' }, updatedAt: { autoUpdatedAt: true, required: false, type: 'number' }, age: { type: 'number', required: false }, foo: { type: 'string', required: true }, pets: { collection: 'pet' } }, primaryKey: 'id', hasSchema: true}, pet: { attributes: { id: { type:'number', required: true, unique: true } }, primaryKey: 'id', hasSchema: true } } }); console.log(util.inspect(q,{depth:5}));
  1518. ```*/
  1519.  
  1520.  
  1521.  
  1522. /**
  1523.  * Mongo-style `sort` clause semantics...
  1524.  */
  1525.  
  1526. /*```
  1527. q = { using: 'user', method: 'update', criteria: { sort: { age: -1 } }, valuesToSet: { id: 'wat', age: null, foo: 4 } };  require('./lib/waterline/utils/query/forge-stage-two-query')(q, { collections: { user: { attributes: { id: { type: 'string', required: true, unique: true }, age: { type: 'number', required: false, defaultsTo: 99 }, foo: { type: 'string', required: true }, pets: { collection: 'pet' } }, primaryKey: 'id', hasSchema: true}, pet: { attributes: { id: { type:'number', required: true, unique: true } }, primaryKey: 'id', hasSchema: true } } }); console.log(util.inspect(q,{depth:5}));
  1528. ```*/
  1529.  
  1530.  
  1531. /**
  1532.  * `where` fracturing...
  1533.  */
  1534.  
  1535. /*```
  1536. q = { using: 'user', method: 'find', criteria: {where: {id: '3d', foo: 'bar'}, limit: 3} };  require('./lib/waterline/utils/query/forge-stage-two-query')(q, { collections: { user: { attributes: { id: { type: 'string', required: true, unique: true } }, primaryKey: 'id', hasSchema: false } } }); console.log(util.inspect(q,{depth:5}));
  1537. ```*/
  1538.  
  1539.  
  1540. /**
  1541.  * Another fracturing test case, this time with fracturing of modifiers within a multi-key, complex filter...
  1542.  */
  1543.  
  1544. /*```
  1545. q = { using: 'user', method: 'find', criteria: {where: {id: '3d', foo: { startsWith: 'b', contains: 'bar'} }, limit: 3} };  require('./lib/waterline/utils/query/forge-stage-two-query')(q, { collections: { user: { attributes: { id: { type: 'string', required: true, unique: true } }, primaryKey: 'id', hasSchema: false } } }); console.log(util.inspect(q,{depth:7}));
  1546. ```*/
  1547.  
  1548. /**
  1549.  * to demonstrate that you cannot both populate AND sort by an attribute at the same time...
  1550.  */
  1551.  
  1552. /*```
  1553. q = { using: 'user', method: 'find', populates: {mom: {}, pets: { sort: [{id: 'DESC'}] }}, criteria: {where: {}, limit: 3, sort: 'mom ASC'} };  require('./lib/waterline/utils/query/forge-stage-two-query')(q, { collections: { user: { attributes: { id: { type: 'string', required: true, unique: true }, mom: { model: 'user' }, pets: { collection: 'pet' } }, primaryKey: 'id', hasSchema: false }, pet: { attributes: { id: { type:'number', required: true, unique: true } }, primaryKey: 'id', hasSchema: true } } }); console.log(util.inspect(q,{depth:5}));
  1554. ```*/
  1555.  
  1556. /**
  1557.  * to demonstrate that you cannot sort by a plural association...
  1558.  */
  1559.  
  1560. /*```
  1561. q = { using: 'user', method: 'find', populates: {pets: { sort: [{id: 'DESC'}] }}, criteria: {where: {and: [{id: '3d'}, {or: [{id: 'asdf'}]} ]}, limit: 3, sort: 'pets asc'} };  require('./lib/waterline/utils/query/forge-stage-two-query')(q, { collections: { user: { attributes: { id: { type: 'string', required: true, unique: true }, pets: { collection: 'pet' } }, primaryKey: 'id', hasSchema: false }, pet: { attributes: { id: { type:'number', required: true, unique: true } }, primaryKey: 'id', hasSchema: true } } }); console.log(util.inspect(q,{depth:5}));
  1562. ```*/
  1563.  
  1564. /**
  1565.  * to demonstrate constraint normalization, and that it DOES NOT do full pk values checks...
  1566.  * (this is on purpose -- see https://docs.google.com/spreadsheets/d/1whV739iW6O9SxRZLCIe2lpvuAUqm-ie7j7tn_Pjir3s/edit#gid=1814738146)
  1567.  */
  1568.  
  1569. /*```
  1570. q = { using: 'user', method: 'find', criteria: {where: {id: '3.5'}, limit: 3} };  require('./lib/waterline/utils/query/forge-stage-two-query')(q, { collections: { user: { attributes: { id: { type: 'number', required: true, unique: true } }, primaryKey: 'id', hasSchema: false } } }); console.log(util.inspect(q,{depth:5}));
  1571. ```*/
  1572.  
  1573. /**
  1574.  * to demonstrate schema-aware normalization of modifiers...
  1575.  */
  1576.  
  1577. /*```
  1578. q = { using: 'user', method: 'find', criteria: {where: {id: { '>': '5' } }, limit: 3} };  require('./lib/waterline/utils/query/forge-stage-two-query')(q, { collections: { user: { attributes: { id: { type: 'number', required: true, unique: true } }, primaryKey: 'id', hasSchema: false } } }); console.log(util.inspect(q,{depth:5}));
  1579. ```*/
  1580.  
  1581.  
  1582.  
  1583. /**
  1584.  * to demonstrate expansion and escaping in string search modifiers...
  1585.  */
  1586.  
  1587. /*```
  1588. q = { using: 'user', method: 'find', criteria: {where: {foo: { 'contains': '100%' } }, limit: 3} };  require('./lib/waterline/utils/query/forge-stage-two-query')(q, { collections: { user: { attributes: { id: { type: 'number', required: true, unique: true } }, primaryKey: 'id', hasSchema: false } } }); console.log(util.inspect(q,{depth:5}));
  1589. ```*/
  1590.  
  1591.  
  1592.  
  1593. /**
  1594.  * to demonstrate how Date instances behave in criteria, and how they depend on the schema...
  1595.  */
  1596.  
  1597. /*```
  1598. q = { using: 'user', method: 'find', criteria: {where: {foo: { '>': new Date() }, createdAt: { '>': new Date() }, updatedAt: { '>': new Date() } }, limit: 3} };  require('./lib/waterline/utils/query/forge-stage-two-query')(q, { collections: { user: { attributes: { id: { type: 'number', required: true, unique: true }, createdAt: { type: 'number', required: false }, updatedAt: { type: 'string', required: false } }, primaryKey: 'id', hasSchema: false } } }); console.log(util.inspect(q,{depth:5}));
  1599. ```*/
  1600.  
  1601.  
  1602.  
  1603. /**
  1604.  * to demonstrate propagation of cascadeOnDestroy and fetchRecordsOnDestroy model settings
  1605.  */
  1606.  
  1607. /*```
  1608. q = { using: 'user', method: 'destroy', criteria: { sort: 'age DESC' } };  require('./lib/waterline/utils/query/forge-stage-two-query')(q, { collections: { user: { attributes: { id: { type: 'string', required: true, unique: true }, age: { type: 'number', required: false, defaultsTo: 99 }, foo: { type: 'string', required: true }, pets: { collection: 'pet' } }, primaryKey: 'id', hasSchema: true, fetchRecordsOnDestroy: true, cascadeOnDestroy: true}, pet: { attributes: { id: { type:'number', required: true, unique: true } }, primaryKey: 'id', hasSchema: true } } }); console.log(util.inspect(q,{depth:5}));
  1609. ```*/
  1610.  
downloadforge-stage-two-query.js Source code - Download waterline Source code
Related Source Codes/Software:
notepad-plus-plus - Notepad++ official repository h... 2017-01-10
che - Eclipse Che: Next-generation Eclipse IDE. Open sou... 2017-01-10
Gource - oftware version control visualization ... 2017-01-10
FDFullscreenPopGesture - A UINavigationController's category to enable full... 2017-01-10
node-style-guide - A guide for styling your node.js / JavaScript code... 2017-01-09
Workerman - An asynchronous event driven PHP framework for eas... 2017-01-10
structor - An advanced visual editor for React components ... 2017-01-10
golearn - Machine Learning for Go 2017-01-10
poisontap - Exploits locked/password protected computers over ... 2017-01-10
kcptun - A Simple UDP Tunnel Based On KCP 2017-01-11
GoSublime - A Golang plugin collection for SublimeText **3**, ... 2017-02-19
awesome-emacs - A community driven list of useful Emacs packages, ... 2017-02-19
RKNotificationHub - Make any UIView a full fledged notification center 2017-02-19
vimr - Project VimR - Refined Neovim experience for macOS... 2017-02-19
vue-admin - Vue Admin Panel Framework, Powered by Vue 2.0 and ... 2017-02-19
dev-setup - Mac OS X development environment setup: Easy-to-un... 2017-02-19
CMPopTipView - Custom UIView for iOS that pops up an animated "bu... 2017-02-19
git-recipes - Git for recipes in Chinese. The high quality of Gi... 2017-02-19
CLNDR - 2017-02-19
OptiKey - OptiKey - Full computer control and speech with yo... 2017-02-19

 Back to top