Repository: Automattic/mongoose (JavaScript)

Issue:
 bug: field with `alias: 'id'` has conflict with schema automatic id setter
### Prerequisites

- [X] I have written a descriptive issue title
- [X] I have searched existing issues to ensure the bug has not already been reported


### Mongoose version

7.4.0

### Node.js version

16.20.0

### MongoDB server version

6.0.6

### Typescript version (if applicable)

_No response_

### Description

After updating mongoose to version 7.4.0, an error appeared (everything worked correctly on version 7.3.4).

Adding to schema a field with `alias: 'id'` causes strange setter behaviour (see Steps to Reproduce). The bug is related to the commit https://github.com/Automattic/mongoose/commit/32a84b7a65cca90c1355ce6cd218f89a92ce1e4d

### Steps to Reproduce

### Steps to Reproduce:

```javascript
const mongoose = require('mongoose');
const { Schema } = mongoose;

const removeMultipleSpaces = (val) => {
  console.log('removeMultipleSpaces', val, typeof val);
  return val.replace(/\s+/g, ' ');
};


run()
  .then(() => console.log('done'))
  .catch(error => console.error(error))
  .then(() => process.exit(0));

async function run() {
  await mongoose.connect('mongodb://localhost:27017/gh13650');
  await mongoose.connection.dropDatabase();


  const HouseSchema = new Schema({
    _id: {
      type: String,
      required: true,
      set: removeMultipleSpaces,
      alias: 'id',
    },
  }, {
    // id: false
  });

  const House = mongoose.model('House', HouseSchema);

  const house = new House({
    id: 'H-1',
  });

  await house.save();


  await mongoose.disconnect();
}

```

### Output:
```
removeMultipleSpaces H-1 string
removeMultipleSpaces { _id: 'H-1' } object          <<------------ Look here
Error: House validation failed: _id: Cast to String failed for value "{ _id: 'H-1' }" (type model) at path "_id" because of "TypeError"
    at ValidationError.inspect (mongoose_bugreport/node_modules/mongoose/lib/error/validation.js:50:26)
    at formatValue (node:internal/util/inspect:806:19)
    at inspect (node:internal/util/inspect:365:10)
    at formatWithOptionsInternal (node:internal/util/inspect:2292:40)
    at formatWithOptions (node:internal/util/inspect:2154:10)
    at console.value (node:internal/console/constructor:348:14)
    at console.warn (node:internal/console/constructor:381:61)
    at mongoose_bugreport/mongoose.bug.0000.js:12:27
    at processTicksAndRejections (node:internal/process/task_queues:96:5) {
  errors: {
    _id: CastError: Cast to String failed for value "{ _id: 'H-1' }" (type model) at path "_id" because of "TypeError"
        at model.$set (mongoose_bugreport/node_modules/mongoose/lib/document.js:1460:9)
        at model.set [as _id] (mongoose_bugreport/node_modules/mongoose/lib/helpers/document/compile.js:205:19)
        at model.idSetter (mongoose_bugreport/node_modules/mongoose/lib/helpers/schema/idGetter.js:41:12)
        at VirtualType.applySetters (mongoose_bugreport/node_modules/mongoose/lib/virtualtype.js:166:16)
        at model.$set (mongoose_bugreport/node_modules/mongoose/lib/document.js:1271:12)
        at model.$set (mongoose_bugreport/node_modules/mongoose/lib/document.js:1113:16)
        at model.Document (mongoose_bugreport/node_modules/mongoose/lib/document.js:164:12)
        at model.Model (mongoose_bugreport/node_modules/mongoose/lib/model.js:122:12)
        at new model (mongoose_bugreport/node_modules/mongoose/lib/model.js:4684:15)
        at run (mongoose_bugreport/mongoose.bug.0000.js:33:17) {
      stringValue: `"{ _id: 'H-1' }"`,
      messageFormat: undefined,
      kind: 'String',
      value: [Object],
      path: '_id',
      reason: TypeError: val.replace is not a function
          at model.removeMultipleSpaces (mongoose_bugreport/mongoose.bug.0000.js:6:14)
          at SchemaString.SchemaType._applySetters (mongoose_bugreport/node_modules/mongoose/lib/schematype.js:1189:20)
          at SchemaString.SchemaType.applySetters (mongoose_bugreport/node_modules/mongoose/lib/schematype.js:1214:16)
          at model.$set (mongoose_bugreport/node_modules/mongoose/lib/document.js:1411:22)
          at model.set [as _id] (mongoose_bugreport/node_modules/mongoose/lib/helpers/document/compile.js:205:19)
          at model.idSetter (mongoose_bugreport/node_modules/mongoose/lib/helpers/schema/idGetter.js:41:12)
          at VirtualType.applySetters (mongoose_bugreport/node_modules/mongoose/lib/virtualtype.js:166:16)
          at model.$set (mongoose_bugreport/node_modules/mongoose/lib/document.js:1271:12)
          at model.$set (mongoose_bugreport/node_modules/mongoose/lib/document.js:1113:16)
          at model.Document (mongoose_bugreport/node_modules/mongoose/lib/document.js:164:12),
      valueType: 'model'
    }
  },
  _message: 'House validation failed'
}
```

### Expected Behavior

I understand that object validation fails because of the `CastError` error, and it is called because an object is passed to `removeMultipleSpaces` and a `TypeError` already occurs there.

I found out that there are several ways to solve the problem:

1. Remove `alias: 'id'`. Since version 7.4.0 added an automatic setter for the `id` virtual field (commit: https://github.com/Automattic/mongoose/commit/32a84b7a65cca90c1355ce6cd218f89a92ce1e4d).
2. Add the `{ id: false }` option to the schema to disable automatic addition of the `id` virtual field with its getter/setter
3. Add an argument check to `removeMultipleSpaces` that a string came in (but this is strange because I defined the setter for a string field).

Also I suggest adding a new feature to mongoose:

> If there is some field with `alias: 'id'`, then automatically set the option `{ id: false }` for the schema


Discussion:
Ensure you're not defining _id manually unless it's necessary.
Avoid naming virtual paths as _id.
If you need a custom _id, disable Mongoose's default behavior by setting { _id: false }



Patch:
diff --git a/lib/helpers/schema/idGetter.js b/lib/helpers/schema/idGetter.js
index ba5ef1011c2..6df8a8cc04f 100644
--- a/lib/helpers/schema/idGetter.js
+++ b/lib/helpers/schema/idGetter.js
@@ -12,6 +12,9 @@ module.exports = function addIdGetter(schema) {
   if (!autoIdGetter) {
     return schema;
   }
+  if (schema.aliases && schema.aliases.id) {
+    return schema;
+  }
   schema.virtual('id').get(idGetter);
   schema.virtual('id').set(idSetter);
 


Test patch:
diff --git a/test/schema.alias.test.js b/test/schema.alias.test.js
index 0f37f595c1a..0e9a96e6a5b 100644
--- a/test/schema.alias.test.js
+++ b/test/schema.alias.test.js
@@ -183,4 +183,21 @@ describe('schema alias option', function() {
     assert.ok(schema.virtuals['name1']);
     assert.ok(schema.virtuals['name2']);
   });
+  it('should disable the id virtual entirely if there\'s a field with alias `id` gh-13650', async function() {
+
+    const testSchema = new Schema({
+      _id: {
+        type: String,
+        required: true,
+        set: (val) => val.replace(/\s+/g, ' '),
+        alias: 'id'
+      }
+    });
+    const Test = db.model('gh13650', testSchema);
+    const doc = new Test({
+      id: 'H-1'
+    });
+    await doc.save();
+    assert.ok(doc);
+  });
 });