Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Infer schema type automatically. #11487

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
b7f70f8
add tsafe package to devDependencies
mohammad0-0ahmad Mar 6, 2022
8c891e4
create utilities-types helps to infer schema type
mohammad0-0ahmad Mar 6, 2022
3c2fd6b
Make some changes that obtain schema type & create some tests
mohammad0-0ahmad Mar 6, 2022
c6b6324
Remove tsafe & rewrite related tests
mohammad0-0ahmad Mar 7, 2022
9f30615
Reimplement auto DocType inference
mohammad0-0ahmad Mar 7, 2022
cc33fc2
Merge branch 'Automattic:master' into master
mohammad0-0ahmad Mar 7, 2022
d5f11c7
Make Model instance show correct type instead of "any"
mohammad0-0ahmad Mar 7, 2022
083ce6f
Refact ApplyBasicQueryCasting type to make schema properties types vi…
mohammad0-0ahmad Mar 7, 2022
f33b24a
Make schema props type visible
mohammad0-0ahmad Mar 7, 2022
c3ae6c5
Make InferredSchemaType support nested schemas
mohammad0-0ahmad Mar 7, 2022
b7152f7
Make statics functions auto typed by supplying them to schema options
mohammad0-0ahmad Mar 7, 2022
e6aa4f1
Merge https://github.com/Automattic/mongoose
mohammad0-0ahmad Mar 7, 2022
92fc04a
Merge branch 'Automattic:master'
mohammad0-0ahmad Mar 7, 2022
d5cbee3
Refact Schema & Model types
mohammad0-0ahmad Mar 9, 2022
11b6bed
Refact some infer-doc-type import lines
mohammad0-0ahmad Mar 9, 2022
6020771
Create some tests for Model functions
mohammad0-0ahmad Mar 10, 2022
d5059d5
Infer document instance methods from methods object supplied in schem…
mohammad0-0ahmad Mar 10, 2022
8fe4262
Use auto infered docType as default type every where
mohammad0-0ahmad Mar 10, 2022
9f4b974
Improve ResolvePathType ability to resolve correct type of types stri…
mohammad0-0ahmad Mar 11, 2022
4a5d3fa
create & implement FlexibleObject & refact typos
mohammad0-0ahmad Mar 12, 2022
a57b871
create & implement FlexibleObject & refact typos
mohammad0-0ahmad Mar 12, 2022
e5e42b7
Merge branch 'master' of https://github.com/mohammad0-0ahmad-forks/mo…
mohammad0-0ahmad Mar 12, 2022
6d4b08a
Merge branch 'master' of https://github.com/Automattic/mongoose
mohammad0-0ahmad Mar 12, 2022
3e23cae
Delete AnyKeys type & refact create & insertMany types
mohammad0-0ahmad Mar 12, 2022
15a9213
Support obtaining enum type
mohammad0-0ahmad Mar 13, 2022
638a0b6
Support custom typeKey
mohammad0-0ahmad Mar 13, 2022
c938e1e
Refact previous commit
mohammad0-0ahmad Mar 13, 2022
ce0f299
Provide JSDoc comments for inferschematype types.
mohammad0-0ahmad Mar 22, 2022
bc62a77
Merge branch 'master' of https://github.com/Automattic/mongoose
mohammad0-0ahmad Mar 23, 2022
6b424d4
Merge branch 'master' of https://github.com/Automattic/mongoose
mohammad0-0ahmad Mar 23, 2022
8fe3bd2
chore: release 6.2.8
vkarpov15 Mar 23, 2022
2df2e8b
test: add coverage for #11480
vkarpov15 Mar 23, 2022
dc1ede2
date expires option - add unit, behavior, example
boly38 Mar 23, 2022
204495c
fix typo
AbdelrahmanHafez Mar 23, 2022
b621612
Merge pull request #11557 from boly38/patch-1
AbdelrahmanHafez Mar 23, 2022
5127024
perf(document+model): make a few small optimizations re: #11380
vkarpov15 Mar 23, 2022
7f7735c
quick fix re: #103800
vkarpov15 Mar 23, 2022
f4a112b
style: fix lint re: #11380
vkarpov15 Mar 23, 2022
93dd492
add tsafe package to devDependencies
mohammad0-0ahmad Mar 6, 2022
2743a05
create utilities-types helps to infer schema type
mohammad0-0ahmad Mar 6, 2022
4ea70f0
Make some changes that obtain schema type & create some tests
mohammad0-0ahmad Mar 6, 2022
8ce5c39
Remove tsafe & rewrite related tests
mohammad0-0ahmad Mar 7, 2022
85e4c1c
Reimplement auto DocType inference
mohammad0-0ahmad Mar 7, 2022
6961e1c
Make Model instance show correct type instead of "any"
mohammad0-0ahmad Mar 7, 2022
0795b69
Refact ApplyBasicQueryCasting type to make schema properties types vi…
mohammad0-0ahmad Mar 7, 2022
40cf9a0
Make schema props type visible
mohammad0-0ahmad Mar 7, 2022
2139bce
Make InferredSchemaType support nested schemas
mohammad0-0ahmad Mar 7, 2022
622071b
Make statics functions auto typed by supplying them to schema options
mohammad0-0ahmad Mar 7, 2022
3c539a9
Refact Schema & Model types
mohammad0-0ahmad Mar 9, 2022
3fc1cb7
Refact some infer-doc-type import lines
mohammad0-0ahmad Mar 9, 2022
f1c0a9c
Create some tests for Model functions
mohammad0-0ahmad Mar 10, 2022
b4c5e0a
Infer document instance methods from methods object supplied in schem…
mohammad0-0ahmad Mar 10, 2022
c9aec3a
Use auto infered docType as default type every where
mohammad0-0ahmad Mar 10, 2022
818bac0
Improve ResolvePathType ability to resolve correct type of types stri…
mohammad0-0ahmad Mar 11, 2022
239846e
create & implement FlexibleObject & refact typos
mohammad0-0ahmad Mar 12, 2022
3696661
Delete AnyKeys type & refact create & insertMany types
mohammad0-0ahmad Mar 12, 2022
86558a5
Support obtaining enum type
mohammad0-0ahmad Mar 13, 2022
52551a9
Support custom typeKey
mohammad0-0ahmad Mar 13, 2022
605a089
Refact previous commit
mohammad0-0ahmad Mar 13, 2022
797e1dc
Provide JSDoc comments for inferschematype types.
mohammad0-0ahmad Mar 22, 2022
bc4953c
Merge branch 'master' of https://github.com/Automattic/mongoose
mohammad0-0ahmad Mar 23, 2022
f2f38f8
Merge branch 'master' of https://github.com/mohammad0-0ahmad-forks/mo…
mohammad0-0ahmad Mar 24, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
6.2.8 / 2022-03-22
==================
* fix(document): handle casting array of spread docs #11522
* fix(document): avoid setting nested properties on top-level document when initing with strict: false #11526
* fix(document): correctly handle deeply nested subdocuments when getting paths to validate #11501
* fix(types): avoid making TInstanceMethods any by default leading to `this = any` in middleware #11435
* fix(types): allow defining array default if using Types.Array<> in document interface #11391
* docs(migrating_to_6): describe breaking change in Mongoose 6 about default query populate model #11289
* docs(middleware): fix typo #11537 [x1489](https://github.com/x1489)

6.2.7 / 2022-03-16
==================
* perf(document): avoid running validation on every array element if there's no validators to run #11380
Expand Down
1 change: 1 addition & 0 deletions lib/document.js
Original file line number Diff line number Diff line change
Expand Up @@ -2421,6 +2421,7 @@ Document.prototype.validate = function(pathsToValidate, options, callback) {
}

this.$__validate(pathsToValidate, options, (error) => {
this.$__.validating = null;
this.$op = null;
cb(error);
});
Expand Down
4 changes: 1 addition & 3 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ const legacyPluralize = require('./helpers/pluralize');
const utils = require('./utils');
const pkg = require('../package.json');
const cast = require('./cast');
const clearValidating = require('./plugins/clearValidating');
const removeSubdocs = require('./plugins/removeSubdocs');
const saveSubdocs = require('./plugins/saveSubdocs');
const trackTransaction = require('./plugins/trackTransaction');
Expand Down Expand Up @@ -107,8 +106,7 @@ function Mongoose(options) {
[validateBeforeSave, { deduplicate: true }],
[shardingPlugin, { deduplicate: true }],
[removeSubdocs, { deduplicate: true }],
[trackTransaction, { deduplicate: true }],
[clearValidating, { deduplicate: true }]
[trackTransaction, { deduplicate: true }]
]
});
}
Expand Down
117 changes: 58 additions & 59 deletions lib/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,8 +245,7 @@ function _applyCustomWhere(doc, where) {
*/

Model.prototype.$__handleSave = function(options, callback) {
const _this = this;
let saveOptions = {};
const saveOptions = {};

applyWriteConcern(this.$__schema, options);
if (typeof options.writeConcern !== 'undefined') {
Expand Down Expand Up @@ -274,14 +273,10 @@ Model.prototype.$__handleSave = function(options, callback) {
if ('checkKeys' in options) {
saveOptions.checkKeys = options.checkKeys;
}
const session = this.$session();
if (!saveOptions.hasOwnProperty('session')) {
saveOptions.session = session;
saveOptions.session = this.$session();
}

if (Object.keys(saveOptions).length === 0) {
saveOptions = null;
}
if (this.$isNew) {
// send entire doc
const obj = this.toObject(saveToObjectOptions);
Expand All @@ -298,74 +293,77 @@ Model.prototype.$__handleSave = function(options, callback) {
}

this.$__version(true, obj);
this[modelCollectionSymbol].insertOne(obj, saveOptions, function(err, ret) {
this[modelCollectionSymbol].insertOne(obj, saveOptions, (err, ret) => {
if (err) {
_setIsNew(_this, true);
_setIsNew(this, true);

callback(err, null);
return;
}

callback(null, ret);
});

this.$__reset();
_setIsNew(this, false);
// Make it possible to retry the insert
this.$__.inserting = true;
} else {
// Make sure we don't treat it as a new object on error,
// since it already exists
this.$__.inserting = false;

const delta = this.$__delta();
if (delta) {
if (delta instanceof MongooseError) {
callback(delta);
return;
}

const where = this.$__where(delta[0]);
if (where instanceof MongooseError) {
callback(where);
return;
}
return;
}

_applyCustomWhere(this, where);
this[modelCollectionSymbol].updateOne(where, delta[1], saveOptions, (err, ret) => {
if (err) {
this.$__undoReset();
// Make sure we don't treat it as a new object on error,
// since it already exists
this.$__.inserting = false;

callback(err);
return;
}
ret.$where = where;
callback(null, ret);
});
} else {
const optionsWithCustomValues = Object.assign({}, options, saveOptions);
const where = this.$__where();
if (this.$__schema.options.optimisticConcurrency) {
const key = this.$__schema.options.versionKey;
const val = this.$__getValue(key);
if (val != null) {
where[key] = val;
}
}
this.constructor.exists(where, optionsWithCustomValues)
.then(documentExists => {
const matchedCount = !documentExists ? 0 : 1;
callback(null, { $where: where, matchedCount });
})
.catch(callback);
const delta = this.$__delta();
if (delta) {
if (delta instanceof MongooseError) {
callback(delta);
return;
}

// store the modified paths before the document is reset
this.$__.modifiedPaths = this.modifiedPaths();
this.$__reset();
const where = this.$__where(delta[0]);
if (where instanceof MongooseError) {
callback(where);
return;
}

_setIsNew(this, false);
_applyCustomWhere(this, where);
this[modelCollectionSymbol].updateOne(where, delta[1], saveOptions, (err, ret) => {
if (err) {
this.$__undoReset();

callback(err);
return;
}
ret.$where = where;
callback(null, ret);
});
} else {
const optionsWithCustomValues = Object.assign({}, options, saveOptions);
const where = this.$__where();
if (this.$__schema.options.optimisticConcurrency) {
const key = this.$__schema.options.versionKey;
const val = this.$__getValue(key);
if (val != null) {
where[key] = val;
}
}
this.constructor.exists(where, optionsWithCustomValues)
.then(documentExists => {
const matchedCount = !documentExists ? 0 : 1;
callback(null, { $where: where, matchedCount });
})
.catch(callback);
return;
}

// store the modified paths before the document is reset
this.$__.modifiedPaths = this.modifiedPaths();
this.$__reset();

_setIsNew(this, false);
};

/*!
Expand All @@ -374,8 +372,8 @@ Model.prototype.$__handleSave = function(options, callback) {

Model.prototype.$__save = function(options, callback) {
this.$__handleSave(options, (error, result) => {
const hooks = this.$__schema.s.hooks;
if (error) {
const hooks = this.$__schema.s.hooks;
return hooks.execPost('save:error', this, [this], { error: error }, (error) => {
callback(error, this);
});
Expand Down Expand Up @@ -423,6 +421,7 @@ Model.prototype.$__save = function(options, callback) {
this.$__undoReset();
error = new DocumentNotFoundError(result.$where,
this.constructor.modelName, numAffected, result);
const hooks = this.$__schema.s.hooks;
return hooks.execPost('save:error', this, [this], { error: error }, (error) => {
callback(error, this);
});
Expand Down Expand Up @@ -516,9 +515,9 @@ Model.prototype.save = function(options, fn) {
this.$__.saveOptions = options;

this.$__save(options, error => {
this.$__.saving = undefined;
delete this.$__.saveOptions;
delete this.$__.$versionError;
this.$__.saving = null;
this.$__.saveOptions = null;
this.$__.$versionError = null;
this.$op = null;

if (error) {
Expand Down
9 changes: 8 additions & 1 deletion lib/options/SchemaDateOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ Object.defineProperty(SchemaDateOptions.prototype, 'max', opts);
/**
* If set, Mongoose creates a TTL index on this path.
*
* mongo TTL index `expireAfterSeconds` value will take 'expires' value expressed in seconds.
*
* ####Example:
*
* const schema = new Schema({ "expireAt": { type: Date, expires: 11 } });
* // if 'expireAt' is set, then document expires at expireAt + 11 seconds
*
* @api public
* @property expires
* @memberOf SchemaDateOptions
Expand All @@ -61,4 +68,4 @@ Object.defineProperty(SchemaDateOptions.prototype, 'expires', opts);
* ignore
*/

module.exports = SchemaDateOptions;
module.exports = SchemaDateOptions;
4 changes: 2 additions & 2 deletions lib/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,9 @@ function Schema(obj, options) {
this.inherits = {};
this.callQueue = [];
this._indexes = [];
this.methods = {};
this.methods = (options && options.methods) || {};
mohammad0-0ahmad marked this conversation as resolved.
Show resolved Hide resolved
this.methodOptions = {};
this.statics = {};
this.statics = (options && options.statics) || {};
this.tree = {};
this.query = {};
this.childSchemas = [];
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "mongoose",
"description": "Mongoose MongoDB ODM",
"version": "6.2.7",
"version": "6.2.8",
"author": "Guillermo Rauch <guillermo@learnboost.com>",
"keywords": [
"mongodb",
Expand All @@ -20,7 +20,7 @@
"license": "MIT",
"dependencies": {
"bson": "^4.2.2",
"kareem": "2.3.4",
"kareem": "2.3.5",
"mongodb": "4.3.1",
"mpath": "0.8.4",
"mquery": "4.0.2",
Expand Down
8 changes: 8 additions & 0 deletions test/document.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11198,4 +11198,12 @@ describe('document', function() {

await doc.validate();
});

it('should give an instance function back rather than undefined (m0_0a)', function M0_0aModelJS() {
const testSchema = new mongoose.Schema({}, { methods: { instanceFn() { return 'Returned from DocumentInstanceFn'; } } });
const TestModel = mongoose.model('TestModel', testSchema);
const TestDocument = new TestModel({});
assert.equal(TestDocument.instanceFn(), 'Returned from DocumentInstanceFn');
});

});
10 changes: 9 additions & 1 deletion test/model.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8485,4 +8485,12 @@ function pick(obj, keys) {
}
}
return newObj;
}
}

describe('Check if statics functions that is supplied in schema option is availabe (m0_0a)', function() {
it('should give a static function back rather than undefined', function M0_0aModelJS() {
const testSchema = new mongoose.Schema({}, { statics: { staticFn() { return 'Returned from staticFn'; } } });
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so do we add a new option?
Probably also means that we have to modify the docs too.

@vkarpov15
Is adding statics here ok? Or does it could it break something with loadClass or so?

const TestModel = mongoose.model('TestModel', testSchema);
assert.equal(TestModel.staticFn(), 'Returned from staticFn');
});
});
2 changes: 1 addition & 1 deletion test/types/create.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Schema, model, Document, Types } from 'mongoose';
const schema: Schema = new Schema({ name: { type: 'String' } });

interface ITest extends Document {
_id?: Types.ObjectId;
_id?: Types.ObjectId | string;
name?: string;
}

Expand Down
8 changes: 7 additions & 1 deletion test/types/document.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,12 @@ function gh11435() {
const ItemSchema = new Schema<Item>({ name: String });

ItemSchema.pre('validate', function preValidate() {
expectType<Model<unknown>>(this.model('Item1'));
expectType<Model<Item>>(this.model('Item1'));
});

const AutoTypedItemSchema = new Schema({ name: String });

AutoTypedItemSchema.pre('validate', function preValidate() {
expectType<Model<Item>>(this.model('Item1'));
});
}
17 changes: 15 additions & 2 deletions test/types/middleware.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Schema, model, Model, Document, SaveOptions, Query, Aggregate, HydratedDocument, PreSaveMiddlewareFunction } from 'mongoose';
import { expectError, expectType } from 'tsd';
import { expectError, expectAssignable, expectNotType } from 'tsd';

interface ITest extends Document {
name?: string;
Expand Down Expand Up @@ -81,6 +81,19 @@ const Test = model<ITest>('Test', schema);

function gh11257(): void {
schema.pre('save', { document: true }, function() {
expectType<HydratedDocument<ITest>>(this);
expectAssignable<HydratedDocument<ITest>>(this);
});
}

function gh11480(): void {
type IUserSchema = {
name: string;
}

const UserSchema = new Schema<IUserSchema>({ name: { type: String } });

UserSchema.pre('save', function(next) {
expectNotType<any>(this);
next();
});
}
Loading