Skip to content

Commit

Permalink
Merge branch 'master' into 8.10
Browse files Browse the repository at this point in the history
  • Loading branch information
vkarpov15 committed Jan 2, 2025
2 parents a8fc5cd + f025b29 commit 60c5c7f
Show file tree
Hide file tree
Showing 7 changed files with 241 additions and 119 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
8.9.3 / 2024-12-30
==================
* fix(schema): make duplicate index error a warning for now to prevent blocking upgrading #15135 #15112 #15109
* fix(model): handle document array paths set to non-array values in Model.castObject() #15124 #15075
* fix(document): avoid using childSchemas.path for compatibility with pre-Mongoose-8.8 schemas #15131 #15071
* fix(model): avoid throwing unnecessary error if updateOne() returns null in save() #15126
* perf(cursor): clear the stack every time if using populate with batchSize to avoid stack overflows with large docs #15136 #10449
* types: make BufferToBinary avoid Document instances #15123 #15122
* types(model+query): avoid stripping out virtuals when calling populate with paths generic #15132 #15111
* types(schema): add missing removeIndex #15134
* types: add cleanIndexes() to IndexManager interface #15127
* docs: move search endpoint to netlify #15119

8.9.2 / 2024-12-19
==================
* fix(schema): avoid throwing duplicate index error if index spec keys have different order or index has a custom name #15112 #15109
Expand Down
2 changes: 1 addition & 1 deletion lib/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -2146,7 +2146,7 @@ Schema.prototype.index = function(fields, options) {

for (const existingIndex of this.indexes()) {
if (options.name == null && existingIndex[1].name == null && isIndexSpecEqual(existingIndex[0], fields)) {
throw new MongooseError(`Schema already has an index on ${JSON.stringify(fields)}`);
utils.warn(`Duplicate schema index on ${JSON.stringify(fields)} found. This is often due to declaring an index using both "index: true" and "schema.index()". Please remove the duplicate index definition.`);
}
}

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "mongoose",
"description": "Mongoose MongoDB ODM",
"version": "8.9.2",
"version": "8.9.3",
"author": "Guillermo Rauch <[email protected]>",
"keywords": [
"mongodb",
Expand Down
56 changes: 36 additions & 20 deletions test/schema.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const DocumentObjectId = mongoose.Types.ObjectId;
const vm = require('vm');
const idGetter = require('../lib/helpers/schema/idGetter');
const applyPlugins = require('../lib/helpers/schema/applyPlugins');
const utils = require('../lib/utils');

/**
* Test Document constructor.
Expand Down Expand Up @@ -3279,29 +3280,44 @@ describe('schema', function() {
assert.equal(subdoc.getAnswer(), 42);
});
it('throws "already has an index" error if duplicate index definition (gh-15056)', function() {
const ObjectKeySchema = new mongoose.Schema({
key: {
type: String,
required: true,
unique: true
},
type: {
type: String,
required: false
}
});
sinon.stub(utils, 'warn').callsFake(() => {});
try {
const ObjectKeySchema = new mongoose.Schema({
key: {
type: String,
required: true,
unique: true
},
type: {
type: String,
required: false
}
});

assert.throws(() => {
ObjectKeySchema.index({ key: 1 });
}, /MongooseError.*already has an index/);
assert.equal(utils.warn.getCalls().length, 1);
let [message] = utils.warn.getCalls()[0].args;
assert.equal(
message,
'Duplicate schema index on {"key":1} found. This is often due to declaring an index using both "index: true" and "schema.index()". Please remove the duplicate index definition.'
);

ObjectKeySchema.index({ key: 1, type: 1 });
assert.throws(() => {
ObjectKeySchema.index({ key: 1, type: 1 });
}, /MongooseError.*already has an index/);

ObjectKeySchema.index({ type: 1, key: 1 });
ObjectKeySchema.index({ key: 1, type: -1 });
ObjectKeySchema.index({ key: 1, type: 1 }, { unique: true, name: 'special index' });
assert.equal(utils.warn.getCalls().length, 1);
ObjectKeySchema.index({ key: 1, type: 1 });
assert.equal(utils.warn.getCalls().length, 2);
[message] = utils.warn.getCalls()[1].args;
assert.equal(
message,
'Duplicate schema index on {"key":1,"type":1} found. This is often due to declaring an index using both "index: true" and "schema.index()". Please remove the duplicate index definition.'
);

ObjectKeySchema.index({ type: 1, key: 1 });
ObjectKeySchema.index({ key: 1, type: -1 });
ObjectKeySchema.index({ key: 1, type: 1 }, { unique: true, name: 'special index' });
assert.equal(utils.warn.getCalls().length, 2);
} finally {
sinon.restore();
}
});
});
95 changes: 94 additions & 1 deletion test/types/populate.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import mongoose, { Schema, model, Document, PopulatedDoc, Types, HydratedDocument, SchemaTypeOptions } from 'mongoose';
import mongoose, { Schema, model, Document, PopulatedDoc, Types, HydratedDocument, SchemaTypeOptions, Model } from 'mongoose';
// Use the mongodb ObjectId to make instanceof calls possible
import { ObjectId } from 'mongodb';
import { expectAssignable, expectError, expectType } from 'tsd';
Expand Down Expand Up @@ -459,3 +459,96 @@ async function gh14574() {
expectType<string>(user.fullName());
expectType<string>(user.friend.fullName());
}

async function gh15111() {
interface IChild {
_id: Types.ObjectId;
name: string;
}

type ChildDocumentOverrides = {};

interface IChildVirtuals {
id: string;
}

type ChildInstance = HydratedDocument<
IChild,
ChildDocumentOverrides & IChildVirtuals
>;

type ChildModelType = Model<
IChild,
{},
ChildDocumentOverrides,
IChildVirtuals,
ChildInstance
>;
const childSchema = new Schema<IChild, ChildModelType>(
{
name: {
type: 'String',
required: true,
trim: true
}
}
);
const ChildModel = mongoose.model<IChild, ChildModelType>('Child', childSchema);

interface IParent {
_id: Types.ObjectId;
name: string;
surname: string;
child: PopulatedDoc<Document<Types.ObjectId> & IChild>;
}

type ParentDocumentOverrides = {};

interface IParentVirtuals {
id: string;
fullName: string;
}

type ParentInstance = HydratedDocument<
IParent,
ParentDocumentOverrides & IParentVirtuals
>;

type ParentModelType = Model<
IParent,
{},
ParentDocumentOverrides,
IParentVirtuals,
ParentInstance
>;
const parentSchema = new Schema<IParent, ParentModelType>(
{
name: {
type: 'String',
required: true,
trim: true
},
surname: {
type: 'String',
required: true,
trim: true
},
child: {
type: 'ObjectId',
ref: 'Child',
required: true
}
}
);

parentSchema.virtual('fullName').get(function() {
return `${this.name} ${this.surname}`;
});

const ParentModel = mongoose.model<IParent, ParentModelType>('Parent', parentSchema);

const parents = await ParentModel.find().populate<{ child: ChildInstance }>(
'child'
);
expectType<string>(parents[0].fullName);
}
Loading

0 comments on commit 60c5c7f

Please sign in to comment.