NestJS/Mongoose - Create an Array of Object with reference to another Schema - node.js

I'm building the back-end side of a personal application, and I have two particular models/schemas. One if for Products an another for Orders. I want to do the following:
The Orders need to have the following array with this structure:
products: [
{
product: string;
quantity: number;
}
]
The product should be an ObjectId of mongo, and this needs a reference for a 'Product' model.
How I can reach this? I don't really know how to "type" this with the #Prop() decorator.
#Prop({
// HOW I DO THIS?
})
products: [{ quantity: number; product: mongoose.Types.ObjectId }];
This is my Order Schema:
import { Prop, Schema, SchemaFactory } from '#nestjs/mongoose';
import * as mongoose from 'mongoose';
import { Document } from 'mongoose';
export type OrderDocument = Order & Document;
#Schema()
export class Order {
#Prop({ type: String, required: true })
name: string;
#Prop({ type: Number, min: 0, required: true })
total: number;
#Prop({
type: String,
default: 'PENDING',
enum: ['PENDING', 'IN PROGRESS', 'COMPLETED'],
})
status: string;
#Prop({
// HOW I DO THIS?
})
products: [{ quantity: number; product: mongoose.Types.ObjectId }];
#Prop({
type: mongoose.Schema.Types.ObjectId,
ref: 'Customer',
required: true,
})
customer: mongoose.Types.ObjectId;
#Prop({
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true,
})
owner: mongoose.Types.ObjectId;
#Prop({ type: Date, default: Date.now() })
createdAt: Date;
}
export const OrderSchema = SchemaFactory.createForClass(Order);

#Prop({
type:[{quantity:{type:Number}, product:{type:Schema.Types.ObjectId}}]
})
products: { quantity: number; product: Product }[];

The best way to achieve both reference population and mongoose schema validation is to create a subschema for the nested entity.
import { Document, SchemaTypes, Types } from 'mongoose';
import { Prop, Schema, SchemaFactory } from '#nestjs/mongoose';
#Schema({ _id: false, versionKey: false })
class OrderProduct {
#Prop({ required: true })
quantity: number;
#Prop({ type: [SchemaTypes.ObjectId], ref: 'Product', required: true })
product: Product[];
}
const OrderProductSchema = SchemaFactory.createForClass(OrderProduct);
#Schema()
export class Order {
// other order schema props ...
#Prop([{ type: OrderProductSchema }])
products: OrderProduct[];
}

Related

Creation of duplicate IDs in arrays of different documents in mongo db

I want to add an element to the array of all collections in the city collection, but Mongo creates the ID as duplicate.
this is my code
await this.cityRepository.updateMany(
{},
{
$push: {
tags: {
title: tagValue.title,
description: tagValue.description,
applyToAllCity: tagValue.cityId ? false : true,
},
},
},
);
City Schema
export class BaseCity extends Document {
#Prop({
type: String,
required: true,
})
_id: string;
#Prop({ type: String, unique: true })
code: string;
#Prop({ type: String, ref: Province.name })
province: string | Province;
#Prop({ type: String })
faName: string;
}
#Schema({ timestamps: true })
#Schema({ collection: 'city', virtuals: true, _id: false, timestamps: true })
export class City extends BaseCity {
#Prop({ type: String })
imageName: string;
#Prop({ index: true, type: String })
enName: string;
#Prop({ type: Number })
displayOrder: number;
#Prop({ type: Boolean })
isFeatured: boolean;
#Prop({ type: Boolean })
isEnabled: boolean;
#Prop({ type: Coordinate })
coordinate: Coordinate;
#Prop([{ type: Region, ref: Region.name, default: [] }])
region: Region[];
#Prop([{ type: SubMenu }])
subMenu: SubMenu[];
#Prop([{ type: CityTags }])
tags: CityTags[];
}
const CitySchema = SchemaFactory.createForClass(City);
CitySchema.index({ faName: 'text' });
export { CitySchema };
DB
As you can see, ID 63ec8f47efbd82c8face341a is duplicated in all documents.
Is there a solution to solve this problem?
To avoid duplicate IDs, you could use the $addToSet instead of $push. The $addToSet adds an element to an array only if it does not already exist in the set.
Check this:
await this.cityRepository.updateMany(
{},
{
$addToSet: {
tags: {
title: tagValue.title,
description: tagValue.description,
applyToAllCity: tagValue.cityId ? false : true,
},
},
},
);
Update:
To keep unique ids
await this.cityRepository.updateMany(
{},
{
$push: {
tags: {
_id: new ObjectId(),
title: tagValue.title,
description: tagValue.description,
applyToAllCity: tagValue.cityId ? false : true,
},
},
},
);

How to auto update the following schema based upon certain condition?

Suppose I have Auction Schema as follows
import { Document, model, Schema } from "mongoose";
import { IProduct } from "./product";
const AuctionSchema = new Schema(
{
name: {
type: String,
required: true,
unique: true,
},
products: [{ type: Schema.Types.ObjectId, ref: "Product" }],
status: { type: Boolean, default: false },
duration: { type: Number },
start: { type: Date },
end: { type: Date },
},
{ timestamps: true }
);
export interface IAuction extends Document {
name: string;
status?: boolean;
products: IProduct["_id"][];
start?: Date;
end?: Date;
duration?: number;
}
const Auction = model<IAuction>("Auction", AuctionSchema);
export default Auction;
I want to update this auction related field i.e status to false automatically if the field start and end are same
How to do this?

How to seed data in NestJs & Mongoose?

I've shops and products collections in Mongodb.
Shops Schema
#Schema({ timestamps: true })
export class Shop extends Document {
#Prop({ required: true, index: { unique: true } })
id: string;
#Prop({ required: true, index: { unique: true } })
shop: string;
#Prop({ required: true })
accessToken: string;
#Prop({ required: true })
scope: string;
#Prop({ type: Date })
expires: Date;
#Prop({ type: Boolean })
isOnline: boolean;
#Prop({ type: String })
state: string;
}
export const ShopSchema = SchemaFactory.createForClass(Shop);
Products Schema
#Schema({ timestamps: true })
export class Product extends Document {
#Prop({ required: true, index: {unique: true} })
id: string
#Prop({ required: true })
title: string
#Prop({ required: true })
desc: string
#Prop({ required: true })
price: number
#Prop({ required: true })
stock: number
#Prop({ type: mongooseSchema.Types.ObjectId, ref: 'Shop', required: true })
shopId: mongooseSchema.Types.ObjectId;
}
export const ProductSchema = SchemaFactory.createForClass(Product);
Now I want to seed some dummy data (from json) into products collection whenever new shop is added. I've done using Nestjs Command and it worked perfectly. But I don't want to seed data by using command, so I tried on Nestjs lifecycle event method (which is not a proper way to do). Can anyone recommend me any other method?
I heard we can do using Mongoose hooks. If we can, please anyone explain about it.

When using MongoDB with TypeScript, should I add an _id field to the interfaces of my models?

This is the fist time I use MongoDB with Mongoose and Typescript. I am defining an interface for each model of my database.
Should I add the _id field to the interfaces I create, like I am doing in this code snippet?
import mongoose, { Model, Document, ObjectId } from "mongoose";
export interface IUser extends Document {
_id: ObjectId;
name: string;
email: string;
password: string;
created: Date;
isPremium: boolean;
img?: string;
applicantProfile?: string;
businessProfile?: string;
}
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true },
password: { type: String, required: true },
isPremium: { type: Boolean, required: true, default: true },
created: { type: Date, required: true, default: Date.now },
img: String,
applicantProfile: String,
businessProfile: String,
});
export const User: Model<IUser> = mongoose.model("User", userSchema)
The tutorials I have read about Mongo + Typescript don't do this, but I have found that, if I don't declare the _id field in my interface, then, when I try to access the _id field, it has a value of any, which I don't like.
I'd like to know how you create the interfaces of your models and whether adding the _id field to the interface is a good practice.

Typegoose ref with array of subdocuments

I'm trying to get nested subdocuments array using Typegoose.
Before refactoring with Typegoose, I had this working code with mongoose :
Interface :
export interface IFamily extends Document {
name: string;
products: IProduct[];
}
Schema :
const familySchema: Schema = new Schema({
name: { type: String },
products: [{ type: Schema.Types.ObjectId, ref: 'Product' }]
});
When I did Family.findById('5f69aa0a56ca5426b44a86c5'), I had an array of Product ObjectId in my JSON result.
After refoctoring, I use Typegoose :
Class :
#modelOptions({ schemaOptions: { collection: 'families' } })
export class Family {
#prop({ type: Schema.Types.ObjectId })
public _id?: string;
#prop({ type: String, required: false })
public name?: string;
#prop({ ref: () => Product, required: true, default: [] })
public products!: Product[];
}
When I do :
getModelForClass(Family).findById('5f69aa0a56ca5426b44a86c5')
property "products" with an array of ObjectId is not in the result (property is absent) :
{
"_id": "5f69aa0a56ca5426b44a86c5",
"name": "Fourniture"
}
I can't figue how to make that working. I think the problem is in the family class #prop(ref). I saw some example where peoples used #arrayProp but now is deprecated.
I find documentation about ref with a simple object but not with an array of object with version 5.9.1 of Typegoose.
Thanks
aside from using an old version of typegoose with new "syntax"
this is how it is supposed to look in typegoose (7.4)
#modelOptions({ schemaOptions: { collection: 'families' } })
export class Family {
#prop()
public _id?: string;
#prop()
public name?: string;
#prop({ ref: () => Product, required: true, default: [] })
public products!: Ref<Product>[]; // if Product's "_id" is also "string", then it would need to be ": Ref<Product, string>[];"
}

Resources