Mongoose 来操作我们的数据库

89 阅读7分钟

官方文档:mongoosejs.net/docs/

安装 mongoose

项目中我们会用到 Mongoose 来操作我们的数据库,Nest 官方为我们提供了一个 Mongoose 的封装,我们需要安装 mongoose 和 @nestjs/mongoose:

npm install mongoose @nestjs/mongoose --save

创建user依赖的几个文件

# 创建模块
nest g module user server

# 创建控制器
nest g controller user server

# 创建 Provider 服务
nest g service user server

连接数据库引入 根模块Mongoose

连接数据之前,我们要先在根模块,也就是 app.module.ts 中引入 Mongoose 的连接模块:

 
// app.module.ts
//MongooseModule.forRoot("mongodb://user:pass@ip:port/database");
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './server/user/user.module';
@Module({  imports: [MongooseModule.forRoot('mongodb://localhost:27017/Users'), UserModule],
  controllers: [AppController],
  providers: [AppService]
})
export class AppModule {}

error:如果这里运行后端文件是又可能会报错,如果报错就添加:

npm install @types/mongoose --dev

引入 分模块Mongoose

这里我们先要创建一个数据表的格式,在 src/server/user 文件夹下创建一个 user.schema.ts 文件,定义一个数据表的格式:

// user.schema.ts
import { Schema } from 'mongoose';
export const userSchema = new Schema({
  // _id: { type: String, required: true }, // 覆盖 Mongoose 生成的默认 _id
  user_name: { type: String, required: true },
  password: { type: String, required: true }
});

然后将我们的 user.module.ts 文件修改成这样:

 
// user.module.ts
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { UserController } from './user.controller';
import { userSchema } from './user.schema';
import { UserService } from './user.service';
@Module({
  imports: [MongooseModule.forFeature([{ name: 'Users', schema: userSchema }])],
  controllers: [UserController],
  providers: [UserService]
})
export class UserModule {}

CRUD

创建两个文件做数据类型的定义:

 
// user.interface.ts
import { Document } from 'mongoose';
export interface User extends Document {
  //   readonly _id: string;
  readonly user_name: string;
  readonly password: string;
}

//////////////////////////////////////////////////////////
// user.dto.ts
export class CreateUserDTO {
  readonly user_name: string;
  readonly password: string;
}  
export class EditUserDTO {
  readonly user_name: string;
  readonly password: string;
}

修改 user.service.ts 文件,为 UserService 类添加一个构造函数,让其在实例化的时候能够接收到数据库 Model,这样才能在类中的方法里操作数据库。

因为 mongoose 操作数据库其实是异步的,所以这里我们使用 async 函数来处理异步的过程。

import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { CreateUserDTO, EditUserDTO } from './user.dto';
import { User } from './user.interface';
@Injectable()
export class UserService {
    constructor(@InjectModel('Users') private readonly userModel: Model<User>) { }
    // 查找所有用户
    async findAll(): Promise<User[]> {
        const users = await this.userModel.find();
        return users;
    }
    // 查找单个用户
    async findOne(user_name: string): Promise<User> {
        return await this.userModel.findOne({user_name});
    }
    // 添加单个用户
    async addOne(body: CreateUserDTO): Promise<void> {
        await this.userModel.create(body);
    }
    // 编辑单个用户
    async updateOne(user_name: string, newPassword: string): Promise<void> {
        await this.userModel.updateOne({user_name},{password: newPassword});
    }
    // 删除单个用户
    async deleteOne(user_name: string): Promise<void> {
        await this.userModel.deleteOne({user_name});
    }
}

修改 user.controller.ts 设置路由了,将客户端的请求进行处理,调用相应的服务实现相应的功能:

import {
    Controller,
    Body,
    // Delete,
    Get,
    Param,
    Post,
    Put
} from '@nestjs/common';
import { CreateUserDTO, EditUserDTO } from './user.dto';
import { User } from './user.interface';
import { UserService } from './user.service';
interface UserResponse<T = unknown> {
  //这是一个 TypeScript 接口定义,用于描述用户响应的数据结构。它包含一个泛型参数 T,默认值为 unknown,其中包含 code(响应码)、data(响应数据,可选)和 message(响应消息)三个属性。
    code: number;
    data?: T;
    message: string;
}
@Controller('user')
export class UserController {
    constructor(private readonly userService: UserService) { }
    // GET /user/users
    @Get('/users')
    async findAll(): Promise<UserResponse<User[]>> {
        return {
            code: 200,
            data: await this.userService.findAll(),
            message: '查询成功.'
        };
    }
    // POST /user/find_one
    @Post('/find_one')
    async findOne(@Body() userData: { user_name: string }): Promise<UserResponse> {
        await this.userService.findOne(userData.user_name); // 使用传入的 user_name 参数
        return {
            code: 200,
            data: await this.userService.findOne(userData.user_name),
            message: '查询成功.'
        };
    }
    // POST /user/user
    @Post('/user')
    async addOne(@Body() body: CreateUserDTO): Promise<UserResponse> {
        await this.userService.addOne(body);
        return {
            code: 200,
            message: '添加成功.'
        };
    }
    // POST /user/upd
    @Post('/upd')
    async updateOne(@Body() userData: { user_name: string, newPassword: string }): Promise<UserResponse> {
        await this.userService.updateOne(userData.user_name, userData.newPassword); // 使用传入的 user_name 参数
        return {
            code: 200,
            message: '修改成功.'
        };
    }
    // Post /user/deluser
    @Post('/deluser')
    async deleteOne(@Body() userData: { user_name: string }): Promise<UserResponse> {
        await this.userService.deleteOne(userData.user_name); // 使用传入的 user_name 参数
        return {
            code: 200,
            message: '删除成功.'
        };
    }
}

nest.js调用mongoose来操作mongoDB数据库的后端程序就已经写好了。

小程序端调用

这里只包含了与后端交互的部分代码

Page({
  ...
  tijiao(){
    wx.request({
      url: 'http://localhost:3000/user/user',
      method:'POST',
      data:{
        user_name:this.data.inputValue1,
        password:this.data.inputValue2
      },
    })
  },
  // 删除
  shanchu() {
    wx.request({
        url: 'http://localhost:3000/user/deluser',
        method: 'POST',
        data: {
            user_name: this.data.inputValue_del,
        },
        success: (res) => {
            // 处理成功的情况
            console.log(res.data); // 可以打印后端返回的数据
        },
        fail: (error) => {
            // 处理失败的情况
            console.error(error);
        }
    })
  },
  // 修改
  xiugai(){
    wx.request({
      url: 'http://localhost:3000/user/upd',
      method:'POST',
      data:{
        // 名字
        user_name:this.data.inputValue1_upda,
        // 修改后的年龄
        newPassword:this.data.inputValue2_upda,
      },
    })
  },
  // 查询
  find(){
    wx.request({
      url: 'http://localhost:3000/user/find_one',
      method: 'POST',
      data:{
        user_name:this.data.inputValue1_find,
      },
      success: function(res) {
        console.log('服务器返回的数据:', res.data);
        if (res.data && res.data.length > 0) {
          const records = res.data;
          records.forEach(record => {
            console.log('记录:', record);
          });
        } else {
          console.log('未找到匹配的记录');
        }
      },
      fail: function(error) {
        console.error('请求失败:', error);
      }
    });
  },
  find_all(){
    wx.request({
      url: 'http://localhost:3000/user/users',
      method: 'GET',
      success: function(res) {
        // 请求成功,处理从服务器返回的数据
        console.log('服务器返回的数据:', res.data);
 
        // 检查是否找到匹配的记录
        if (res.data && res.data.length > 0) {
          // 处理返回的记录数据
          const records = res.data;
          records.forEach(record => {
            console.log('记录:', record);
            // 在这里进行您的处理逻辑,例如显示在界面上
          });
        } 
      },
      fail: function(error) {
        // 请求失败,处理错误
        console.error('请求失败:', error);
        // 在界面上显示错误信息,告知用户请求失败
      }
    });
  }
})

多数据库

有的项目需要多数据库连接,可以在这个模块中实现。要使用多连接,首先要创建连接,在这种情况下,连接必须**要有名称。

//app.module.ts
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';

@Module({
  imports: [
    MongooseModule.forRoot('mongodb://localhost/test', {
      connectionName: 'cats',
    }),
    MongooseModule.forRoot('mongodb://localhost/users', {
      connectionName: 'users',
    }),
  ],
})
export class AppModule {}

在设置中,要告诉MongooseModule.forFeature()方法应该使用哪个连接。

// cat/cat.module.ts
@Module({
  imports: [MongooseModule.forFeature([{ name: 'Cat', schema: CatSchema }], 'cats')],
})
export class CatModule {}

分页


// cats.service.ts
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Cat } from './cat.schema';
 
@Injectable()
export class CatsService {
  constructor(@InjectModel(Cat.name) private readonly catModel: Model<Cat>,) {}

  async findAll(page: number, limit: number): Promise<Cat[]> {
    return this.catModel.find().skip((page - 1) * limit).limit(limit).exec();
  }
}

CURD

数据库的基本操作包括四个,增加(create),删除(delete),修改(update),查(read)

// 插入一条
SongModel.create({
        title:'给我一首歌的时间',
        author: 'Jay'
    }, 
    function(err, data){
        //错误
        console.log(err);
        //插入后的数据对象
        console.log(data);
});


// 批量插入
PhoneModel.insertMany([
    {
        brand:'华为',
        color:'灰色',
        price:2399,
        tags:['电量大','屏幕大','信号好']
    },
    {
        brand:'小米',
        color:'白色',
        price:2099,
        tags:['电量大','屏幕大','信号好']
    }],
    (err,data)=>{
    if(err) throw err;
    console.log('写入成功');
    mongoose.connection.close();
})

// 删除一条数据
SongModel.deleteOne(
    {_id:'5dd65f32be6401035cb5b1ed'}, 
    function(err){
        if(err) throw err;
        console.log('删除成功');
        mongoose.connection.close();
    }
)

// 批量删除
SongModel.deleteMany({author:'Jay'}, 
function(err){
    if(err) throw err;
    console.log('删除成功');
    mongoose.connection.close();
})

// 更新一条数据
SongModel.updateOne({author: 'JJ Lin'}, {author: '林俊杰'}, 
function (err) {
    if(err) throw err;
    mongoose.connection.close();
})

// 批量更新数据
SongModel.updateMany({author: 'Leehom Wang'}, {author: '王力宏'}, function (err) {
    if(err) throw err;
    mongoose.connection.close();
})

// 查询一条数据
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();
})

//不加条件查询
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();
});

数据处理

返回指定字段

//0:不要的字段
//1:要的字段
SongModel.find()
.select({_id:0,title:1})
.exec(function(err,data){
    if(err) throw err;
    console.log(data);
    mongoose.connection.close();
});

数据排序

//sort 排序
//1:升序
//-1:倒序
SongModel.find()
.sort({hot:1})
.exec(function(err,data){
    if(err) throw err;
    console.log(data);
    mongoose.connection.close();
});

数据截取

//skip 跳过 limit 限定
SongModel.find()
.skip(10)
.limit(10)
.exec(function(err,data){
    if(err) throw err;
    console.log(data);
    mongoose.connection.close();
})

连表操作

// 定义User模型
const UserSchema = new mongoose.Schema({
  name: String,
  email: String
});
 
const User = mongoose.model('User', UserSchema);
 
// 定义Post模型
const PostSchema = new mongoose.Schema({
  content: String,
  author: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User'
  }
});
 
const Post = mongoose.model('Post', PostSchema);
 
// 现在我们要获取一个帖子并且包含作者信息
Post.findOne({ _id: postId }).populate('author').exec((err, post) => {
  if (err) {
    // 处理错误
    console.error(err);
  } else {
    // 获取到的帖子,包含作者信息
    console.log(post);
  }
});

创建、查询

const studentSchema = Schema({
    name: String,
    age: Number,
    xuexiao: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'School' //这里要写你指向的数据库表名字
    }
});

const schoolSchemal = Schema({
    name: String,
});

const studentModel = mongoose.model('Student', studentSchema);
const schoolModel = mongoose.model('School', schoolSchemal);


//创建学校
const school = schoolModel({
    name: '第一高中'
});

school.save(function(err) {
    if (err) {
        console.log(err);
        return;
    }
    //创建了学生1
    const student = studentModel({
        name: '小红',
        age: 16,
        xuexiao: school._id //把学校的唯一id给绑定到学生身上。
    });
    student.save(function(err) {
        if (err) console.log(err);
    });

    //创建了学生2
    const student2 = studentModel({
        name: '小明',
        age: 16,
        xuexiao: school._id //把学校的唯一id给绑定到学生身上。
    });
    student2.save(function(err) {
        if (err) console.log(err);
    });
});

//查询//////////////////////////
studentModel
    .findOne({
        name: '小红'
    })
    .exec(function(err, student) {
        console.log('id: ', student.xuexiao, 'name: ', student.xuexiao.name);
        //id:  58ddd5db6216a905ce973de4 name:  undefined
        //这里打印只有id,并没有属性
    });

studentModel
    .findOne({
        name: '小红'
    })
    .populate('xuexiao') //加上这句话也就有了属性值, 
    .exec(function(err, student) {
        console.log('id: ', student.xuexiao, 'name: ', student.xuexiao.name);
        //id:  { _id: 58ddd5db6216a905ce973de4, name: '第一高中', __v: 0 } name:  第一高中
    });

Mongoose 预定义修饰符 Getter 与 Setter

// mongoose 预定义修饰符
trim:true  //去首尾空格
lowercase:true //转化为小写
uppercase:true  //转化为大写

var UserSchema=mongoose.Schema({
  name:{
    type:String,
    trim:true  //去首尾空格
  },
  age:Number,
  status:{
    type:Number,
    default:1,
        lowercase:true 
  }
})

自定义修饰符 Setter 和 Getter

// 加入我们要对 url 进行格式化,如果数据以 http:// 开头我们原样存入,如果不以 http:// 开头,添加之后再存入数据库
const ArticleSchema = mongoose.Schema({
    title:String,
    author:String,
    img:{
        type:String,
        set(params){
            if(!params) return params;
            return /^http:\/\//.test(params) ? params : 'http://' + params
        },
        get(params){
            return 'mongodb:' + params;
        }
    }
});

const article2 = new Article({
    title:'武汉加油',
    author: '人民日报',
    img:'wuhan/pic02'
});

// 这样存入数据库的数据就是 http://wuhan/pico2