Simba
Simba
MIDWAY

Midway-Mongoose实践

Midway-Mongoose实践
5 min read
#Midway

前言

之前写了个Midway & Typescript & Mongoose的demo 但是呢 才发现从utils创建出来的model无法获取model中方法的 这就很尴尬了 第一版的逻辑是这样的

1.在utils工具文件中暴露出一个getModel(mongoose: any, options: ISchemaOption)方法 通过定义Schema设定来对应创建model并返回创建好的对象

// model/utils.ts
import { ISchemaOption } from "../interface";
 
export function getModel(mongoose: any, options: ISchemaOption) {
  if(mongoose.models[options.modelName]){
    return mongoose.models[options.modelName];
  }
  const Schema = mongoose.Schema
  const schema = new Schema(options.schemaOptions)
  return mongoose.model(options.modelName, schema, options.collection)
}

2.在对应model文件中通过@plugin来引入mongoose 传入引入的mongoose调用上面一步创建好的getModel方法 生成可操作数据库的model对象

// model/posts.ts
import { provide, plugin } from 'midway'
import { Mongoose } from 'mongoose';
import { getModel } from './utils';
 
const options = {
  modelName: 'Posts',
  collection: 'posts',
  schemaOptions: {
    title: String,
    brief: String,
    time: { type: Date , default: Date.now },
    content: String,
    count: Number,
    isDelete: { type: Number , default: 0 }
  }
}
 
@provide()
export class PostModel {
 
  @plugin()
  mongoose: Mongoose;
 
  getModel() {
    return getModel(this.mongoose, options)
  }
}

然后问题就出来了Typescript并没有检索出里面可以操作数据库的方法 -> 排查发现当时也并没有定义getModel方法的返回类型 所以导致Typescript没办法检索数据model里拥有的方法

分(chui)析(shui)

通过VS Code快捷键command + click可以进入到定义mongoose类型的源码里 这里就说一下我一顿瞎分析的步骤吧

1.进入到声明model的地方 从声明中能看出来model的返回值是一个继承于DocumentModel类型泛型 并且还有声明合并 推测是函数重载(?

// @types/mongoose/index.d.ts
/**
  * Defines a model or retrieves it.
  * Models defined on the mongoose instance are available to all connection
  *   created by the same mongoose instance.
  * @param name model name
  * @param collection (optional, induced from model name)
  * @param skipInit whether to skip initialization (defaults to false)
  */
export function model<T extends Document>(name: string, schema?: Schema, collection?: string,
  skipInit?: boolean): Model<T>;
export function model<T extends Document, U extends Model<T>>(
  name: string,
  schema?: Schema,
  collection?: string,
  skipInit?: boolean
): U;

2.进入到声明Model的地方 发现它竟然是个any??? 当时有点懵逼 难倒真的要Anyscript了嘛 然后发现下面定义Model的接口的地方是应该自己自定义的一个Document泛型对象的 里面就包含着数据库操作的方法

// @types/mongoose/index.d.ts
/*
  * section model.js
  * http://mongoosejs.com/docs/api.html#model-js
  */
export var Model: Model<any>;
interface Model<T extends Document, QueryHelpers = {}> extends NodeJS.EventEmitter, ModelProperties {
  // ModelProperties...
}

到此关于model对象的源码结构就基本了解了

实现步骤

1.Model层

import * as Mongoose from 'mongoose'
 
// 定义需要的Document的结构
export interface IPosts extends Mongoose.Document {
  title: string,
  brief: string,
  time: Date
  content: string,
  count: number,
  isDelete: number
}
 
// 对应的Schema结构
const PostSchema: Mongoose.Schema = new Mongoose.Schema({
  title: String,
  brief: String,
  time: { type: Date , default: Date.now },
  content: String,
  count: Number,
  isDelete: { type: Number , default: 0 }
})
 
// 将model对象暴露给service
export default () => Mongoose.model<IPosts>('posts', PostSchema)

2.Service层

import postsModel from '../model/posts'
 
@provide('postService')
export class PostService implements IPostService{
  async getPosts(): Promise<IPosts[]>{
      const result: IPosts[] = await postsModel().find()
      return result
  }
}

上面这种方法只是我自己踩出来的写法 但是特别不优雅 如果有更好实践方法欢迎来issues

Links

Complete guide for Typescript with Mongoose for Node.js

Mongoose the Typescript way…?

midway-mongoose-demo

到时候写好一个完整的demo再填github上的坑吧 留下了菜鸡的眼泪💧