使用 Mongoose 在 Node 和 Nest 中操作 MongoDB 数据库

在 Node.js 中,我们通常会使用 mongoose 这个第三方库来操作 MongoDB 数据库。

Node 操作 mongoose

初始化项目

创建项目:

bash 复制代码
mkdir mongoose-test
cd mongoose-test
npm init -y

接下来,我们在项目中安装 mongoose:

bash 复制代码
npm install mongoose

连接 MongoDB 插入数据

然后,使用 Node.js 代码连接到本地运行的 MongoDB 实例:

创建 index.js 文件:

javascript 复制代码
const mongoose = require('mongoose');

main().catch(err => console.error(err));

async function main() {
	// 连接到本地 MongoDB 实例的 'yun' 数据库
	await mongoose.connect('mongodb://localhost:27017/yun');

	// 定义一个 Person 的 Schema,描述文档结构
	const PersonSchema = new mongoose.Schema({
		name: String,
		age: Number,
    gender: String,
		hobbies: [String],
	});

	// 根据 Schema 创建一个 Model
	const Person = mongoose.model('Person', PersonSchema);

	// 创建两个 Person 文档实例并保存到数据库
	const yun = new Person({ name: '云牧', age: 20 });
	const dai = new Person({ name: '黛玉', age: 21, hobbies: ['reading', 'play'] });

	await yun.save();
	await dai.save();

	// 查询数据库中的所有 Person 记录
	const persons = await Person.find();
	console.log(persons);
}

注意 :上面代码中 await mongoose.connect(...) 确保了数据库连接成功之后,才会执行后续操作。

在 MongoDB 中,一个 collection 中的 document 可以是任意形状的。

因此,我们需要先定义一个 Schema 来描述我们想要存储的数据结构,然后根据这个 Schema 创建 Model,以便进行增删改查(CRUD)操作。

字段值类型和对象验证

文档结构可选的常用字段类型列表:

Mongoose 可以使用对象形式对字段值进行更多验证:

javascript 复制代码
const PersonSchema = new mongoose.Schema({
    // 简单的字符串类型
    name: String,

    // 带有更多配置的对象类型
    name: {
        type: String,
        required: true, // 设置为必填字段
        trim: true, // 自动去除字符串两端的空格
        lowercase: true, // 自动将字符串转为小写
        unique: true, // 字段值必须唯一
        minlength: 2, // 最小长度限制
        maxlength: 50, // 最大长度限制
        // 自定义验证器
        validate: {
            validator: function(v) {
                return /[a-zA-Z]/.test(v); // 只允许字母
            },
            message: props => `${props.value} is not a valid name!`
        },
        default: '匿名' // 默认值
    },
    gender: {
      type: String,
      enum: ['男', '女'] // 设置的值必须是数组中的
    },
    // 其他字段保持不变
    age: Number,
    hobbies: [String],
});

查看数据

在 MongoDB Compass 中可以看到数据插入了:

我们也可以在代码中查询:

CURD

增加

插入一条数据:

javascript 复制代码
SongModel.create({ author: '王力宏' }, function (err, data) {
  if (err) throw err;
  // 处理插入的数据
  mongoose.connection.close();
});

批量插入数据:

javascript 复制代码
SongModel.insertMany([{ author: '王力宏' }, { author: '周杰伦' }], function (err) {
  if (err) throw err;
  mongoose.connection.close();
});

删除

删除一条数据:

javascript 复制代码
SongModel.deleteOne({ _id: '5dd662b5381fc316b44ce167' }, function (err) {
  if (err) throw err;
  mongoose.connection.close();
});

批量删除:

javascript 复制代码
SongModel.deleteMany({ author: '王力宏' }, function (err) {
  if (err) throw err;
  mongoose.connection.close();
});

更新

更新一条数据:

javascript 复制代码
SongModel.updateOne({ _id: '5dd662b5381fc316b44ce167' }, { author: '王力宏' }, function (err) {
  if (err) throw err;
  mongoose.connection.close();
});

批量更新数据:

javascript 复制代码
SongModel.updateMany({ author: 'Leehom Wang' }, { author: '王力宏' }, function (err) {
  if (err) throw err;
  mongoose.connection.close();
});

查询

查询一条数据:

javascript 复制代码
SongModel.findOne({ author: '王力宏' }, function (err, data) {
  if (err) throw err;
  console.log(data);
  mongoose.connection.close();
});

// 根据ID查询数据
SongModel.findById('5dd662b5381fc316b44ce167', function (err, data) {
  if (err) throw err;
  console.log(data);
  mongoose.connection.close();
});

批量查询数据:

javascript 复制代码
// 不加条件查询
SongModel.find(function (err, data) {
  if (err) throw err;
  console.log(data);
  mongoose.connection.close();
});

// 加条件查询
SongModel.find({ author: '王力宏' }, function (err, data) {
  if (err) throw err;
  console.log(data);
  mongoose.connection.close();
});

条件控制与运算符

在 MongoDB 中,对于比较运算,我们无法直接使用传统的大于(>)、小于(<)等运算符,而需要使用特定的替代符号:

  • 使用 $gt (greater than)

  • < 使用 $lt (less than)
  • = 使用 $gte (greater than or equal)

  • <= 使用 $lte (less than or equal)
  • !== 使用 $ne (not equal)

逻辑运算

MongoDB 提供了以下逻辑运算符,用于组合查询条件:

  • $or:逻辑或,用于查询满足任一条件的情况。
  • $and:逻辑与,用于查询同时满足多个条件的情况。
javascript 复制代码
db.students.find({$or:[{age:18},{age:24}]})

db.students.find({$and: [{age: {$lt:20}}, {age: {$gt: 15}}]});

正则匹配

MongoDB 允许使用 JavaScript 正则表达式进行模糊查询,可以直接在条件中使用:

javascript 复制代码
db.students.find({ name: /imissyou/ });

个性化读取与字段筛选

字段筛选

使用 MongoDB 的查询接口,可以指定需要返回或者排除的字段:

javascript 复制代码
// 0: 不需要的字段,1: 需要的字段
SongModel.find()
  .select({ _id: 0, title: 1 }) // 筛选掉_id字段,只返回title字段
  .exec(function (err, data) {
    if (err) throw err;
    console.log(data);
    mongoose.connection.close();
  });

数据排序

使用 .sort() 方法可以按照指定的字段进行排序:

javascript 复制代码
// 1: 升序,-1: 降序
SongModel.find()
  .sort({ hot: 1 }) // 按hot字段升序排列
  .exec(function (err, data) {
    if (err) throw err;
    console.log(data);
    mongoose.connection.close();
  });

数据截取

.skip() 和 .limit() 方法可以用来实现数据的分页截取:

javascript 复制代码
SongModel.find()
  .skip(10) // 跳过前10条数据
  .limit(10) // 限制返回10条数据
  .exec(function (err, data) {
    if (err) throw err;
    console.log(data);
    mongoose.connection.close();
  });

Nest 操作 mongoose

初始化项目

创建项目:

bash 复制代码
nest new nest-mongoose -p npm

安装依赖:

bash 复制代码
cd nest-mongoose
npm install @nestjs/mongoose mongoose
npm install class-validator class-transformer

启动项目:

bash 复制代码
npm run start:dev

配置 MongooseModule

在 AppModule 中引入 MongooseModule 并配置数据库连接:

typescript 复制代码
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { MongooseModule } from '@nestjs/mongoose';

@Module({
  imports: [MongooseModule.forRoot('mongodb://localhost:27017/yun')],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

定义 Dog 实体

使用 Nest CLI 创建 dog 模块,并排除测试文件:

bash 复制代码
nest g resource dog --no-spec

在 dog.entities.ts 中使用装饰器定义 Dog 实体:

typescript 复制代码
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { HydratedDocument } from 'mongoose';

/**
 * 定义一个名为Dog的mongoose模式类,用于数据库中的狗的文档表示。
 */
@Schema()
export class Dog {
  @Prop()
  name: string;

  @Prop()
  age: number;

  @Prop([String])
  tags: string[];
}

// 定义Dog模式类的文档类型,这是mongoose处理后带有一些额外字段的Dog类实例。
export type DogDocument = HydratedDocument<Dog>;

// 创建并返回Dog模式的实例,供nestjs/mongoose使用。
export const DogSchema = SchemaFactory.createForClass(Dog);

HydratedDocument 只是在 Dog 类型的基础上加了一个 _id 属性:

DogModule 配置

在 dog.module.ts 中引入 DogSchema 并注册到 MongooseModule:

typescript 复制代码
import { Module } from '@nestjs/common';
import { DogService } from './dog.service';
import { DogController } from './dog.controller';
import { MongooseModule } from '@nestjs/mongoose';
import { Dog, DogSchema } from './entities/dog.entity';

@Module({
  imports: [MongooseModule.forFeature([{ name: Dog.name, schema: DogSchema }])],
  controllers: [DogController],
  providers: [DogService],
})
export class DogModule {}

定义数据传输对象(DTO)

在 create-dog.dto.ts 文件中定义创建 Dog 实体的数据验证规则:

typescript 复制代码
import { IsNotEmpty, IsNumber, IsString, Length } from 'class-validator';

export class CreateDogDto {
  // 狗狗的名字必须是字符串类型,不能为空,且长度至少为3个字符
  @IsString()
  @IsNotEmpty()
  @Length(3)
  name: string;

  // 狗狗的年龄必须是数字类型,且不能为空
  @IsNumber()
  @IsNotEmpty()
  age: number;

  // 狗狗的标签,以字符串数组形式存储
  tags: string[];
}

DogService 实现

在 dog.service.ts 中实现 CRUD 操作:

typescript 复制代码
import { Injectable } from '@nestjs/common';
import { CreateDogDto } from './dto/create-dog.dto';
import { UpdateDogDto } from './dto/update-dog.dto';
import { Model } from 'mongoose';
import { InjectModel } from '@nestjs/mongoose';
import { Dog } from './entities/dog.entity';

/**
 * 提供对狗信息的增删改查服务
 */
@Injectable()
export class DogService {
  /**
   * 通过@InjectModel装饰器注入Dog模型
   */
  @InjectModel(Dog.name)
  private dogModel: Model<Dog>;

  /**
   * 创建一个新的狗记录
   * @param createDogDto 创建狗所需的DTO
   * @returns 返回创建的狗记录
   */
  create(createDogDto: CreateDogDto) {
    const dog = new this.dogModel(createDogDto);
    return dog.save();
  }

  /**
   * 查找所有狗记录
   * @returns 返回所有狗记录的数组
   */
  findAll() {
    return this.dogModel.find();
  }

  /**
   * 根据ID查找一个狗记录
   * @param id 狗的ID
   * @returns 返回找到的狗记录,如果没有找到则返回null
   */
  findOne(id: string) {
    return this.dogModel.findById(id);
  }

  /**
   * 根据ID更新一个狗记录
   * @param id 狗的ID
   * @param updateDogDto 更新狗所需的DTO
   * @returns 返回更新后的狗记录
   */
  update(id: string, updateDogDto: UpdateDogDto) {
    return this.dogModel.findByIdAndUpdate(id, updateDogDto);
  }

  /**
   * 根据ID删除一个狗记录
   * @param id 狗的ID
   * @returns 返回删除操作的结果
   */
  remove(id: string) {
    return this.dogModel.findByIdAndDelete(id);
  }
}

DogController 修改

DogController 基本没变化,只是把 +d 改成 id:

postman 测试

创建

使用 post 请求创建两个 dog:

查询

get 请求查询全部:

查询下单个:

修改

修改下:

在查询下:

删除

删除下大黄:

查询下:

Mongodb Compass 里点击刷新,也能看到对应数据:

这就是在 nest 里对 MongoDB 做 CRUD 的方式。

相关推荐
C语言魔术师12 分钟前
【小游戏篇】三子棋游戏
前端·算法·游戏
匹马夕阳1 小时前
Vue 3中导航守卫(Navigation Guard)结合Axios实现token认证机制
前端·javascript·vue.js
你熬夜了吗?1 小时前
日历热力图,月度数据可视化图表(日活跃图、格子图)vue组件
前端·vue.js·信息可视化
桂月二二8 小时前
探索前端开发中的 Web Vitals —— 提升用户体验的关键技术
前端·ux
hunter2062069 小时前
ubuntu向一个pc主机通过web发送数据,pc端通过工具直接查看收到的数据
linux·前端·ubuntu
qzhqbb9 小时前
web服务器 网站部署的架构
服务器·前端·架构
刻刻帝的海角9 小时前
CSS 颜色
前端·css
九酒9 小时前
从UI稿到代码优化,看Trae AI 编辑器如何帮助开发者提效
前端·trae
浪浪山小白兔10 小时前
HTML5 新表单属性详解
前端·html·html5
lee57610 小时前
npm run dev 时直接打开Chrome浏览器
前端·chrome·npm