Skip to content

Commit 0f2f461

Browse files
authored
Merge pull request #12949 from Automattic/6.9
6.9
2 parents 3fc7054 + c6c30ed commit 0f2f461

File tree

10 files changed

+207
-92
lines changed

10 files changed

+207
-92
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ coverage
2020
npm-debug.log
2121
data/
2222
.nyc_output/
23-
23+
.env
2424
tools/31*
2525

2626
# Visual Studio

lib/cast.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,18 @@ module.exports = function cast(schema, obj, options, context) {
6464
if (!Array.isArray(val)) {
6565
throw new CastError('Array', val, path);
6666
}
67-
for (let k = 0; k < val.length; ++k) {
67+
for (let k = val.length - 1; k >= 0; k--) {
6868
if (val[k] == null || typeof val[k] !== 'object') {
6969
throw new CastError('Object', val[k], path + '.' + k);
7070
}
7171
val[k] = cast(schema, val[k], options, context);
72+
if (Object.keys(val[k]).length === 0) {
73+
val.splice(k, 1);
74+
}
75+
}
76+
77+
if (val.length === 0) {
78+
delete obj[path];
7279
}
7380
} else if (path === '$where') {
7481
type = typeof val;

lib/schema.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2251,6 +2251,32 @@ function _deletePath(schema, name) {
22512251
delete branch[last];
22522252
}
22532253

2254+
/**
2255+
*
2256+
* @param {String|Array} path The virutal path(s) to remove.
2257+
* @returns {Schema} the Schema instance, or a mongoose error if the virtual does not exist.
2258+
* @api public
2259+
*/
2260+
2261+
Schema.prototype.removeVirtual = function(path) {
2262+
if (typeof path === 'string') {
2263+
path = [path];
2264+
}
2265+
if (Array.isArray(path)) {
2266+
for (const virtual of path) {
2267+
if (this.virtuals[virtual] == null) {
2268+
throw new MongooseError(`Attempting to remove virtual "${virtual}" that does not exist.`);
2269+
}
2270+
}
2271+
2272+
for (const virtual of path) {
2273+
delete this.paths[virtual];
2274+
delete this.virtuals[virtual];
2275+
}
2276+
}
2277+
return this;
2278+
};
2279+
22542280
/**
22552281
* Loads an ES6 class into a schema. Maps [setters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set) + [getters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get), [static methods](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/static),
22562282
* and [instance methods](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#Class_body_and_method_definitions)

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"dependencies": {
2222
"bson": "^4.7.0",
2323
"kareem": "2.5.1",
24-
"mongodb": "4.12.1",
24+
"mongodb": "4.13.0",
2525
"mpath": "0.9.0",
2626
"mquery": "4.0.3",
2727
"ms": "2.1.3",

test/docs/cast.test.js

Lines changed: 46 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -101,40 +101,58 @@ describe('Cast Tutorial', function() {
101101
await query.exec();
102102
});
103103

104-
it('strictQuery true', async function() {
105-
mongoose.deleteModel('Character');
106-
const schema = new mongoose.Schema({ name: String, age: Number }, {
107-
strictQuery: true
104+
describe('strictQuery', function() {
105+
it('strictQuery true - simple object', async function() {
106+
mongoose.deleteModel('Character');
107+
const schema = new mongoose.Schema({ name: String, age: Number }, {
108+
strictQuery: true
109+
});
110+
Character = mongoose.model('Character', schema);
111+
112+
const query = Character.findOne({ notInSchema: { $lt: 'not a number' } });
113+
114+
await query.exec();
115+
query.getFilter(); // Empty object `{}`, Mongoose removes `notInSchema`
116+
// acquit:ignore:start
117+
assert.deepEqual(query.getFilter(), {});
118+
// acquit:ignore:end
108119
});
109-
Character = mongoose.model('Character', schema);
110120

111-
const query = Character.findOne({ notInSchema: { $lt: 'not a number' } });
121+
it('strictQuery true - conditions', async function() {
122+
mongoose.deleteModel('Character');
123+
const schema = new mongoose.Schema({ name: String, age: Number }, {
124+
strictQuery: true
125+
});
126+
Character = mongoose.model('Character', schema);
112127

113-
await query.exec();
114-
query.getFilter(); // Empty object `{}`, Mongoose removes `notInSchema`
115-
// acquit:ignore:start
116-
assert.deepEqual(query.getFilter(), {});
117-
// acquit:ignore:end
118-
});
128+
const query = Character.findOne({ $or: [{ notInSchema: { $lt: 'not a number' } }], $and: [{ name: 'abc' }, { age: { $gt: 18 } }, { notInSchema: { $lt: 'not a number' } }] });
119129

120-
it('strictQuery throw', async function() {
121-
mongoose.deleteModel('Character');
122-
const schema = new mongoose.Schema({ name: String, age: Number }, {
123-
strictQuery: 'throw'
130+
await query.exec();
131+
query.getFilter(); // Empty object `{}`, Mongoose removes `notInSchema`
132+
// acquit:ignore:start
133+
assert.deepEqual(query.getFilter(), { $and: [{ name: 'abc' }, { age: { $gt: 18 } }] });
134+
// acquit:ignore:end
124135
});
125-
Character = mongoose.model('Character', schema);
126136

127-
const query = Character.findOne({ notInSchema: { $lt: 'not a number' } });
128-
129-
const err = await query.exec().then(() => null, err => err);
130-
err.name; // 'StrictModeError'
131-
// Path "notInSchema" is not in schema and strictQuery is 'throw'.
132-
err.message;
133-
// acquit:ignore:start
134-
assert.equal(err.name, 'StrictModeError');
135-
assert.equal(err.message, 'Path "notInSchema" is not in schema and ' +
136-
'strictQuery is \'throw\'.');
137-
// acquit:ignore:end
137+
it('strictQuery throw', async function() {
138+
mongoose.deleteModel('Character');
139+
const schema = new mongoose.Schema({ name: String, age: Number }, {
140+
strictQuery: 'throw'
141+
});
142+
Character = mongoose.model('Character', schema);
143+
144+
const query = Character.findOne({ notInSchema: { $lt: 'not a number' } });
145+
146+
const err = await query.exec().then(() => null, err => err);
147+
err.name; // 'StrictModeError'
148+
// Path "notInSchema" is not in schema and strictQuery is 'throw'.
149+
err.message;
150+
// acquit:ignore:start
151+
assert.equal(err.name, 'StrictModeError');
152+
assert.equal(err.message, 'Path "notInSchema" is not in schema and ' +
153+
'strictQuery is \'throw\'.');
154+
// acquit:ignore:end
155+
});
138156
});
139157

140158
it('implicit in', async function() {

test/schema.test.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2910,4 +2910,63 @@ describe('schema', function() {
29102910

29112911
assert.equal(schema._getSchema('child.testMap.foo.bar').instance, 'Mixed');
29122912
});
2913+
2914+
it('should allow deleting a virtual path off the schema gh-8397', async function() {
2915+
const schema = new Schema({
2916+
name: String
2917+
}, {
2918+
virtuals: {
2919+
foo: {
2920+
get() {
2921+
return 42;
2922+
}
2923+
}
2924+
}
2925+
});
2926+
assert.ok(schema.virtuals.foo);
2927+
schema.removeVirtual('foo');
2928+
assert.ok(!schema.virtuals.foo);
2929+
const Test = db.model('gh-8397', schema);
2930+
const doc = new Test({ name: 'Test' });
2931+
assert.equal(doc.foo, undefined);
2932+
});
2933+
2934+
it('should allow deleting multiple virtuals gh-8397', async function() {
2935+
const schema = new Schema({
2936+
name: String
2937+
}, {
2938+
virtuals: {
2939+
foo: {
2940+
get() {
2941+
return 42;
2942+
}
2943+
},
2944+
bar: {
2945+
get() {
2946+
return 41;
2947+
}
2948+
}
2949+
}
2950+
});
2951+
assert.ok(schema.virtuals.foo);
2952+
assert.ok(schema.virtuals.bar);
2953+
schema.removeVirtual(['foo', 'bar']);
2954+
assert.ok(!schema.virtuals.foo);
2955+
assert.ok(!schema.virtuals.bar);
2956+
const Test = db.model('gh-8397', schema);
2957+
const doc = new Test({ name: 'Test' });
2958+
assert.equal(doc.foo, undefined);
2959+
assert.equal(doc.bar, undefined);
2960+
});
2961+
2962+
it('should throw an error if attempting to delete a virtual path that does not exist gh-8397', function() {
2963+
const schema = new Schema({
2964+
name: String
2965+
});
2966+
assert.ok(!schema.virtuals.foo);
2967+
assert.throws(() => {
2968+
schema.removeVirtual('foo');
2969+
}, { message: 'Attempting to remove virtual "foo" that does not exist.' });
2970+
2971+
});
29132972
});

test/types/models.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,7 @@ function gh12100() {
466466
const TestModel = model('test', schema_with_string_id);
467467
const obj = new TestModel();
468468

469-
expectType<string>(obj._id);
469+
expectType<string | null>(obj._id);
470470
})();
471471

472472
(async function gh12094() {

test/types/queries.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ async function gh12342_manual() {
411411

412412
async function gh12342_auto() {
413413
interface Project {
414-
name?: string, stars?: number
414+
name?: string | null, stars?: number | null
415415
}
416416

417417
const ProjectSchema = new Schema({

0 commit comments

Comments
 (0)