This is the fourth day of my participation in the November Gwen Challenge. Check out the details: The last Gwen Challenge 2021

In Mongoose, Schema is the configuration object of the model. Schema doesn’t allow you to read and write from MongoDB, which is where the model comes in.

It can:

  • Defines what attributes a document saved in MongoDB can have
  • Define custom authentication
  • The statement virtuals
  • Declare getters and setters
  • Define static and method

Schema path and transformation

The first argument to the Schema class constructor is a Definition object. This object defines the path that Schema has. For example, the following userSchema has a name path and an age path.

const userSchema = new mongoose.Schema({
  name: String.age: Number
})
​
userSchema.path('name') // SchemaString { ... }
userSchema.path('age') // SchemaNumber { ... }
Copy the code

To create a model in Mongoose, call the mongoose.model() method with Schema as the second parameter. For example, the UserModel in the following example will have name and age attributes and will remove any attributes that are not defined in the userSchema.

const userSchema = new mongoose.Schema({
  name: String.age: Number
})
​
const UserModel = mongoose.model('User', userSchema)
​
const doc = new UserModel({
  name: 'O.O'.age: 18.hobby: 'programming'
})
​
console.log(doc.name) // 'Jean-Luc Picard'
console.log(doc.age) / / 59// undefined, Mongoose removes hobby because it is not in the schema
console.log(doc.hobby)
Copy the code

In addition, Mongoose will cast the document to match the given Schema type. This means that you can safely pass untrusted data to Mongoose and trust that the data will match your Schema.

const UserModel = mongoose.model('User', userSchema)
​
const doc = new UserModel({
  name: 'O.O'.age: '18' // Mongoose will convert it to a number
})
​
console.log(doc.age) / / 18
await doc.save()
​
// Mongoose converts 20 from a string to a number, even in an update
await UserModel.updateOne({}, { $set: { age: '20'}})Copy the code

validation

In addition to casting values, Mongoose also allows you to define validation in the Schema. For example, suppose you want to ensure that your user has a name. You can set the name attribute required in the Schema, as shown below.

const userSchema = new mongoose.Schema({
  // Set name to required, which means the field is required
  name: { type: String.required: true },
  age: Number
})
const UserModel = mongoose.model('User', userSchema)
​
const doc = new UserModel({ age: 30 })
​
const err = await doc.save().catch(err= > err)
console.log(err.message) // Path name is required.
Copy the code

options

The Schema constructor takes two parameters: definition and options. You can find a complete list of Schema options in the Mongoose documentation.

For example, the typeKey option allows you to configure which key Mongoose looks for to determine if you are defining nested paths. Suppose we want to define a nested key named type:

const schema = new mongoose.Schema({
  nested: {
    type: String
  }
})
​
schema.path('nested') // SchemaString { ... }
schema.path('nested.type') // undefined
Copy the code

There are several solutions to this use case. One is to set the typeKey option, as shown below.

// Let Mongoose look for $type instead of type
const options = { typeKey: '$type' }
const schema = new mongoose.Schema({
  nested: {
    type: String
  },
  otherProperty: {
    $type: String
  }
}, options)
​
schema.path('nested.type') // SchemaString { ... }
schema.path('otherProperty') // SchemaString { ... }
Copy the code

SchemaType

In Mongoose, SchemaType is a configuration object for a single path in Schema. SchemaType tells you what type the path should be, how to validate it, what the default value of the path is, and other Mongoose specific configuration options.

const schema = Schema({ name: String.age: Number })
​
schema.path('name') instanceof mongoose.SchemaType // true
schema.path('age') instanceof mongoose.SchemaType // true
Copy the code

The SchemaType class is just a base class. There are several classes derived from SchemaType that represent different core Mongoose types:

  • mongoose.Schema.Types.String
  • mongoose.Schema.Types.Number
  • mongoose.Schema.Types.Date
  • mongoose.Schema.Types.Buffer
  • mongoose.Schema.Types.Boolean
  • mongoose.Schema.Types.Mixed
  • mongoose.Schema.Types.ObjectIdOr equivalentmongoose.ObjectId)
  • mongoose.Schema.Types.Array
  • mongoose.Schema.Types.Decimal128
  • mongoose.Schema.Types.Map

Such as:

const schema = Schema({ name: String.age: Number })
​
schema.path('name') instanceof mongoose.SchemaType // true
schema.path('name') instanceof mongoose.Schema.Types.String // true
​
schema.path('age') instanceof mongoose.SchemaType // true
schema.path('age') instanceof mongoose.Schema.Types.Number // true
Copy the code

You don’t usually have to use SchemaType instances directly. You can declare validators and default values in the Schema definition. For example, the following example sets the default age to 25 and adds a validator to ensure that age is at least 21.

const schema = Schema({
  age: {
    type: Number.default: 25.validate: v= > v >= 21}})Copy the code

This is how you normally declare default values and validators in Mongoose. But after you create schemas, there is nothing to prevent you from adding them to Age SchemaType.

/ / equivalent
const schema = Schema({ age: Number })
​
schema.path('age').default(25)
schema.path('age').validate(v= > v >= 21)
Copy the code

The latter syntax is the same as the former, but is not often used. The most common case for using SchemaType instances directly is with an embedded discriminator.