json-fs-query 2.1.1 → 2.2.1
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 +8 -0
- package/browser/jsonFsQuery.js +41 -19
- package/browser/jsonFsQuery.min.js +1 -1
- package/dist/data/queries/sortBy.d.ts +6 -0
- package/dist/data/queries/sortBy.js +13 -4
- package/dist/data/queries/sortBy.min.js +1 -1
- package/dist/data/queries/where.js +8 -6
- package/dist/data/queries/where.min.js +1 -1
- package/dist/data/utilities/parsers/findSortClauses.d.ts +1 -0
- package/dist/data/utilities/runQuery.js +18 -7
- package/dist/data/utilities/runQuery.min.js +1 -1
- package/package.json +1 -1
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
|
package/browser/jsonFsQuery.js
CHANGED
|
@@ -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
|
-
//
|
|
1236
|
-
//
|
|
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 (
|
|
1247
|
+
if (valueA == valueB) {
|
|
1239
1248
|
continue;
|
|
1240
1249
|
}
|
|
1241
|
-
const result =
|
|
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
|
|
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
|
|
1564
|
+
return typedValue > (0, _compareAs.compareAs)(type, value);
|
|
1554
1565
|
case '>=':
|
|
1555
|
-
return
|
|
1566
|
+
return typedValue >= (0, _compareAs.compareAs)(type, value);
|
|
1556
1567
|
case '<':
|
|
1557
|
-
return
|
|
1568
|
+
return typedValue < (0, _compareAs.compareAs)(type, value);
|
|
1558
1569
|
case '<=':
|
|
1559
|
-
return
|
|
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) &&
|
|
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
|
}
|
|
@@ -3810,6 +3821,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
3810
3821
|
});
|
|
3811
3822
|
exports.runQuery = void 0;
|
|
3812
3823
|
require("core-js/modules/esnext.iterator.constructor.js");
|
|
3824
|
+
require("core-js/modules/esnext.iterator.find.js");
|
|
3813
3825
|
require("core-js/modules/esnext.iterator.reduce.js");
|
|
3814
3826
|
require("core-js/modules/esnext.set.add-all.js");
|
|
3815
3827
|
require("core-js/modules/esnext.set.delete-all.js");
|
|
@@ -3849,15 +3861,15 @@ var _isEntityProperty = require("./parsers/isEntityProperty");
|
|
|
3849
3861
|
var _splitEntityProperty = require("./parsers/splitEntityProperty");
|
|
3850
3862
|
var _fieldType = require("./fieldType");
|
|
3851
3863
|
/**
|
|
3852
|
-
* Give each condition its field's declared type, so where
|
|
3853
|
-
* is looked up on that entity's record).
|
|
3864
|
+
* Give each condition and sort clause its field's declared type, so where and sortBy compare by it
|
|
3865
|
+
* (a joined entity's field is looked up on that entity's record).
|
|
3854
3866
|
* @param parsed
|
|
3855
3867
|
* @param recordData
|
|
3856
3868
|
*/
|
|
3857
|
-
const
|
|
3858
|
-
for (const
|
|
3869
|
+
const typeClauses = async (parsed, recordData) => {
|
|
3870
|
+
for (const clause of [...parsed.conditions, ...parsed.sortClauses]) {
|
|
3859
3871
|
let entity = parsed.entity;
|
|
3860
|
-
let property =
|
|
3872
|
+
let property = clause.property;
|
|
3861
3873
|
if ((0, _isEntityProperty.isEntityProperty)(property)) {
|
|
3862
3874
|
({
|
|
3863
3875
|
entity,
|
|
@@ -3867,7 +3879,7 @@ const typeConditions = async (parsed, recordData) => {
|
|
|
3867
3879
|
if (!Object.prototype.hasOwnProperty.call(recordData, entity)) {
|
|
3868
3880
|
recordData[entity] = await (0, _retrieveRecord.retrieveRecord)(entity);
|
|
3869
3881
|
}
|
|
3870
|
-
|
|
3882
|
+
clause.type = (0, _fieldType.fieldType)(recordData[entity], property);
|
|
3871
3883
|
}
|
|
3872
3884
|
};
|
|
3873
3885
|
const runQuery = async parsed => {
|
|
@@ -3890,13 +3902,23 @@ const runQuery = async parsed => {
|
|
|
3890
3902
|
[parsed.entity]: await (0, _showRecords.showRecords)()
|
|
3891
3903
|
};
|
|
3892
3904
|
}
|
|
3905
|
+
// update/delete apply their conditions before any join or merge runs, so a condition on another entity's field
|
|
3906
|
+
// can't be checked there. An indexed one used to be dropped once its index had narrowed the joined rows, and the
|
|
3907
|
+
// command then hit every row the other conditions allowed (`delete workouts.exercises on ... where
|
|
3908
|
+
// exercises._id = 1` deleted every workout)
|
|
3909
|
+
if (parsed.command === 'update' || parsed.command === 'delete') {
|
|
3910
|
+
const other = parsed.conditions.find(condition => (0, _isEntityProperty.isEntityProperty)(condition.property) && (0, _splitEntityProperty.splitEntityProperty)(condition.property).entity !== parsed.entity);
|
|
3911
|
+
if (other) {
|
|
3912
|
+
throw new Error(`${parsed.command} can only use conditions on ${parsed.entity}'s own fields, not ${other.property}`);
|
|
3913
|
+
}
|
|
3914
|
+
}
|
|
3893
3915
|
const recordData = {
|
|
3894
3916
|
[parsed.entity]: await (0, _retrieveRecord.retrieveRecord)(parsed.entity)
|
|
3895
3917
|
};
|
|
3896
3918
|
if (typeof parsed.joinEntity !== 'undefined') {
|
|
3897
3919
|
recordData[parsed.joinEntity] = await (0, _retrieveRecord.retrieveRecord)(parsed.joinEntity);
|
|
3898
3920
|
}
|
|
3899
|
-
await
|
|
3921
|
+
await typeClauses(parsed, recordData);
|
|
3900
3922
|
let dataSet = {
|
|
3901
3923
|
[parsed.entity]: []
|
|
3902
3924
|
};
|
|
@@ -3956,7 +3978,7 @@ const runQuery = async parsed => {
|
|
|
3956
3978
|
return dataSet;
|
|
3957
3979
|
};
|
|
3958
3980
|
exports.runQuery = runQuery;
|
|
3959
|
-
},{"../queries/createRecord":15,"../queries/deleteEntity":16,"../queries/describeRecord":17,"../queries/dropRecord":18,"../queries/groupBy":19,"../queries/insertEntity":20,"../queries/joinEntity":21,"../queries/limitOffset":22,"../queries/mergeJoins":23,"../queries/readEntity":28,"../queries/selectFields":29,"../queries/showRecords":30,"../queries/sortBy":31,"../queries/updateEntity":32,"../queries/where":33,"./fieldType":39,"./getConditionIndexedList":40,"./getJoinIndexedList":41,"./parsers/isEntityProperty":58,"./parsers/splitEntityProperty":66,"./retrieveRecord":77,"core-js/modules/esnext.iterator.constructor.js":235,"core-js/modules/esnext.iterator.reduce.js":243,"core-js/modules/esnext.set.add-all.js":258,"core-js/modules/esnext.set.delete-all.js":259,"core-js/modules/esnext.set.difference.js":260,"core-js/modules/esnext.set.every.js":261,"core-js/modules/esnext.set.filter.js":262,"core-js/modules/esnext.set.find.js":263,"core-js/modules/esnext.set.intersection.js":264,"core-js/modules/esnext.set.is-disjoint-from.js":265,"core-js/modules/esnext.set.is-subset-of.js":266,"core-js/modules/esnext.set.is-superset-of.js":267,"core-js/modules/esnext.set.join.js":268,"core-js/modules/esnext.set.map.js":269,"core-js/modules/esnext.set.reduce.js":270,"core-js/modules/esnext.set.some.js":271,"core-js/modules/esnext.set.symmetric-difference.js":272,"core-js/modules/esnext.set.union.js":273}],80:[function(require,module,exports){
|
|
3981
|
+
},{"../queries/createRecord":15,"../queries/deleteEntity":16,"../queries/describeRecord":17,"../queries/dropRecord":18,"../queries/groupBy":19,"../queries/insertEntity":20,"../queries/joinEntity":21,"../queries/limitOffset":22,"../queries/mergeJoins":23,"../queries/readEntity":28,"../queries/selectFields":29,"../queries/showRecords":30,"../queries/sortBy":31,"../queries/updateEntity":32,"../queries/where":33,"./fieldType":39,"./getConditionIndexedList":40,"./getJoinIndexedList":41,"./parsers/isEntityProperty":58,"./parsers/splitEntityProperty":66,"./retrieveRecord":77,"core-js/modules/esnext.iterator.constructor.js":235,"core-js/modules/esnext.iterator.find.js":239,"core-js/modules/esnext.iterator.reduce.js":243,"core-js/modules/esnext.set.add-all.js":258,"core-js/modules/esnext.set.delete-all.js":259,"core-js/modules/esnext.set.difference.js":260,"core-js/modules/esnext.set.every.js":261,"core-js/modules/esnext.set.filter.js":262,"core-js/modules/esnext.set.find.js":263,"core-js/modules/esnext.set.intersection.js":264,"core-js/modules/esnext.set.is-disjoint-from.js":265,"core-js/modules/esnext.set.is-subset-of.js":266,"core-js/modules/esnext.set.is-superset-of.js":267,"core-js/modules/esnext.set.join.js":268,"core-js/modules/esnext.set.map.js":269,"core-js/modules/esnext.set.reduce.js":270,"core-js/modules/esnext.set.some.js":271,"core-js/modules/esnext.set.symmetric-difference.js":272,"core-js/modules/esnext.set.union.js":273}],80:[function(require,module,exports){
|
|
3960
3982
|
"use strict";
|
|
3961
3983
|
|
|
3962
3984
|
Object.defineProperty(exports, "__esModule", {
|