Skip to content

Commit 14db11d

Browse files
authored
Merge pull request #16088 from Automattic/vkarpov15/gh-16082
fix(model): handle passing string projection to hydrate()
2 parents 37ff792 + da1fc44 commit 14db11d

File tree

3 files changed

+29
-7
lines changed

3 files changed

+29
-7
lines changed

lib/helpers/projection/parseProjection.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
'use strict';
22

33
/**
4-
* Convert a string or array into a projection object, retaining all
5-
* `-` and `+` paths.
4+
* Convert a string or array into a projection object. Treats `-foo` as
5+
* equivalent to `foo: 0` depending on `retainMinusPaths`. If `retainMinusPaths`
6+
* is true, then `-foo` will be included in the projection as `'-foo': 0`.
7+
*
8+
* @param {object|string|string[]} v
9+
* @param {boolean} [retainMinusPaths]
10+
* @return {object}
611
*/
712

813
module.exports = function parseProjection(v, retainMinusPaths) {
@@ -22,9 +27,9 @@ module.exports = function parseProjection(v, retainMinusPaths) {
2227
if (!field) {
2328
continue;
2429
}
25-
const include = '-' == field[0] ? 0 : 1;
30+
const include = field.charAt(0) === '-' ? 0 : 1;
2631
if (!retainMinusPaths && include === 0) {
27-
field = field.substring(1);
32+
field = field.slice(1);
2833
}
2934
ret[field] = include;
3035
}

lib/model.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ const decorateDiscriminatorIndexOptions = require('./helpers/indexes/decorateDis
6161
const isPathSelectedInclusive = require('./helpers/projection/isPathSelectedInclusive');
6262
const leanPopulateMap = require('./helpers/populate/leanPopulateMap');
6363
const parallelLimit = require('./helpers/parallelLimit');
64+
const parseProjection = require('./helpers/projection/parseProjection');
6465
const prepareDiscriminatorPipeline = require('./helpers/aggregate/prepareDiscriminatorPipeline');
6566
const pushNestedArrayPaths = require('./helpers/model/pushNestedArrayPaths');
6667
const removeDeselectedForeignField = require('./helpers/populate/removeDeselectedForeignField');
@@ -3935,7 +3936,7 @@ Model.buildBulkWriteOperations = function buildBulkWriteOperations(documents, op
39353936
* @api public
39363937
*/
39373938

3938-
Model.hydrate = function(obj, projection, options) {
3939+
Model.hydrate = function hydrate(obj, projection, options) {
39393940
_checkContext(this, 'hydrate');
39403941

39413942
if (options?.virtuals && options?.hydratedPopulatedDocs === false) {
@@ -3946,6 +3947,7 @@ Model.hydrate = function(obj, projection, options) {
39463947
if (obj?.$__ != null) {
39473948
obj = obj.toObject(internalToObjectOptions);
39483949
}
3950+
projection = parseProjection(projection);
39493951
obj = applyProjection(obj, projection);
39503952
}
39513953
const document = require('./queryHelpers').createModel(this, obj, projection, projection, options);

test/model.hydrate.test.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,18 +79,33 @@ describe('model', function() {
7979
assert.deepEqual(['food'], Object.keys(err.errors));
8080
});
8181

82-
it('supports projection (gh-9209)', function() {
82+
it('supports projection (gh-16082) (gh-9209)', function() {
8383
const schema = new Schema({
8484
prop: String,
8585
arr: [String]
8686
});
8787
const Model = db.model('Test2', schema);
8888

89-
const doc = Model.hydrate({ prop: 'test' }, { arr: 0 });
89+
let doc = Model.hydrate({ prop: 'test' }, { arr: 0 });
90+
assert.equal(doc.isNew, false);
91+
assert.equal(doc.isModified(), false);
92+
assert.ok(!doc.$__delta());
93+
assert.strictEqual(doc.prop, 'test');
94+
// Array implicit default of `[]` shouldn't apply
95+
assert.strictEqual(doc.arr, undefined);
96+
97+
doc = Model.hydrate({ prop: 'test' }, '-arr');
98+
assert.equal(doc.isNew, false);
99+
assert.equal(doc.isModified(), false);
100+
assert.ok(!doc.$__delta());
101+
assert.strictEqual(doc.prop, 'test');
102+
assert.strictEqual(doc.arr, undefined);
90103

104+
doc = Model.hydrate({ prop: 'test' }, ['_id']);
91105
assert.equal(doc.isNew, false);
92106
assert.equal(doc.isModified(), false);
93107
assert.ok(!doc.$__delta());
108+
assert.ok(!doc.prop);
94109
});
95110

96111
it('works correctly with model discriminators', function() {

0 commit comments

Comments
 (0)