使用 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 的方式。

相关推荐
生椰拿铁You3 分钟前
09 —— Webpack搭建开发环境
前端·webpack·node.js
狸克先生14 分钟前
如何用AI写小说(二):Gradio 超简单的网页前端交互
前端·人工智能·chatgpt·交互
baiduopenmap29 分钟前
百度世界2024精选公开课:基于地图智能体的导航出行AI应用创新实践
前端·人工智能·百度地图
loooseFish36 分钟前
小程序webview我爱死你了 小程序webview和H5通讯
前端
请叫我欧皇i1 小时前
html本地离线引入vant和vue2(详细步骤)
开发语言·前端·javascript
533_1 小时前
[vue] 深拷贝 lodash cloneDeep
前端·javascript·vue.js
guokanglun1 小时前
空间数据存储格式GeoJSON
前端
zhang-zan1 小时前
nodejs操作selenium-webdriver
前端·javascript·selenium
猫爪笔记2 小时前
前端:HTML (学习笔记)【2】
前端·笔记·学习·html
brief of gali2 小时前
记录一个奇怪的前端布局现象
前端