mongoose 9.9.2 → 9.9.3

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.
@@ -30,6 +30,12 @@ module.exports = function castDouble(val) {
30
30
  assert.ok(false);
31
31
  }
32
32
  } else if (typeof val === 'object') {
33
+ if (Array.isArray(val)) {
34
+ // `[5].valueOf()` returns the array itself, so without this guard a
35
+ // single-element or empty array would fall through to `Number(tempVal)`
36
+ // below and silently coerce instead of throwing.
37
+ assert.ok(false);
38
+ }
33
39
  const tempVal = val.valueOf() ?? val.toString();
34
40
  // ex: { a: 'im an object, valueOf: () => 'helloworld' } // throw an error
35
41
  if (typeof tempVal === 'string') {
package/lib/cast/int32.js CHANGED
@@ -20,6 +20,12 @@ module.exports = function castInt32(val) {
20
20
  if (val === '') {
21
21
  return null;
22
22
  }
23
+ if (Array.isArray(val)) {
24
+ // `Number([5])` is `5` and `Number([])` is `0`, so without this guard a
25
+ // single-element or empty array would silently coerce instead of throwing,
26
+ // unlike every other array input and unlike `castNumber()`'s own guard.
27
+ assert.ok(false);
28
+ }
23
29
 
24
30
  const coercedVal = isBsonType(val, 'Long') ? val.toNumber() : Number(val);
25
31
 
package/lib/document.js CHANGED
@@ -2570,25 +2570,15 @@ Document.prototype.isSelected = function isSelected(path) {
2570
2570
  return path.some(p => this.$__isSelected(p));
2571
2571
  }
2572
2572
 
2573
- const paths = Object.keys(this.$__.selected);
2574
- let inclusive = null;
2573
+ const index = _getProjectionIndex(this);
2574
+ const paths = index.paths;
2575
+ const inclusive = index.inclusive;
2575
2576
 
2576
- if (paths.length === 1 && paths[0] === '_id') {
2577
+ if (index.onlyId) {
2577
2578
  // only _id was selected.
2578
2579
  return this.$__.selected._id === 0;
2579
2580
  }
2580
2581
 
2581
- for (const cur of paths) {
2582
- if (cur === '_id') {
2583
- continue;
2584
- }
2585
- if (!isDefiningProjection(this.$__.selected[cur])) {
2586
- continue;
2587
- }
2588
- inclusive = !!this.$__.selected[cur];
2589
- break;
2590
- }
2591
-
2592
2582
  if (inclusive === null) {
2593
2583
  return true;
2594
2584
  }
@@ -2599,6 +2589,25 @@ Document.prototype.isSelected = function isSelected(path) {
2599
2589
 
2600
2590
  const pathHasDot = path.indexOf('.') !== -1;
2601
2591
 
2592
+ if (!index.hasNestedKey) {
2593
+ // No projection key is nested, so no key can start with `path + '.'` and
2594
+ // only the "is an ancestor of `path` projected?" check below can match.
2595
+ // Every ancestor that matches returns the same value, so we can walk
2596
+ // `path`'s ancestors instead of scanning every projection key. This keeps
2597
+ // `isSelected()` O(depth of path) rather than O(number of projected paths),
2598
+ // which matters because `toObject({ getters: true })` calls this once per
2599
+ // schema path. Re: gh-16373
2600
+ if (pathHasDot) {
2601
+ for (let dot = path.indexOf('.'); dot !== -1; dot = path.indexOf('.', dot + 1)) {
2602
+ const ancestor = path.slice(0, dot);
2603
+ if (ancestor !== '_id' && Object.hasOwn(this.$__.selected, ancestor)) {
2604
+ return inclusive;
2605
+ }
2606
+ }
2607
+ }
2608
+ return !inclusive;
2609
+ }
2610
+
2602
2611
  for (const cur of paths) {
2603
2612
  if (cur === '_id') {
2604
2613
  continue;
@@ -2615,6 +2624,43 @@ Document.prototype.isSelected = function isSelected(path) {
2615
2624
  return !inclusive;
2616
2625
  };
2617
2626
 
2627
+ /*!
2628
+ * Computes and caches the derived properties of `doc.$__.selected` that
2629
+ * `isSelected()` and `isDirectSelected()` would otherwise recompute on every
2630
+ * call. `$__.selected` is assigned once in the Document constructor and never
2631
+ * mutated afterwards, so this can safely be cached for the document's lifetime.
2632
+ */
2633
+
2634
+ function _getProjectionIndex(doc) {
2635
+ if (doc.$__.selectedIndex !== undefined) {
2636
+ return doc.$__.selectedIndex;
2637
+ }
2638
+
2639
+ const selected = doc.$__.selected;
2640
+ const paths = Object.keys(selected);
2641
+ let inclusive = null;
2642
+ let hasNestedKey = false;
2643
+
2644
+ for (const cur of paths) {
2645
+ if (cur === '_id') {
2646
+ continue;
2647
+ }
2648
+ if (cur.indexOf('.') !== -1) {
2649
+ hasNestedKey = true;
2650
+ }
2651
+ if (inclusive === null && isDefiningProjection(selected[cur])) {
2652
+ inclusive = !!selected[cur];
2653
+ }
2654
+ }
2655
+
2656
+ return (doc.$__.selectedIndex = {
2657
+ paths,
2658
+ inclusive,
2659
+ hasNestedKey,
2660
+ onlyId: paths.length === 1 && paths[0] === '_id'
2661
+ });
2662
+ }
2663
+
2618
2664
  Document.prototype.$__isSelected = Document.prototype.isSelected;
2619
2665
 
2620
2666
  /**
@@ -2650,34 +2696,22 @@ Document.prototype.isDirectSelected = function isDirectSelected(path) {
2650
2696
  return path.some(p => this.isDirectSelected(p));
2651
2697
  }
2652
2698
 
2653
- const paths = Object.keys(this.$__.selected);
2654
- let inclusive = null;
2699
+ const index = _getProjectionIndex(this);
2655
2700
 
2656
- if (paths.length === 1 && paths[0] === '_id') {
2701
+ if (index.onlyId) {
2657
2702
  // only _id was selected.
2658
2703
  return this.$__.selected._id === 0;
2659
2704
  }
2660
2705
 
2661
- for (const cur of paths) {
2662
- if (cur === '_id') {
2663
- continue;
2664
- }
2665
- if (!isDefiningProjection(this.$__.selected[cur])) {
2666
- continue;
2667
- }
2668
- inclusive = !!this.$__.selected[cur];
2669
- break;
2670
- }
2671
-
2672
- if (inclusive === null) {
2706
+ if (index.inclusive === null) {
2673
2707
  return true;
2674
2708
  }
2675
2709
 
2676
2710
  if (Object.hasOwn(this.$__.selected, path)) {
2677
- return inclusive;
2711
+ return index.inclusive;
2678
2712
  }
2679
2713
 
2680
- return !inclusive;
2714
+ return !index.inclusive;
2681
2715
  };
2682
2716
 
2683
2717
  /**
package/lib/internal.js CHANGED
@@ -17,6 +17,8 @@ InternalCache.prototype.strictMode = true;
17
17
 
18
18
  InternalCache.prototype.fullPath = undefined;
19
19
  InternalCache.prototype.selected = undefined;
20
+ // Lazily computed index over `selected`, see `_getProjectionIndex()` in document.js
21
+ InternalCache.prototype.selectedIndex = undefined;
20
22
  InternalCache.prototype.shardval = undefined;
21
23
  InternalCache.prototype.saveError = undefined;
22
24
  InternalCache.prototype.validationError = undefined;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mongoose",
3
3
  "description": "Mongoose MongoDB ODM",
4
- "version": "9.9.2",
4
+ "version": "9.9.3",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",