Typegoose 模型和多对多关系
Typegoose Models and Many to Many Relationships
所以我正在使用 NestJs 和 Typegoose 构建一个后端,具有以下模型:
部门
@modelOptions({ schemaOptions: { collection: 'user_department', toJSON: { virtuals: true }, toObject: { virtuals: true }, id: false } })
export class Department {
@prop({ required: true })
_id: mongoose.Types.ObjectId;
@prop({ required: true })
name: string;
@prop({ ref: () => User, type: String })
public supervisors: Ref<User>[];
members: User[];
static paginate: PaginateMethod<Department>;
}
用户
@modelOptions({ schemaOptions: { collection: 'user' } })
export class User {
@prop({ required: true, type: String })
_id: string;
@prop({ required: true })
userName: string;
@prop({ required: true })
firstName: string;
@prop({ required: true })
lastName: string;
[...]
@prop({ ref: () => Department, default: [] })
memberOfDepartments?: Ref<Department>[];
static paginate: PaginateMethod<User>;
}
你可能猜到了,一个用户可能在多个部门,一个部门可以有很多成员(用户)。由于部门的数量或多或少是有限的(与用户相比),我决定使用一种嵌入方式,如下所述:Two Way Embedding vs. One Way Embedding in MongoDB (Many-To-Many)。这就是用户持有数组“memberOfDepartments”但部门不保存成员数组的原因(因为缺少@prop)。
第一个问题,当我请求Department-object时,如何查询它的成员呢?查询必须查找 department._id 在数组 memberOfDepartments.
中的用户
我在这里尝试了很多东西,比如虚拟填充:https://typegoose.github.io/typegoose/docs/api/virtuals/#virtual-populate 在 department.model 上这样:
@prop({
ref: () => User,
foreignField: 'memberOfDepartments',
localField: '_id', // compare this to the foreign document's value defined in "foreignField"
justOne: false
})
public members: Ref<User>[];
但它不会输出 属性。我的猜测是,这只适用于一个站点上的一对多...我也尝试过 set/get 但我在 DepartmentModel 中使用 UserModel 时遇到了问题。
目前我在服务中这样做是在“作弊”:
async findDepartmentById(id: string): Promise<Department> {
const res = await this.departmentModel
.findById(id)
.populate({ path: 'supervisors', model: User })
.lean()
.exec();
res.members = await this.userModel.find({ memberOfDepartments: res._id })
.lean()
.exec()
if (!res) {
throw new HttpException(
'No Department with the id=' + id + ' found.',
HttpStatus.NOT_FOUND,
);
}
return res;
}
..但我认为这不是正确的解决方案,因为我猜它属于模型。
第二个问题 是,我将如何处理一个部门的删除导致我必须删除对该部门的引用。在用户中?
我知道那里有 mongodb 和 mongoose 的文档,但我无法理解如何以“typegoose 方式”完成此操作,因为 typegoose 文档似乎仅限于我。任何提示表示赞赏。
所以,这不容易找到,希望这个答案能帮助别人。我仍然认为有必要记录更多的基本内容——比如在对象被删除时删除对对象的引用。就像,任何有参考文献的人都需要这个,但在任何文档(typegoose、mongoose、mongodb)中都没有给出完整的示例。
答案一:
@prop({
ref: () => User,
foreignField: 'memberOfDepartments',
localField: '_id', // compare this to the foreign document's value defined in "foreignField"
justOne: false
})
public members: Ref<User>[];
这就是问题中定义虚拟的正确方法。但是我做错了,我认为不是那么明显:我不得不打电话给
.populate({ path: 'members', model: User })
明确如
const res = await this.departmentModel
.findById(id)
.populate({ path: 'supervisors', model: User })
.populate({ path: 'members', model: User })
.lean()
.exec();
如果你不这样做,你将根本看不到 属性 成员。 我对此有疑问,因为如果你这样做像主管这样的参考字段,您至少会得到一个数组 ob objectIds。但是如果你不 pupulate 虚拟机,你根本得不到任何成员字段。
回答2:
我的研究使我得出结论,最好的解决方案是使用预挂钩。基本上你可以定义一个函数,它在执行特定操作之前调用(如果你想在之后使用 post-hook)。在我的例子中,操作是“删除”,因为我想在删除文档本身之前删除引用。
你可以用这个装饰器在typegoose中定义一个pre-hook,只要把它放在你的模型前面即可:
@pre<Department>('deleteOne', function (next) {
const depId = this.getFilter()["_id"];
getModelForClass(User).updateMany(
{ 'timeTrackingProfile.memberOfDepartments': depId },
{ $pull: { 'timeTrackingProfile.memberOfDepartments': depId } },
{ multi: true }
).exec();
next();
})
export class Department {
[...]
}
在我的研究中发现的很多灵魂都使用了“remove”,它在你调用 f.e 时被调用。 departmentmodel.remove()。不要使用它,因为 remove() 已被弃用。请改用“deleteOne()”。使用“const depId = this.getFilter()["_id"];”您可以访问将在操作中删除的文档的 ID。
所以我正在使用 NestJs 和 Typegoose 构建一个后端,具有以下模型:
部门
@modelOptions({ schemaOptions: { collection: 'user_department', toJSON: { virtuals: true }, toObject: { virtuals: true }, id: false } })
export class Department {
@prop({ required: true })
_id: mongoose.Types.ObjectId;
@prop({ required: true })
name: string;
@prop({ ref: () => User, type: String })
public supervisors: Ref<User>[];
members: User[];
static paginate: PaginateMethod<Department>;
}
用户
@modelOptions({ schemaOptions: { collection: 'user' } })
export class User {
@prop({ required: true, type: String })
_id: string;
@prop({ required: true })
userName: string;
@prop({ required: true })
firstName: string;
@prop({ required: true })
lastName: string;
[...]
@prop({ ref: () => Department, default: [] })
memberOfDepartments?: Ref<Department>[];
static paginate: PaginateMethod<User>;
}
你可能猜到了,一个用户可能在多个部门,一个部门可以有很多成员(用户)。由于部门的数量或多或少是有限的(与用户相比),我决定使用一种嵌入方式,如下所述:Two Way Embedding vs. One Way Embedding in MongoDB (Many-To-Many)。这就是用户持有数组“memberOfDepartments”但部门不保存成员数组的原因(因为缺少@prop)。
第一个问题,当我请求Department-object时,如何查询它的成员呢?查询必须查找 department._id 在数组 memberOfDepartments.
中的用户我在这里尝试了很多东西,比如虚拟填充:https://typegoose.github.io/typegoose/docs/api/virtuals/#virtual-populate 在 department.model 上这样:
@prop({
ref: () => User,
foreignField: 'memberOfDepartments',
localField: '_id', // compare this to the foreign document's value defined in "foreignField"
justOne: false
})
public members: Ref<User>[];
但它不会输出 属性。我的猜测是,这只适用于一个站点上的一对多...我也尝试过 set/get 但我在 DepartmentModel 中使用 UserModel 时遇到了问题。
目前我在服务中这样做是在“作弊”:
async findDepartmentById(id: string): Promise<Department> {
const res = await this.departmentModel
.findById(id)
.populate({ path: 'supervisors', model: User })
.lean()
.exec();
res.members = await this.userModel.find({ memberOfDepartments: res._id })
.lean()
.exec()
if (!res) {
throw new HttpException(
'No Department with the id=' + id + ' found.',
HttpStatus.NOT_FOUND,
);
}
return res;
}
..但我认为这不是正确的解决方案,因为我猜它属于模型。
第二个问题 是,我将如何处理一个部门的删除导致我必须删除对该部门的引用。在用户中?
我知道那里有 mongodb 和 mongoose 的文档,但我无法理解如何以“typegoose 方式”完成此操作,因为 typegoose 文档似乎仅限于我。任何提示表示赞赏。
所以,这不容易找到,希望这个答案能帮助别人。我仍然认为有必要记录更多的基本内容——比如在对象被删除时删除对对象的引用。就像,任何有参考文献的人都需要这个,但在任何文档(typegoose、mongoose、mongodb)中都没有给出完整的示例。
答案一:
@prop({
ref: () => User,
foreignField: 'memberOfDepartments',
localField: '_id', // compare this to the foreign document's value defined in "foreignField"
justOne: false
})
public members: Ref<User>[];
这就是问题中定义虚拟的正确方法。但是我做错了,我认为不是那么明显:我不得不打电话给
.populate({ path: 'members', model: User })
明确如
const res = await this.departmentModel
.findById(id)
.populate({ path: 'supervisors', model: User })
.populate({ path: 'members', model: User })
.lean()
.exec();
如果你不这样做,你将根本看不到 属性 成员。 我对此有疑问,因为如果你这样做像主管这样的参考字段,您至少会得到一个数组 ob objectIds。但是如果你不 pupulate 虚拟机,你根本得不到任何成员字段。
回答2: 我的研究使我得出结论,最好的解决方案是使用预挂钩。基本上你可以定义一个函数,它在执行特定操作之前调用(如果你想在之后使用 post-hook)。在我的例子中,操作是“删除”,因为我想在删除文档本身之前删除引用。 你可以用这个装饰器在typegoose中定义一个pre-hook,只要把它放在你的模型前面即可:
@pre<Department>('deleteOne', function (next) {
const depId = this.getFilter()["_id"];
getModelForClass(User).updateMany(
{ 'timeTrackingProfile.memberOfDepartments': depId },
{ $pull: { 'timeTrackingProfile.memberOfDepartments': depId } },
{ multi: true }
).exec();
next();
})
export class Department {
[...]
}
在我的研究中发现的很多灵魂都使用了“remove”,它在你调用 f.e 时被调用。 departmentmodel.remove()。不要使用它,因为 remove() 已被弃用。请改用“deleteOne()”。使用“const depId = this.getFilter()["_id"];”您可以访问将在操作中删除的文档的 ID。