How to seed data in NestJs & Mongoose? - node.js

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.

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 define array of objects in Sequelize.js in NestJS for Postgres?

How can I define an array of objects field in Sequelize.js model in NestJS for Postgres?
I need something like this
{
userId: { type: String, required: true},
products: [
{
productId: {
type: String
},
quantity: {
type: Number,
default: 1
}
}
]
}
I tried to translate the above code to NestJS using Sequelize.js, but using Postman I sent post requests and got an untyped object in the products array. And I don't understand how to solve this problem.
import {Column, DataType, Model, Table} from "sequelize-typescript";
export interface IProductItem {
productId: string
quantity: number
}
interface ICartsCreationAttrs {
userId: number
products: Array<IProductItem>
}
#Table({tableName: 'carts'})
export class CartsModel extends Model<CartsModel, ICartsCreationAttrs> {
#Column({type: DataType.INTEGER, unique: true, autoIncrement: true, primaryKey: true})
id: number
#Column({type: DataType.INTEGER, unique: true, allowNull: false})
userId: number
#Column({type: DataType.ARRAY(DataType.JSON), allowNull: false})
products: Array<IProductItem>
}
For sequelize doesn't needs a type definition in an array or JSON object. You can define only column types.
(JSONB allows only for Postgress)
products: { type: JSONB, required: true}

Nest.js TypeORM FindOneBy returns null even with data on the database

So I have this entity...
import { Column, Entity, PrimaryColumn } from "typeorm";
#Entity('Users')
export class User {
#PrimaryColumn({ type: 'nvarchar', length: 36, unique: true })
UserId: string;
#Column({ type: 'nvarchar', length: 100, unique: true })
Email: string;
#Column({ type: 'nvarchar', length: 36 })
CrmUserId: string;
#Column({ type: 'nvarchar', length: 36, nullable: true })
AzureB2CUserId: string;
#Column({ type: 'bit', default: false })
IsActive: boolean;
#Column({ type: 'nvarchar', length: 100 })
CreatedBy: string;
#Column({ type: 'datetime' })
CreatedDate: Date;
#Column({ type: 'nvarchar', length: 100, nullable: true })
UpdatedBy: string;
#Column({ type: 'datetime', nullable: true })
UpdatedDate: Date;
}
and using TypeORM I want to get one record by the email, not the UserId. So I had this on the repository.
public async getUserByEmail(email: string): Promise<User | null> {
let _res = await this._userRepository.findOne({ where: { Email: email, IsActive: true }})
return _res;
}
But it always returns a null, even if the record exists, I was thinking of doing it with a CreateQueryBuilder, like this...
public async getUserByEmail(email: string): Promise<User | null> {
let _res = await this._userRepository.createQueryBuilder()
.select()
.from(User, "Users")
.where('email = :emailParameter', { email })
.getOne();
return _res;
}
But the result is the same, I keep getting null, I have no idea what I am doing wrong, because it works if I use the primary key on the findOne and findOneBy. Any help out there with this?
If you are using MongoDB then use import { MongoRepository, Repository } from 'typeorm'; for the _userRepository type.
While using _userRepository findOneBy wrap the userId parameter in import { ObjectId } from 'mongodb' const query = {"_id":ObjectId(req.params.productId)}
OR have a look at this example
know this due to the fact that where in the "entity" you give the default value in the "delete_date" to the user, you must definitely give null.
I wrote in my entity like this
#DeleteDateColumn({
name: 'deleted_date',
type: 'timestamp',
default: null, // default: () => 'CURRENT_TIMESTAMP(6)',
})
public deletedAt: Date;
#CreateDateColumn({
name: 'created_date',
type: 'timestamp',
default: () => 'CURRENT_TIMESTAMP(6)',
})
public createdAt: Date;
#UpdateDateColumn({
name: 'updated_date',
type: 'timestamp',
default: null
onUpdate: 'CURRENT_TIMESTAMP(6)',
})
public updatedAt: Date;
you can't give values to a "delete_date" when you create a user

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

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[];
}

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.

Resources