Skip to content

Commit 7c36396

Browse files
Cache Hammbrettz9
authored andcommitted
- Refactory fix for wrap:false to not incur performance cost
1 parent 59b33b0 commit 7c36396

File tree

9 files changed

+77
-188
lines changed

9 files changed

+77
-188
lines changed

dist/index-es.js

Lines changed: 18 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,7 @@ JSONPath.prototype.evaluate = function (expr, json, callback, otherTypeCallback)
480480
return wrap ? [] : undefined;
481481
}
482482

483-
if (!wrap && this.isSingularResult(result, exprList)) {
483+
if (!wrap && result.length === 1 && !result[0].hasArrExpr) {
484484
return this._getPreferredOutput(result[0]);
485485
}
486486

@@ -522,52 +522,6 @@ JSONPath.prototype._getPreferredOutput = function (ea) {
522522
return JSONPath.toPointer(ea.path);
523523
}
524524
};
525-
/**
526-
* Detect filter expressions.
527-
* @param {string}loc
528-
* @returns {boolean}
529-
*/
530-
531-
532-
JSONPath.prototype.isFilterExpr = function (loc) {
533-
return loc.indexOf('?(') === 0;
534-
};
535-
/**
536-
* Detects operators in the expression list that require an array result.
537-
* an array of results. If no such operator exists, the result
538-
* will be treated as a singular value.
539-
*
540-
* For example, the following paths reference singular results:
541-
* "store.book[0]" - specific book
542-
* "store.bicycle.red" - single property of a single object
543-
*
544-
* Conversely, the following paths will always result in an array,
545-
* because they can generate multiple results depending on the dataset:
546-
* $.store.book[0][category,author] - category,author will return 2 values
547-
* $..book - ".." will recurse through the store object
548-
* $.store.book[1:2] - indicates a range within the array
549-
* $.store.book[*] - wild card indicates multiple results
550-
* $.store.book[?(@.isbn)] - filtering
551-
*/
552-
553-
/**
554-
* @param {PlainObject} result - json path result
555-
* @param {array} exprList - array of json path expressions
556-
* @returns {boolean}
557-
*/
558-
559-
560-
JSONPath.prototype.isSingularResult = function (result, exprList) {
561-
var _this2 = this;
562-
563-
return result.length === 1 && !exprList.includes('*') && !exprList.includes('..') && exprList.every(function (loc) {
564-
return !_this2.isFilterExpr(loc);
565-
}) && exprList.every(function (loc) {
566-
return !loc.includes(',');
567-
}) && exprList.every(function (loc) {
568-
return !loc.includes(':');
569-
});
570-
};
571525

572526
JSONPath.prototype._handleCallback = function (fullRetObj, callback, type) {
573527
if (callback) {
@@ -587,11 +541,12 @@ JSONPath.prototype._handleCallback = function (fullRetObj, callback, type) {
587541
* @param {string} parentPropName
588542
* @param {JSONPathCallback} callback
589543
* @param {boolean} literalPriority
544+
* @param {boolean} hasArrExpr
590545
* @returns {ReturnObject|ReturnObject[]}
591546
*/
592547

593548

594-
JSONPath.prototype._trace = function (expr, val, path, parent, parentPropName, callback, literalPriority) {
549+
JSONPath.prototype._trace = function (expr, val, path, parent, parentPropName, callback, hasArrExpr, literalPriority) {
595550
// No expr to follow? return path and value as the result of
596551
// this trace branch
597552
var retObj;
@@ -602,7 +557,8 @@ JSONPath.prototype._trace = function (expr, val, path, parent, parentPropName, c
602557
path: path,
603558
value: val,
604559
parent: parent,
605-
parentProperty: parentPropName
560+
parentProperty: parentPropName,
561+
hasArrExpr: hasArrExpr
606562
};
607563

608564
this._handleCallback(retObj, callback, 'value');
@@ -636,24 +592,24 @@ JSONPath.prototype._trace = function (expr, val, path, parent, parentPropName, c
636592

637593
if ((typeof loc !== 'string' || literalPriority) && val && hasOwnProp.call(val, loc)) {
638594
// simple case--directly follow property
639-
addRet(this._trace(x, val[loc], push(path, loc), val, loc, callback));
595+
addRet(this._trace(x, val[loc], push(path, loc), val, loc, callback, hasArrExpr));
640596
} else if (loc === '*') {
641597
// all child properties
642598
this._walk(loc, x, val, path, parent, parentPropName, callback, function (m, l, _x, v, p, par, pr, cb) {
643-
addRet(that._trace(unshift(m, _x), v, p, par, pr, cb, true));
599+
addRet(that._trace(unshift(m, _x), v, p, par, pr, cb, true, true));
644600
});
645601
} else if (loc === '..') {
646602
// all descendent parent properties
647603
// Check remaining expression with val's immediate children
648-
addRet(this._trace(x, val, path, parent, parentPropName, callback));
604+
addRet(this._trace(x, val, path, parent, parentPropName, callback, hasArrExpr));
649605

650606
this._walk(loc, x, val, path, parent, parentPropName, callback, function (m, l, _x, v, p, par, pr, cb) {
651607
// We don't join m and x here because we only want parents,
652608
// not scalar values
653609
if (_typeof(v[m]) === 'object') {
654610
// Keep going with recursive descent on val's
655611
// object children
656-
addRet(that._trace(unshift(l, _x), v[m], push(p, m), v, m, cb));
612+
addRet(that._trace(unshift(l, _x), v[m], push(p, m), v, m, cb, true));
657613
}
658614
}); // The parent sel computation is handled in the frame above using the
659615
// ancestor object of val
@@ -680,19 +636,19 @@ JSONPath.prototype._trace = function (expr, val, path, parent, parentPropName, c
680636
return retObj;
681637
} else if (loc === '$') {
682638
// root only
683-
addRet(this._trace(x, val, path, null, null, callback));
639+
addRet(this._trace(x, val, path, null, null, callback, hasArrExpr));
684640
} else if (/^(\x2D?[0-9]*):(\x2D?[0-9]*):?([0-9]*)$/.test(loc)) {
685641
// [start:end:step] Python slice syntax
686642
addRet(this._slice(loc, x, val, path, parent, parentPropName, callback));
687-
} else if (this.isFilterExpr(loc)) {
643+
} else if (loc.indexOf('?(') === 0) {
688644
// [?(expr)] (filtering)
689645
if (this.currPreventEval) {
690646
throw new Error('Eval [?(expr)] prevented in JSONPath expression.');
691647
}
692648

693649
this._walk(loc, x, val, path, parent, parentPropName, callback, function (m, l, _x, v, p, par, pr, cb) {
694650
if (that._eval(l.replace(/^\?\(((?:[\0-\t\x0B\f\x0E-\u2027\u202A-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*?)\)$/, '$1'), v[m], m, p, par, pr)) {
695-
addRet(that._trace(unshift(m, _x), v, p, par, pr, cb));
651+
addRet(that._trace(unshift(m, _x), v, p, par, pr, cb, true));
696652
}
697653
});
698654
} else if (loc[0] === '(') {
@@ -704,7 +660,7 @@ JSONPath.prototype._trace = function (expr, val, path, parent, parentPropName, c
704660
// parent of the property to which this expression will resolve
705661

706662

707-
addRet(this._trace(unshift(this._eval(loc, val, path[path.length - 1], path.slice(0, -1), parent, parentPropName), x), val, path, parent, parentPropName, callback));
663+
addRet(this._trace(unshift(this._eval(loc, val, path[path.length - 1], path.slice(0, -1), parent, parentPropName), x), val, path, parent, parentPropName, callback, hasArrExpr));
708664
} else if (loc[0] === '@') {
709665
// value type: @boolean(), etc.
710666
var addType = false;
@@ -796,7 +752,7 @@ JSONPath.prototype._trace = function (expr, val, path, parent, parentPropName, c
796752

797753
} else if (loc[0] === '`' && val && hasOwnProp.call(val, loc.slice(1))) {
798754
var locProp = loc.slice(1);
799-
addRet(this._trace(x, val[locProp], push(path, locProp), val, locProp, callback, true));
755+
addRet(this._trace(x, val[locProp], push(path, locProp), val, locProp, callback, hasArrExpr, true));
800756
} else if (loc.includes(',')) {
801757
// [name1,name2,...]
802758
var parts = loc.split(',');
@@ -807,7 +763,7 @@ JSONPath.prototype._trace = function (expr, val, path, parent, parentPropName, c
807763
try {
808764
for (var _iterator = parts[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
809765
var part = _step.value;
810-
addRet(this._trace(unshift(part, x), val, path, parent, parentPropName, callback));
766+
addRet(this._trace(unshift(part, x), val, path, parent, parentPropName, callback, true));
811767
} // simple case--directly follow property
812768

813769
} catch (err) {
@@ -825,7 +781,7 @@ JSONPath.prototype._trace = function (expr, val, path, parent, parentPropName, c
825781
}
826782
}
827783
} else if (!literalPriority && val && hasOwnProp.call(val, loc)) {
828-
addRet(this._trace(x, val[loc], push(path, loc), val, loc, callback, true));
784+
addRet(this._trace(x, val[loc], push(path, loc), val, loc, callback, hasArrExpr, true));
829785
} // We check the resulting values for parent selections. For parent
830786
// selections we discard the value object and continue the trace with the
831787
// current val object
@@ -836,7 +792,7 @@ JSONPath.prototype._trace = function (expr, val, path, parent, parentPropName, c
836792
var rett = ret[t];
837793

838794
if (rett.isParentSelector) {
839-
var tmp = that._trace(rett.expr, val, rett.path, parent, parentPropName, callback);
795+
var tmp = that._trace(rett.expr, val, rett.path, parent, parentPropName, callback, hasArrExpr);
840796

841797
if (Array.isArray(tmp)) {
842798
ret[t] = tmp[0];
@@ -887,7 +843,7 @@ JSONPath.prototype._slice = function (loc, expr, val, path, parent, parentPropNa
887843
var ret = [];
888844

889845
for (var i = start; i < end; i += step) {
890-
var tmp = this._trace(unshift(i, expr), val, path, parent, parentPropName, callback);
846+
var tmp = this._trace(unshift(i, expr), val, path, parent, parentPropName, callback, true);
891847

892848
if (Array.isArray(tmp)) {
893849
// This was causing excessive stack size in Node (with or

0 commit comments

Comments
 (0)