spice-js 2.6.73 → 2.6.75
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/build/models/SpiceModel.js +65 -8
- package/package.json +1 -1
- package/src/models/SpiceModel.js +74 -11
|
@@ -584,9 +584,10 @@ class SpiceModel {
|
|
|
584
584
|
if (_.isString(args.id)) {
|
|
585
585
|
if (args.skip_hooks !== true) {
|
|
586
586
|
yield _this4.run_hook(args, "get", "before");
|
|
587
|
-
}
|
|
587
|
+
} // ⚡ Include columns in cache key if specified
|
|
588
|
+
|
|
588
589
|
|
|
589
|
-
var key = "get::" + _this4.type + "::" + args.id;
|
|
590
|
+
var key = "get::" + _this4.type + "::" + args.id + (args.columns ? "::" + args.columns : "");
|
|
590
591
|
var results = {};
|
|
591
592
|
|
|
592
593
|
if (_this4.shouldUseCache(_this4.type)) {
|
|
@@ -640,7 +641,13 @@ class SpiceModel {
|
|
|
640
641
|
|
|
641
642
|
if (results.deleted === undefined || results.deleted === false) {
|
|
642
643
|
if (args.skip_read_serialize !== true && args.skip_serialize !== true) {
|
|
644
|
+
// ⚡ Pass columns to do_serialize so it can skip irrelevant modifiers
|
|
643
645
|
results = yield _this4.do_serialize(results, "read", {}, args, (yield _this4.propsToBeRemoved(results)));
|
|
646
|
+
} // ⚡ OPTIMIZED: Filter results by columns if specified
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
if (args.columns) {
|
|
650
|
+
results = _this4.filterResultsByColumns([results], args.columns)[0];
|
|
644
651
|
}
|
|
645
652
|
|
|
646
653
|
if (args.skip_hooks !== true) {
|
|
@@ -1695,11 +1702,16 @@ class SpiceModel {
|
|
|
1695
1702
|
addModifier(_ref15) {
|
|
1696
1703
|
var {
|
|
1697
1704
|
when,
|
|
1698
|
-
execute
|
|
1705
|
+
execute,
|
|
1706
|
+
field = null
|
|
1699
1707
|
} = _ref15;
|
|
1700
1708
|
|
|
1701
1709
|
if (this[_serializers][when]) {
|
|
1702
|
-
|
|
1710
|
+
// Store as object with field info for column-based filtering
|
|
1711
|
+
this[_serializers][when]["modifiers"].push({
|
|
1712
|
+
execute,
|
|
1713
|
+
field
|
|
1714
|
+
});
|
|
1703
1715
|
}
|
|
1704
1716
|
}
|
|
1705
1717
|
|
|
@@ -1728,15 +1740,19 @@ class SpiceModel {
|
|
|
1728
1740
|
switch (properties[i].map.type) {
|
|
1729
1741
|
case _2.MapType.MODEL:
|
|
1730
1742
|
{
|
|
1743
|
+
var destinationField = properties[i].map.destination || i;
|
|
1744
|
+
|
|
1731
1745
|
switch (properties[i].type) {
|
|
1732
1746
|
case String:
|
|
1733
1747
|
case "string":
|
|
1734
1748
|
{
|
|
1735
1749
|
_this17.addModifier({
|
|
1736
1750
|
when: properties[i].map.when || "read",
|
|
1751
|
+
field: destinationField,
|
|
1752
|
+
// ⚡ Track which field this modifier populates
|
|
1737
1753
|
execute: function () {
|
|
1738
1754
|
var _execute = _asyncToGenerator(function* (data) {
|
|
1739
|
-
return yield _this17.mapToObject(data, _.isString(properties[i].map.reference) ? spice.models[properties[i].map.reference] : properties[i].map.reference, i,
|
|
1755
|
+
return yield _this17.mapToObject(data, _.isString(properties[i].map.reference) ? spice.models[properties[i].map.reference] : properties[i].map.reference, i, destinationField, properties[i]);
|
|
1740
1756
|
});
|
|
1741
1757
|
|
|
1742
1758
|
function execute(_x) {
|
|
@@ -1755,9 +1771,11 @@ class SpiceModel {
|
|
|
1755
1771
|
{
|
|
1756
1772
|
_this17.addModifier({
|
|
1757
1773
|
when: properties[i].map.when || "read",
|
|
1774
|
+
field: destinationField,
|
|
1775
|
+
// ⚡ Track which field this modifier populates
|
|
1758
1776
|
execute: function () {
|
|
1759
1777
|
var _execute2 = _asyncToGenerator(function* (data) {
|
|
1760
|
-
return yield _this17.mapToObjectArray(data, _.isString(properties[i].map.reference) ? spice.models[properties[i].map.reference] : properties[i].map.reference, i,
|
|
1778
|
+
return yield _this17.mapToObjectArray(data, _.isString(properties[i].map.reference) ? spice.models[properties[i].map.reference] : properties[i].map.reference, i, destinationField, properties[i]);
|
|
1761
1779
|
});
|
|
1762
1780
|
|
|
1763
1781
|
function execute(_x2) {
|
|
@@ -1786,6 +1804,35 @@ class SpiceModel {
|
|
|
1786
1804
|
for (var i in properties) {
|
|
1787
1805
|
_loop(i);
|
|
1788
1806
|
}
|
|
1807
|
+
} // ⚡ Parse columns string into a Set for fast lookup
|
|
1808
|
+
|
|
1809
|
+
|
|
1810
|
+
parseRequestedColumns(columns) {
|
|
1811
|
+
if (!columns || columns === "") return null; // Extract field names from column specifications
|
|
1812
|
+
// Handles: "field", "`field`", "table.field", "`table`.`field`", "expr AS alias"
|
|
1813
|
+
|
|
1814
|
+
var fields = new Set();
|
|
1815
|
+
var columnList = columns.split(",").map(c => c.trim());
|
|
1816
|
+
|
|
1817
|
+
for (var col of columnList) {
|
|
1818
|
+
// Check for AS alias
|
|
1819
|
+
var aliasMatch = col.match(/\s+AS\s+`?([\w]+)`?$/i);
|
|
1820
|
+
|
|
1821
|
+
if (aliasMatch) {
|
|
1822
|
+
fields.add(aliasMatch[1]);
|
|
1823
|
+
continue;
|
|
1824
|
+
} // Get the last part after dots (the field name)
|
|
1825
|
+
|
|
1826
|
+
|
|
1827
|
+
var parts = col.replace(/`/g, "").split(".");
|
|
1828
|
+
var fieldName = parts[parts.length - 1];
|
|
1829
|
+
|
|
1830
|
+
if (fieldName && fieldName !== "*") {
|
|
1831
|
+
fields.add(fieldName);
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
return fields.size > 0 ? fields : null;
|
|
1789
1836
|
}
|
|
1790
1837
|
|
|
1791
1838
|
do_serialize(data, type, old_data, args, path_to_be_removed) {
|
|
@@ -1817,14 +1864,24 @@ class SpiceModel {
|
|
|
1817
1864
|
_this18.addExternalModifiers(_this18.type);
|
|
1818
1865
|
|
|
1819
1866
|
_this18[_external_modifier_loaded] = true;
|
|
1820
|
-
} //
|
|
1867
|
+
} // ⚡ OPTIMIZED: Parse requested columns for selective modifier execution
|
|
1868
|
+
|
|
1869
|
+
|
|
1870
|
+
var requestedColumns = _this18.parseRequestedColumns(args == null ? void 0 : args.columns); // Cache the modifiers lookup for the specified type.
|
|
1821
1871
|
|
|
1822
1872
|
|
|
1823
1873
|
var modifiers = ((_this18$_serializers = _this18[_serializers]) == null ? void 0 : (_this18$_serializers$ = _this18$_serializers[type]) == null ? void 0 : _this18$_serializers$.modifiers) || [];
|
|
1824
1874
|
|
|
1825
1875
|
for (var modifier of modifiers) {
|
|
1826
1876
|
try {
|
|
1827
|
-
|
|
1877
|
+
// ⚡ OPTIMIZED: Skip field-specific modifiers if columns specified and field not requested
|
|
1878
|
+
if (requestedColumns && modifier.field && !requestedColumns.has(modifier.field)) {
|
|
1879
|
+
continue; // Skip this modifier - field not in requested columns
|
|
1880
|
+
} // Execute modifier (supports both old function format and new object format)
|
|
1881
|
+
|
|
1882
|
+
|
|
1883
|
+
var executeFn = typeof modifier === "function" ? modifier : modifier.execute;
|
|
1884
|
+
data = yield executeFn(data, old_data, _this18[_ctx], _this18.type);
|
|
1828
1885
|
} catch (error) {
|
|
1829
1886
|
console.error("Modifier error in do_serialize:", error.stack);
|
|
1830
1887
|
}
|
package/package.json
CHANGED
package/src/models/SpiceModel.js
CHANGED
|
@@ -20,7 +20,8 @@ var SDate = require("sonover-date"),
|
|
|
20
20
|
_skip_cache = Symbol(),
|
|
21
21
|
_serializers = Symbol(),
|
|
22
22
|
_level = Symbol(),
|
|
23
|
-
_mapping_dept = Symbol()
|
|
23
|
+
_mapping_dept = Symbol(),
|
|
24
|
+
_mapping_dept_exempt = Symbol();
|
|
24
25
|
|
|
25
26
|
//const _type = Symbol("type");
|
|
26
27
|
|
|
@@ -52,6 +53,7 @@ export default class SpiceModel {
|
|
|
52
53
|
spice.config.database.connections[args.connection].type || "couchbase";
|
|
53
54
|
let Database = require(`spice-${dbtype}`);
|
|
54
55
|
this[_mapping_dept] = args?.args?.mapping_dept || 100;
|
|
56
|
+
this[_mapping_dept_exempt] = args?.args?.mapping_dept_exempt || [];
|
|
55
57
|
this.type = "";
|
|
56
58
|
this.collection = args.connection;
|
|
57
59
|
this[_args] = args.args;
|
|
@@ -491,6 +493,8 @@ export default class SpiceModel {
|
|
|
491
493
|
|
|
492
494
|
const doGet = async () => {
|
|
493
495
|
if (args.mapping_dept) this[_mapping_dept] = args.mapping_dept;
|
|
496
|
+
if (args.mapping_dept_exempt)
|
|
497
|
+
this[_mapping_dept_exempt] = args.mapping_dept_exempt;
|
|
494
498
|
|
|
495
499
|
if (!args) {
|
|
496
500
|
args = {};
|
|
@@ -499,7 +503,8 @@ export default class SpiceModel {
|
|
|
499
503
|
if (args.skip_hooks !== true) {
|
|
500
504
|
await this.run_hook(args, "get", "before");
|
|
501
505
|
}
|
|
502
|
-
|
|
506
|
+
// ⚡ Include columns in cache key if specified
|
|
507
|
+
let key = `get::${this.type}::${args.id}${args.columns ? `::${args.columns}` : ""}`;
|
|
503
508
|
let results = {};
|
|
504
509
|
|
|
505
510
|
if (this.shouldUseCache(this.type)) {
|
|
@@ -553,6 +558,7 @@ export default class SpiceModel {
|
|
|
553
558
|
args.skip_read_serialize !== true &&
|
|
554
559
|
args.skip_serialize !== true
|
|
555
560
|
) {
|
|
561
|
+
// ⚡ Pass columns to do_serialize so it can skip irrelevant modifiers
|
|
556
562
|
results = await this.do_serialize(
|
|
557
563
|
results,
|
|
558
564
|
"read",
|
|
@@ -561,6 +567,12 @@ export default class SpiceModel {
|
|
|
561
567
|
await this.propsToBeRemoved(results)
|
|
562
568
|
);
|
|
563
569
|
}
|
|
570
|
+
|
|
571
|
+
// ⚡ OPTIMIZED: Filter results by columns if specified
|
|
572
|
+
if (args.columns) {
|
|
573
|
+
results = this.filterResultsByColumns([results], args.columns)[0];
|
|
574
|
+
}
|
|
575
|
+
|
|
564
576
|
if (args.skip_hooks !== true) {
|
|
565
577
|
await this.run_hook(results, "get", "after");
|
|
566
578
|
}
|
|
@@ -1136,6 +1148,8 @@ export default class SpiceModel {
|
|
|
1136
1148
|
|
|
1137
1149
|
const doList = async () => {
|
|
1138
1150
|
if (args.mapping_dept) this[_mapping_dept] = args.mapping_dept;
|
|
1151
|
+
if (args.mapping_dept_exempt)
|
|
1152
|
+
this[_mapping_dept_exempt] = args.mapping_dept_exempt;
|
|
1139
1153
|
|
|
1140
1154
|
// Find alias tokens from query/columns/sort
|
|
1141
1155
|
const nestings = [
|
|
@@ -1409,9 +1423,8 @@ export default class SpiceModel {
|
|
|
1409
1423
|
if (!original_is_array) {
|
|
1410
1424
|
data = Array.of(data);
|
|
1411
1425
|
}
|
|
1412
|
-
this[
|
|
1413
|
-
|
|
1414
|
-
if (this[_level] + 1 < this[_mapping_dept]) {
|
|
1426
|
+
const isExempt = this[_mapping_dept_exempt].includes(source_property);
|
|
1427
|
+
if (isExempt || this[_level] + 1 < this[_mapping_dept]) {
|
|
1415
1428
|
let classes = _.compact(_.isArray(Class) ? Class : [Class]);
|
|
1416
1429
|
|
|
1417
1430
|
let ids = [];
|
|
@@ -1431,6 +1444,7 @@ export default class SpiceModel {
|
|
|
1431
1444
|
skip_cache: this[_skip_cache],
|
|
1432
1445
|
_level: this[_level] + 1,
|
|
1433
1446
|
mapping_dept: this[_mapping_dept],
|
|
1447
|
+
mapping_dept_exempt: this[_mapping_dept_exempt],
|
|
1434
1448
|
}).getMulti({
|
|
1435
1449
|
skip_hooks: true,
|
|
1436
1450
|
ids: ids,
|
|
@@ -1468,7 +1482,8 @@ export default class SpiceModel {
|
|
|
1468
1482
|
if (!original_is_array) {
|
|
1469
1483
|
data = Array.of(data);
|
|
1470
1484
|
}
|
|
1471
|
-
|
|
1485
|
+
const isExempt = this[_mapping_dept_exempt].includes(source_property);
|
|
1486
|
+
if (isExempt || this[_level] + 1 < this[_mapping_dept]) {
|
|
1472
1487
|
let ids = [];
|
|
1473
1488
|
_.each(data, (result) => {
|
|
1474
1489
|
let value = [];
|
|
@@ -1493,6 +1508,7 @@ export default class SpiceModel {
|
|
|
1493
1508
|
skip_cache: this[_skip_cache],
|
|
1494
1509
|
_level: this[_level] + 1,
|
|
1495
1510
|
mapping_dept: this[_mapping_dept],
|
|
1511
|
+
mapping_dept_exempt: this[_mapping_dept_exempt],
|
|
1496
1512
|
}).getMulti({
|
|
1497
1513
|
skip_hooks: true,
|
|
1498
1514
|
ids: ids,
|
|
@@ -1533,9 +1549,10 @@ export default class SpiceModel {
|
|
|
1533
1549
|
return original_is_array ? data : data[0];
|
|
1534
1550
|
}
|
|
1535
1551
|
|
|
1536
|
-
addModifier({ when, execute }) {
|
|
1552
|
+
addModifier({ when, execute, field = null }) {
|
|
1537
1553
|
if (this[_serializers][when]) {
|
|
1538
|
-
|
|
1554
|
+
// Store as object with field info for column-based filtering
|
|
1555
|
+
this[_serializers][when]["modifiers"].push({ execute, field });
|
|
1539
1556
|
}
|
|
1540
1557
|
}
|
|
1541
1558
|
|
|
@@ -1556,11 +1573,13 @@ export default class SpiceModel {
|
|
|
1556
1573
|
if (properties[i].map) {
|
|
1557
1574
|
switch (properties[i].map.type) {
|
|
1558
1575
|
case MapType.MODEL: {
|
|
1576
|
+
const destinationField = properties[i].map.destination || i;
|
|
1559
1577
|
switch (properties[i].type) {
|
|
1560
1578
|
case String:
|
|
1561
1579
|
case "string": {
|
|
1562
1580
|
this.addModifier({
|
|
1563
1581
|
when: properties[i].map.when || "read",
|
|
1582
|
+
field: destinationField, // ⚡ Track which field this modifier populates
|
|
1564
1583
|
execute: async (data) => {
|
|
1565
1584
|
return await this.mapToObject(
|
|
1566
1585
|
data,
|
|
@@ -1568,7 +1587,7 @@ export default class SpiceModel {
|
|
|
1568
1587
|
spice.models[properties[i].map.reference]
|
|
1569
1588
|
: properties[i].map.reference,
|
|
1570
1589
|
i,
|
|
1571
|
-
|
|
1590
|
+
destinationField,
|
|
1572
1591
|
properties[i]
|
|
1573
1592
|
);
|
|
1574
1593
|
},
|
|
@@ -1579,6 +1598,7 @@ export default class SpiceModel {
|
|
|
1579
1598
|
case "array": {
|
|
1580
1599
|
this.addModifier({
|
|
1581
1600
|
when: properties[i].map.when || "read",
|
|
1601
|
+
field: destinationField, // ⚡ Track which field this modifier populates
|
|
1582
1602
|
execute: async (data) => {
|
|
1583
1603
|
return await this.mapToObjectArray(
|
|
1584
1604
|
data,
|
|
@@ -1586,7 +1606,7 @@ export default class SpiceModel {
|
|
|
1586
1606
|
spice.models[properties[i].map.reference]
|
|
1587
1607
|
: properties[i].map.reference,
|
|
1588
1608
|
i,
|
|
1589
|
-
|
|
1609
|
+
destinationField,
|
|
1590
1610
|
properties[i]
|
|
1591
1611
|
);
|
|
1592
1612
|
},
|
|
@@ -1604,6 +1624,34 @@ export default class SpiceModel {
|
|
|
1604
1624
|
}
|
|
1605
1625
|
}
|
|
1606
1626
|
|
|
1627
|
+
// ⚡ Parse columns string into a Set for fast lookup
|
|
1628
|
+
parseRequestedColumns(columns) {
|
|
1629
|
+
if (!columns || columns === "") return null;
|
|
1630
|
+
|
|
1631
|
+
// Extract field names from column specifications
|
|
1632
|
+
// Handles: "field", "`field`", "table.field", "`table`.`field`", "expr AS alias"
|
|
1633
|
+
const fields = new Set();
|
|
1634
|
+
const columnList = columns.split(",").map((c) => c.trim());
|
|
1635
|
+
|
|
1636
|
+
for (const col of columnList) {
|
|
1637
|
+
// Check for AS alias
|
|
1638
|
+
const aliasMatch = col.match(/\s+AS\s+`?([\w]+)`?$/i);
|
|
1639
|
+
if (aliasMatch) {
|
|
1640
|
+
fields.add(aliasMatch[1]);
|
|
1641
|
+
continue;
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
// Get the last part after dots (the field name)
|
|
1645
|
+
const parts = col.replace(/`/g, "").split(".");
|
|
1646
|
+
const fieldName = parts[parts.length - 1];
|
|
1647
|
+
if (fieldName && fieldName !== "*") {
|
|
1648
|
+
fields.add(fieldName);
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
return fields.size > 0 ? fields : null;
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1607
1655
|
async do_serialize(data, type, old_data, args, path_to_be_removed = []) {
|
|
1608
1656
|
// Profiling: use track() for proper async context forking
|
|
1609
1657
|
const p = this[_ctx]?.profiler;
|
|
@@ -1620,11 +1668,26 @@ export default class SpiceModel {
|
|
|
1620
1668
|
this[_external_modifier_loaded] = true;
|
|
1621
1669
|
}
|
|
1622
1670
|
|
|
1671
|
+
// ⚡ OPTIMIZED: Parse requested columns for selective modifier execution
|
|
1672
|
+
const requestedColumns = this.parseRequestedColumns(args?.columns);
|
|
1673
|
+
|
|
1623
1674
|
// Cache the modifiers lookup for the specified type.
|
|
1624
1675
|
const modifiers = this[_serializers]?.[type]?.modifiers || [];
|
|
1625
1676
|
for (const modifier of modifiers) {
|
|
1626
1677
|
try {
|
|
1627
|
-
|
|
1678
|
+
// ⚡ OPTIMIZED: Skip field-specific modifiers if columns specified and field not requested
|
|
1679
|
+
if (
|
|
1680
|
+
requestedColumns &&
|
|
1681
|
+
modifier.field &&
|
|
1682
|
+
!requestedColumns.has(modifier.field)
|
|
1683
|
+
) {
|
|
1684
|
+
continue; // Skip this modifier - field not in requested columns
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
// Execute modifier (supports both old function format and new object format)
|
|
1688
|
+
const executeFn =
|
|
1689
|
+
typeof modifier === "function" ? modifier : modifier.execute;
|
|
1690
|
+
data = await executeFn(data, old_data, this[_ctx], this.type);
|
|
1628
1691
|
} catch (error) {
|
|
1629
1692
|
console.error("Modifier error in do_serialize:", error.stack);
|
|
1630
1693
|
}
|