使用 Typescript 在 Mongoose 中缺少子文档方法

Posted

技术标签:

【中文标题】使用 Typescript 在 Mongoose 中缺少子文档方法【英文标题】:Missing Subdocument Methods in Mongoose with Typescript 【发布时间】:2020-01-02 00:48:20 【问题描述】:

我正在做一个项目,需要通过子文档_id 从模型中检索特定的子文档。然后我计划对这些子文档进行更新并保存主文档。 mongoose 子文档文档列出了您可以在 parent.children 数组上调用的许多方法,但是 javascript 中尚不存在数组的方法会给出错误消息,指出它们不存在且无法编译。我正在参考这个文档:https://mongoosejs.com/docs/subdocs.html

我知道应该能够使用.findOneAndUpdate 进行更新,并且使用runValidators 选项仍然应该验证所有内容,但我也想只检索子文档本身。

我查看了这篇文章:MongoDB, Mongoose: How to find subdocument in found document?,我将评论说,如果注册了子文档架构,它会自动为该架构创建一个集合,该集合仅在单独保存该架构时才会创建。您不能使用ChildModel.findOne() 来检索子文档,因为该集合不存在,其中没有任何内容。

拥有IChildModel 扩展mongoose.Types.Subdocument 并拥有IParent 接口引用而不是IChild 并且不注册ChildModel 除了不再允许对.push() 的调用不接受简单之外没有任何改变对象(缺少 30 个左右的属性)。同样在IParent 接口中使用此方法尝试mongoose.Types.Array<IChild> 不会改变任何内容。

IParent 接口更改为将mongoose.Types.Array<IChild> 用于children 属性允许addToSet() 工作,但不允许id()create() 工作

我正在使用 Mongoose 版本 5.5.10、MongoDb 版本 4.2.0 和 Typescript 版本 3.4.5

import mongoose,  Document, Schema  from "mongoose";

// Connect to mongoDB with mongoose
mongoose.connect(process.env.MONGO_HOST + "/" + process.env.DB_NAME, useNewUrlParser: true, useFindAndModify: false);

// Interfaces to be used throughout application
interface IParent 
    name: string;
    children: IChild[];


interface IChild 
    name: string;
    age: number;


// Model interfaces which extend Document
interface IParentModel extends IParent, Document  
interface IChildModel extends IChild, Document  

// Define Schema
const Child: Schema = new Schema(
    name: 
        type: String,
        required: true
    ,
    age: 
        type: Number,
        required: true
    
);

const ChildSchema: Schema = Child;

const Parent: Schema = new Schema(
    name: 
        type: String,
        required: true
    ,
    children: [ChildSchema]
);

const ParentSchema: Schema = Parent;

// Create the mongoose models
const ParentModel = mongoose.model<IParentModel>("Parent", Parent);
const ChildModel = mongoose.model<IChildModel>("Child", Child);

// Instantiate instances of both models
const child = new ChildModel(name: "Lisa", age: 7);
const parent = new ParentModel(name: "Steve", children: [child]);

const childId = child._id;

// Try to use mongoose subdocument methods
const idRetrievedChild = parent.children.id(childId); // Property 'id' does not exist on type 'IChild[]'.ts(2339)
parent.children.addToSet( name: "Liesl", age: 10 ); // Property 'addToSet' does not exist on type 'IChild[]'.ts(2339)
parent.children.create( name: "Steve Jr", age: 2 ); // Property 'create' does not exist on type 'IChild[]'.ts(2339)

// If I always know the exact position in the array of what I'm looking for
const arrayRetrievedChild = parent.children[0]; // no editor errors
parent.children.unshift(); // no editor errors
parent.children.push( name: "Emily", age: 18 ); // no editor errors

【问题讨论】:

【参考方案1】:

总额: 现在,我将使用一个非常快速且肮脏的 polyfill 解决方案,但实际上并不能回答我的问题:

declare module "mongoose" 
    namespace Types 
        class Collection<T> extends mongoose.Types.Array<T> 
            public id: (_id: string) => (T | null);
        
    

然后我们这样声明IParent

interface IParent 
    name: string;
    children: mongoose.Types.Collection<IChild>;

因为函数 id() 已经存在而 typescript 只是不知道它,所以代码可以工作并且 polyfill 让它编译。

Even Grosser: 否则,为了获得更快、更脏的解决方案,当您创建父模型实例时,只需将其类型转换为 any 并丢弃所有打字稿检查:

const child = new ChildModel(name: "Lisa", age: 7);
const parent: any = new ParentModel(name: "Steve", children: [child]);
const idRetrievedChild = parent.children.id(childId); // works because declared any

【讨论】:

【参考方案2】:

回复有点晚,但我查看了输入内容并找到了DocumentArray

import  Document, Embedded, Types  from 'mongoose';
interface IChild extends Embedded 
    name: string;


interface IParent extends Document 
    name: string;
    children: Types.DocumentArray<IChild>;

只是想把它放在这里以防其他人需要它。

【讨论】:

几乎,您只需要为 IChild 使用 Embedded 而不是 Document。这样,除了通常的文档方法之外,您还可以访问子文档方法,例如 parent() 和 ownerDocument()。 在 mongoose 中找不到 Embedded,是否已删除? Embedded 位于Types.Embedded 看起来这些天(猫鼬 6)Types.Subdocument 是你需要的 - Types.Embedded 不再存在

以上是关于使用 Typescript 在 Mongoose 中缺少子文档方法的主要内容,如果未能解决你的问题,请参考以下文章

无法在 NodeJS 中使用 Mongoose 和 Typescript 扩展基类

如何在 typescript 中使用 mongoose 在 mongodb 中插入数据?

使用 Typescript 在 Mongoose 中缺少子文档方法

使用 Typescript 在 Mongoose 中缺少子文档方法

使用`let cached = global.mongoose`时出现Next.js + Typescript + mongoose错误

使用 Typescript 从 Mongoose 模型访问文档属性