阿里云主机折上折
  • 微信号
Current Site:Index > The definition and structure of a Schema

The definition and structure of a Schema

Author:Chuan Chen 阅读数:45353人阅读 分类: MongoDB

Definition of Schema

A Schema in Mongoose is an object used to define the structure of a data model. It describes the field types, default values, validation rules, and other information for documents. A Schema does not directly interact with the database but serves as a template for Models. When performing CRUD operations through a Model, type conversion and validation are carried out based on the Schema's definitions.

const mongoose = require('mongoose');
const { Schema } = mongoose;

// Define a user Schema
const userSchema = new Schema({
  username: String,
  age: Number,
  isAdmin: Boolean
});

Basic Structure of Schema

A Schema consists of field definitions, where each field must specify a type. Mongoose supports the following basic types:

  • String
  • Number
  • Boolean
  • Date
  • Buffer
  • ObjectId
  • Array
  • Mixed
const productSchema = new Schema({
  name: String,  // String type
  price: Number, // Number type
  inStock: Boolean, // Boolean type
  createdAt: Date, // Date type
  tags: [String], // Array of strings
  meta: Schema.Types.Mixed // Mixed type
});

Field Configuration Options

Each field can be configured with various options to control its behavior:

const bookSchema = new Schema({
  title: {
    type: String,
    required: true, // Required field
    trim: true, // Automatically trim whitespace
    minlength: 3, // Minimum length
    maxlength: 100 // Maximum length
  },
  published: {
    type: Date,
    default: Date.now // Default value
  },
  edition: {
    type: Number,
    min: 1, // Minimum value
    max: 10 // Maximum value
  }
});

Nested Schemas

Schemas can be nested within other Schemas to build complex data structures:

const addressSchema = new Schema({
  street: String,
  city: String,
  zipCode: String
});

const customerSchema = new Schema({
  name: String,
  addresses: [addressSchema], // Array of nested Schemas
  primaryAddress: addressSchema // Single nested Schema
});

Custom Validators

Schemas support custom validation logic:

const orderSchema = new Schema({
  items: [{
    productId: Schema.Types.ObjectId,
    quantity: {
      type: Number,
      validate: {
        validator: function(v) {
          return v > 0; // Custom validation function
        },
        message: props => `${props.value} is not a valid quantity`
      }
    }
  }]
});

Virtual Properties

Virtual properties are computed properties not stored in the database:

const personSchema = new Schema({
  firstName: String,
  lastName: String
});

// Define a virtual property fullName
personSchema.virtual('fullName').get(function() {
  return `${this.firstName} ${this.lastName}`;
});

Middleware

Schemas support pre and post middleware to execute code before or after specific operations:

const blogSchema = new Schema({
  title: String,
  content: String,
  views: Number
});

// Pre-save middleware
blogSchema.pre('save', function(next) {
  if (!this.views) {
    this.views = 0;
  }
  next();
});

// Post-find middleware
blogSchema.post('find', function(docs) {
  console.log(`Found ${docs.length} blog posts`);
});

Index Definitions

Indexes can be defined in Schemas to improve query performance:

const productSchema = new Schema({
  name: { type: String, index: true }, // Single-field index
  category: String,
  price: Number
});

// Compound index
productSchema.index({ category: 1, price: -1 });

Custom Methods

Custom instance methods and static methods can be added to Schemas:

const animalSchema = new Schema({
  name: String,
  type: String
});

// Instance method
animalSchema.methods.findSimilarTypes = function(cb) {
  return this.model('Animal').find({ type: this.type }, cb);
};

// Static method
animalSchema.statics.findByName = function(name) {
  return this.find({ name: new RegExp(name, 'i') });
};

Plugin System

Schemas support plugins to extend functionality:

// Define a simple plugin
function timestampPlugin(schema) {
  schema.add({ 
    createdAt: Date,
    updatedAt: Date 
  });

  schema.pre('save', function(next) {
    const now = Date.now();
    this.updatedAt = now;
    if (!this.createdAt) {
      this.createdAt = now;
    }
    next();
  });
}

// Apply the plugin
const postSchema = new Schema({ title: String });
postSchema.plugin(timestampPlugin);

Query Helper Methods

Query interfaces can be extended:

const queryPlugin = function(schema) {
  schema.query.byName = function(name) {
    return this.where({ name: new RegExp(name, 'i') });
  };
};

const userSchema = new Schema({ name: String });
userSchema.plugin(queryPlugin);

// Use the extended query method
User.find().byName('john').exec();

Polymorphic Associations

Polymorphic associations can be implemented using Schemas:

const commentSchema = new Schema({
  content: String,
  commentable: {
    kind: String, // Type of the associated model
    item: { type: Schema.Types.ObjectId, refPath: 'commentable.kind' }
  }
});

const Post = mongoose.model('Post', new Schema({ title: String }));
const Product = mongoose.model('Product', new Schema({ name: String }));

// Create an associated comment
const comment = new Comment({
  content: 'Great post!',
  commentable: {
    kind: 'Post',
    item: post._id
  }
});

Dynamic References

Schemas support dynamically referenced fields:

const orderSchema = new Schema({
  customer: {
    type: Schema.Types.ObjectId,
    ref: 'User' // Static reference
  },
  product: {
    type: Schema.Types.ObjectId,
    ref: function() {
      return this.productType; // Dynamic reference
    }
  },
  productType: String
});

Schema Options

Various options can be specified when creating a Schema:

const options = {
  timestamps: true, // Automatically add createdAt and updatedAt
  toJSON: { virtuals: true }, // Include virtuals when converting to JSON
  toObject: { virtuals: true },
  id: false, // Disable the default id virtual property
  _id: false, // Disable the _id field
  versionKey: '__v' // Custom version key
};

const logSchema = new Schema({
  message: String
}, options);

Schema Inheritance

Related Schemas can be created through inheritance:

// Base Schema
const options = { discriminatorKey: 'kind' };
const eventSchema = new Schema({ time: Date }, options);

// Inherited Schema
const clickedEventSchema = new Schema({
  element: String,
  position: { x: Number, y: Number }
}, options);

const Event = mongoose.model('Event', eventSchema);
const ClickedEvent = Event.discriminator('Clicked', clickedEventSchema);

Schema Aliases

Aliases can be defined for fields:

const personSchema = new Schema({
  n: { type: String, alias: 'name' }, // Use 'n' in the database, 'name' in code
  a: { type: Number, alias: 'age' }
});

const person = new Person({ name: 'John', age: 30 });
console.log(person.name); // 'John'
console.log(person.n); // undefined (unless explicitly set)

Schema Type Extensions

Custom Schema types can be extended:

// Custom email type
function emailValidator(v) {
  return /^([\w-\.]+@([\w-]+\.)+[\w-]{2,4})?$/.test(v);
}

mongoose.Schema.Types.Email = mongoose.SchemaType.extend({
  constructor: function Email(path, options) {
    mongoose.SchemaTypes.String.call(this, path, options);
    this.validate(emailValidator, 'Not a valid email address');
  }
});

// Use the custom type
const contactSchema = new Schema({
  email: mongoose.Schema.Types.Email
});

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.