json-fs-query 2.1.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -148,6 +148,14 @@ read workouts where reps > 8 and where date = '2024-06-02'
148
148
  `or where` is not supported yet, and is an error (in 1.x it was silently read as `and where`). And/or with grouping
149
149
  and precedence is planned.
150
150
 
151
+ ### Comparing by the field's type
152
+
153
+ Conditions compare by the field's declared type, whatever type the stored value or the literal has: in a `string`
154
+ or `int` field, `'3'` and `3` are equal. Ordering (`>`, `>=`, `<`, `<=`, `between`) and `sort` follow it too: a
155
+ `string` field orders as text, so `'10'` comes before `'9'` (even ids stored as numbers), and an `int` field as
156
+ numbers. A field with no declared type, or an `object` or `array` one, compares the stored values as they are.
157
+ `like` only matches values stored as text.
158
+
151
159
  ### Null and missing fields
152
160
 
153
161
  A field can be missing (left out) or hold `null`, and both are their own cases, as in SQL: they match no
@@ -1211,6 +1211,7 @@ Object.defineProperty(exports, "__esModule", {
1211
1211
  value: true
1212
1212
  });
1213
1213
  exports.sortBy = void 0;
1214
+ var _compareAs = require("../utilities/compareAs");
1214
1215
  // Where a value sorts before any other, ascending: missing first, then null, then the values (MySQL's null placement,
1215
1216
  // with missing below null). Descending reverses it.
1216
1217
  const presence = (data, property) => {
@@ -1219,6 +1220,12 @@ const presence = (data, property) => {
1219
1220
  }
1220
1221
  return data[property] === null ? 1 : 2;
1221
1222
  };
1223
+ /**
1224
+ * Sort by each clause in turn: missing, then null, then the values, compared by the field's declared type when the
1225
+ * clause has one (see compareAs), so a string field sorts as text ('10' before '9') and an int field as numbers.
1226
+ * @param dataSet
1227
+ * @param sortClauses
1228
+ */
1222
1229
  const sortBy = (dataSet = [], sortClauses = []) => {
1223
1230
  if (!sortClauses.length) {
1224
1231
  return dataSet;
@@ -1232,13 +1239,15 @@ const sortBy = (dataSet = [], sortClauses = []) => {
1232
1239
  if (rankA !== rankB) {
1233
1240
  return direction === 'asc' ? rankA - rankB : rankB - rankA;
1234
1241
  }
1235
- // Ordering compares the raw stored values, not by declared type (that's only for equality, see where), so
1236
- // numeric ids in a string field still sort numerically. Loose so '3' and 3 still tie, as do two nulls or two missing.
1242
+ // Values compare by the declared type, so numeric ids in a string field sort as text ('10' before '9'). Loose so
1243
+ // '3' and 3 still tie in an untyped field, as do two nulls or two missing.
1244
+ const valueA = (0, _compareAs.compareAs)(sortClause.type, a[property]);
1245
+ const valueB = (0, _compareAs.compareAs)(sortClause.type, b[property]);
1237
1246
  // eslint-disable-next-line eqeqeq
1238
- if (a[property] == b[property]) {
1247
+ if (valueA == valueB) {
1239
1248
  continue;
1240
1249
  }
1241
- const result = a[property] < b[property] ? -1 : 1;
1250
+ const result = valueA < valueB ? -1 : 1;
1242
1251
  return direction === 'asc' ? result : result * -1;
1243
1252
  }
1244
1253
  return 0;
@@ -1246,7 +1255,7 @@ const sortBy = (dataSet = [], sortClauses = []) => {
1246
1255
  return dataSet;
1247
1256
  };
1248
1257
  exports.sortBy = sortBy;
1249
- },{}],32:[function(require,module,exports){
1258
+ },{"../utilities/compareAs":34}],32:[function(require,module,exports){
1250
1259
  "use strict";
1251
1260
 
1252
1261
  Object.defineProperty(exports, "__esModule", {
@@ -1514,7 +1523,8 @@ const likeMatch = (dataValue, pattern) => {
1514
1523
  };
1515
1524
  /**
1516
1525
  * Equality (=, !=, <>, in) compares by the field's declared type when given one (see sameValue), so
1517
- * '3' and 3 match in a string or int field. Ordering (>, <, between, like) compares the raw values.
1526
+ * '3' and 3 match in a string or int field. Ordering (>, >=, <, <=, between) compares by it too (see compareAs): a
1527
+ * string field orders as text ('10' < '9'), an int field as numbers. like compares the raw values.
1518
1528
  *
1519
1529
  * Null and missing are their own cases, as in SQL: a field that is null or missing matches no comparison (not even
1520
1530
  * `!=`), and a null never equals anything, not even another null. Only these test for them:
@@ -1543,6 +1553,7 @@ const makeCondition = (data, property, comparator, value, type = undefined) => {
1543
1553
  return false;
1544
1554
  }
1545
1555
  const dataValue = data[property];
1556
+ const typedValue = (0, _compareAs.compareAs)(type, dataValue);
1546
1557
  switch (comparator) {
1547
1558
  case '=':
1548
1559
  return (0, _compareAs.sameValue)(type, dataValue, value);
@@ -1550,18 +1561,18 @@ const makeCondition = (data, property, comparator, value, type = undefined) => {
1550
1561
  case '<>':
1551
1562
  return !(0, _compareAs.sameValue)(type, dataValue, value);
1552
1563
  case '>':
1553
- return dataValue > value;
1564
+ return typedValue > (0, _compareAs.compareAs)(type, value);
1554
1565
  case '>=':
1555
- return dataValue >= value;
1566
+ return typedValue >= (0, _compareAs.compareAs)(type, value);
1556
1567
  case '<':
1557
- return dataValue < value;
1568
+ return typedValue < (0, _compareAs.compareAs)(type, value);
1558
1569
  case '<=':
1559
- return dataValue <= value;
1570
+ return typedValue <= (0, _compareAs.compareAs)(type, value);
1560
1571
  case 'in':
1561
1572
  return Array.isArray(value) && (type ? value.some(item => (0, _compareAs.sameValue)(type, dataValue, item)) : value.includes(dataValue));
1562
1573
  case 'between':
1563
1574
  // A null bound would act as 0 in >= / <=: it bounds nothing, so nothing matches (the parser refuses it)
1564
- return Array.isArray(value) && value.length === 2 && !value.includes(null) && dataValue >= value[0] && dataValue <= value[1];
1575
+ return Array.isArray(value) && value.length === 2 && !value.includes(null) && typedValue >= (0, _compareAs.compareAs)(type, value[0]) && typedValue <= (0, _compareAs.compareAs)(type, value[1]);
1565
1576
  case 'like':
1566
1577
  return likeMatch(dataValue, value);
1567
1578
  }
@@ -3849,15 +3860,15 @@ var _isEntityProperty = require("./parsers/isEntityProperty");
3849
3860
  var _splitEntityProperty = require("./parsers/splitEntityProperty");
3850
3861
  var _fieldType = require("./fieldType");
3851
3862
  /**
3852
- * Give each condition its field's declared type, so where compares by it (a joined entity's field
3853
- * is looked up on that entity's record).
3863
+ * Give each condition and sort clause its field's declared type, so where and sortBy compare by it
3864
+ * (a joined entity's field is looked up on that entity's record).
3854
3865
  * @param parsed
3855
3866
  * @param recordData
3856
3867
  */
3857
- const typeConditions = async (parsed, recordData) => {
3858
- for (const condition of parsed.conditions) {
3868
+ const typeClauses = async (parsed, recordData) => {
3869
+ for (const clause of [...parsed.conditions, ...parsed.sortClauses]) {
3859
3870
  let entity = parsed.entity;
3860
- let property = condition.property;
3871
+ let property = clause.property;
3861
3872
  if ((0, _isEntityProperty.isEntityProperty)(property)) {
3862
3873
  ({
3863
3874
  entity,
@@ -3867,7 +3878,7 @@ const typeConditions = async (parsed, recordData) => {
3867
3878
  if (!Object.prototype.hasOwnProperty.call(recordData, entity)) {
3868
3879
  recordData[entity] = await (0, _retrieveRecord.retrieveRecord)(entity);
3869
3880
  }
3870
- condition.type = (0, _fieldType.fieldType)(recordData[entity], property);
3881
+ clause.type = (0, _fieldType.fieldType)(recordData[entity], property);
3871
3882
  }
3872
3883
  };
3873
3884
  const runQuery = async parsed => {
@@ -3896,7 +3907,7 @@ const runQuery = async parsed => {
3896
3907
  if (typeof parsed.joinEntity !== 'undefined') {
3897
3908
  recordData[parsed.joinEntity] = await (0, _retrieveRecord.retrieveRecord)(parsed.joinEntity);
3898
3909
  }
3899
- await typeConditions(parsed, recordData);
3910
+ await typeClauses(parsed, recordData);
3900
3911
  let dataSet = {
3901
3912
  [parsed.entity]: []
3902
3913
  };