product.entity.ts
#Entity()
export class Product extends CoreEntity {
#Field(type => String)
#PrimaryColumn('varchar2', { name: 'BAR_CODE' })
barcode: string;
#Field(type => String, { nullable: true })
#Column('varchar2', { name: 'GOODS_NAME', nullable: true })
goodsName: string | null;
#Field(type => Number, { nullable: true })
#Column('number', { name: 'SALE_PRICE', nullable: true, precision: 12, scale: 0 })
salePrice: number | null;
#Field(type => [PromotionProduct])
#OneToMany(
type => PromotionProduct,
promotionProduct => promotionProduct.product,
)
promotionProducts: PromotionProduct[];
}
promotion.entity.ts
#Entity()
export class Promotion extends CoreEntity {
#Field(type => String)
#PrimaryColumn('varchar2', { name: 'PROMOTION_CODE' })
promotionCode: string;
#Field(type => String)
#Column('varchar2', { name: 'PROMOTION_NAME' })
promotionName: string;
#Field(type => String)
#Column('varchar2', { name: 'PROMOTIONSTART_DAY' })
promotionStartDay: string;
#Field(type => String)
#Column('varchar2', { name: 'PROMOTIONEND_DAY' })
promotionEndDay: string;
#Field(type => [PromotionProduct], { nullable: true })
#OneToMany(
type => PromotionProduct,
promotionProduct => promotionProduct.promotion,
)
promotionProducts: PromotionProduct[];
}
promotionProduct.entity.ts
#Entity()
export class PromotionProduct extends CoreEntity {
#Field(type => String)
#PrimaryColumn('varchar2', { name: 'PROMOTION_CODE' })
promotionCode: string;
#Field(type => String)
#PrimaryColumn('varchar2', { name: 'BAR_CODE' })
barcode: string;
#Field(type => Number, { nullable: true })
#Column('number', { name: 'DC_SALE_PRICE' })
dcSalePrice: number | null;
#Field(type => Promotion, { nullable: true })
#JoinColumn({ name: 'PROMOTION_CODE' })
#ManyToOne(
type => Promotion,
promotion => promotion.promotionProducts,
{ nullable: true, onDelete: 'SET NULL' },
)
promotion: Promotion;
#Field(type => Product)
#JoinColumn({ name: 'BAR_CODE' })
#ManyToOne(
type => Product,
product => product.promotionProducts,
)
product: Product;
}
Promotion has many PromotionProduct (1:N)
Product has many PromotionProduct (1:N)
Example,
Product A is promotion target.
Product B is not promotion target.
So Product A has promotionProduct dcSalePrice
Product B hasn't promotionProduct dcSalePrice, just product salePrice.
I want all products with promotion, regardless of whether it's for the promotion or not.
And, PromotionStartDay is LessThanEqual Today, PromotionEndDay is MoreThanEqual.
products.service.ts
const productRepository = await connection.getRepository(Product);
const results = await productRepository.find(
{
relations: ['promotionProducts', 'promotionProducts.promotion'],
where: {
promotionProducts: {
promotion: {
promotionStartDay: LessThanOrEqual(currentDate),
promotionEndDay: MoreThanOrEqual(currentDate),
}
}
}
}
);
But it occurs an error like this:
Cannot query across one-to-many for property promotionProducts
I guess it's like the promotionProducts property doesn't have promotion, but promotionProducts relationship has promotion properties.
I'm not used to typeORM yet, so if you know it, please help me.
Related
I have a query, I'm trying to create a ManyToMany relationship between 2 tables (products and orders) the tables are created fine, but when I want to add the logic in the service to create the records I don't understand how it should be.
This is what I have written till now:
#Entity('pedido')
export class Pedido {
#PrimaryGeneratedColumn()
id: number;
#ManyToMany(() => Product)
#JoinTable({
name: 'pedidos_products',
joinColumn: {
name: 'pedido_id',
},
inverseJoinColumn: {
name: 'product_id',
},
})
product: Product[];
#Column('decimal')
quantity: number;
#Column('decimal')
price: number;
}
#Entity('product')
export class Product {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#ManyToOne(() => Category, (category) => category.product)
category: Category;
#ManyToMany(() => Pedido)
pedido: Pedido[];
}
Service:
async createPedido(body: any) {
const pedidos = body;
const pedidoInstance = new Pedido();
const productsIds = [];
pedidos.forEach(async (item: any) => {
productsIds.push(item.product_id);
pedidoInstance.price = item.price;
pedidoInstance.quantity = item.quantity;
pedidoInstance.product = productsIds;
await this.pedidoRepository.save(pedidoInstance);
});
return {
ok: true,
};
}
I'm inserting multiple entries simultaneously and wanted to store the child relationships as well, but using the code below, province isn't saving into the database. In essence, I want to save the "province" and user when I save the address, and I receive the userID and provinceID in the request.
entity.ts
#Entity()
export class Address {
#PrimaryGeneratedColumn('uuid')
id: string;
#Column()
address1: string;
#Column({ nullable: true })
address2?: string;
#Column()
country: string;
#ManyToOne(() => Province, (province) => province.addresses, { lazy: true })
#JoinColumn({
name: 'province_id',
})
province: Province;
#ManyToOne(() => User, (user) => user.addresses, {
nullable: false,
onDelete: 'CASCADE',
lazy: true,
})
#JoinColumn({
name: 'user_id',
})
user: User;
}
Request from frontend:
[
{
"address1": "Test 1",
"address2": "Test 1",
"country": "Estonia",
"province_id": "3", // suppose uuid
"user_id": "4" // suppose uuid
},
{
"address1": "Test 2",
"address2": "Test 2",
"country": "Estonia",
"province_id": "3", // suppose uuid
"user_id": "4" // suppose uuid
}
]
service.ts
async saveAddresses (_addresses) {
const addresses: any = _addresses.map((address) => ({
...address,
user: address.user_id,
province: address.province_id
}));
const addressesToCreate = this.addressRepository.create(addresses);
await this.addressRepository.insert(addressesToCreate);
}
As you can see how I'm saving the records but unfortunately, the province and user saving as null and I don't want to query for province again n again.
I tried like this article, and works for me
The code:
//Entity
#Entity('organization')
export class Organization {
#PrimaryGeneratedColumn('uuid')
id: string;
#Column({ type: 'varchar' })
name: string;
#OneToMany(() => Users, (obj) => obj.organization)
users: Users[];
}
#Entity('users')
export class Users {
#PrimaryGeneratedColumn('uuid')
id: string;
#Column({ type: 'varchar' })
name: string;
#ManyToOne(() => Organization, (obj) => obj.users, { cascade: true }) // <--- cascade: true
organization: Organization;
}
//Service
async create(dto: CreateDto[]) {
try {
const orgEntity = function (name: string) {
const org = new Organization();
org.name = name;
return org;
};
const entity: any = dto.map((obj) => ({
...obj,
organization: orgEntity(obj.organization),
}));
const entityCreated = this._repoUser.create(entity);
await this._repoUser.save(entity); // <-- save
return entityCreated;
} catch (err) {
console.log(err);
}
}
//dto
export class CreateDto {
#ApiProperty()
#IsString()
name: string;
#ApiProperty()
#IsString()
organization: string;
}
//request
[
{
"name": "user-1",
"organization":"org-1"
},
{
"name": "user-2",
"organization":"org-2"
}
]
When I am trying to update the RFQ it is creating a user automatically using the default value but its nowhere mentioned in the code to create a default new user checked the relations also but couldnot find anything solid
service section
async update(id: number, data: UpdateRfqDto) {
const project = await Rfq.findOne({ where: { id: id } });
if (!project) throw new NotFoundException('Project not found');
const assignee = await this.usersService.findUserById(data.assignee);
if (!assignee) throw new NotFoundException('Assignee not found');
if (data.dueDate) data.dueDate = moment.utc(data.dueDate).format();
if (data.status != 'draft' && !(data.assignee)) throw new NotAcceptableException()
// data.user=assignee
// return data.nonMember;
if (data.nonMember) {
const mem = data.nonMember
const nonmember = (mem.includes(",")) ? mem.split(",") : mem;
this.SendEmailToNonMemberVendor(nonmember, project, data.dueDate);
}
delete data.nonMember;
// return assignee;
// data.user=assignee
this.SendEmailToProjectAssignee(assignee, data.dueDate, project);
let vendorsList = [];
let listVendor = [] ;
if (data.requestVendors.length > 0) {
const vendors=data.requestVendors.map((v)=>{
return v.id;
})
// return vendors;
let vendorsListt = await Users.findBy({ id: In(vendors) });
vendorsList.push(vendorsListt)
await this.SendEmailToRequestQuoteVendor(vendorsListt, data.dueDate, data);
}
delete data.assignee;
delete data.requestVendors;
// return data;
await Rfq.update(id, data);
const newRfq = await Rfq.findOne({
where: { id: id },
relations: ['user'],
});
newRfq.user = assignee;
newRfq.vendors = vendorsList;
await newRfq.save();
return newRfq;
}
async SendEmailToRequestQuoteVendor(vendor, dueDate, project) {
let due = dueDate ? moment(dueDate, 'DD-MM-YYYY') : 'not given';
for (let i = 0; i < vendor.length; i++) {
let emailData = {
name: vendor[i].fullName,
email: vendor[i].email,
subject: VendorRequestTemplate.subject,
body: VendorRequestTemplate.body,
// params:{},
params: {
name: vendor[i].fullName,
dueDate: due,
projectName: project.projectName,
},
};
await sendemail(emailData);
}
//
return vendor;
}
async SendEmailToProjectAssignee(assignee, dueDate, project) {
let due = dueDate ? moment(dueDate, 'DD-MM-YYYY') : 'not given';
let emailData = {
name: assignee.fullName,
email: assignee.email,
subject: AssigneeTemplate.subject,
body: AssigneeTemplate.body,
// params:{},
params: {
name: assignee.fullName,
dueDate: due,
projectName: project.projectName,
},
};
await sendemail(emailData);
return assignee;
}
async SendEmailToNonMemberVendor(vendorEmail, project, dueDate) {
let due = dueDate ? moment(dueDate, 'DD-MM-YYYY') : 'not given';
for (let i = 0; i < vendorEmail.length; i++) {
// if(!inviteSave){
// throw new BadRequestException();
// }
let emailData = {
name: 'Vendor',
email: vendorEmail[i],
subject: InviteVendorTemplate.subject,
body: InviteVendorTemplate.body,
// params:{},
params: {
name: 'Vendor',
dueDate: due,
projectName: project.projectName,
},
};
await sendemail(emailData);
}
return dueDate;
}
model:
import { QuoteMilestone } from 'src/quotePricing/models/quote-milestone.entity';
import { QuotePricing } from 'src/quotePricing/models/quote-pricing.entity';
import {
BaseEntity,
BeforeInsert,
BeforeUpdate,
Column,
CreateDateColumn,
Entity,
JoinColumn,
JoinTable,
ManyToMany,
ManyToOne,
OneToMany,
OneToOne,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm';
import { Users } from '../../users/models/users.entity';
import { InviteVendor } from './invite.entity';
#Entity()
export class Rfq extends BaseEntity {
#PrimaryGeneratedColumn()
id: number;
#Column()
projectName: string;
#Column({ unique: true, default: 'JOB-1' })
jobNumber: string;
#Column()
projectType: string;
#Column({ type: 'text' })
projectDescription: string;
#Column({ type: 'text' })
projectScope: string;
#Column()
attachment: string;
#Column({ default: null })
dueDate: Date;
#Column({ default: 'draft' })
status: string; //draft/created/assigned/quote/paid/completed
#Column()
#CreateDateColumn()
createdAt: Date;
#Column()
#UpdateDateColumn()
updatedAt: Date;
#ManyToOne(() => Users, (user) => user.rfqs) //Assignee
user: Users;
#ManyToOne(() => Users, (user) => user.created_by) //CreatedBy
created_by: Users;
#OneToMany(() => QuotePricing, (QuotePricing) => QuotePricing.rfq)
quotePricings: QuotePricing[];
#OneToMany(() => InviteVendor, (invite) => invite.project)
inviteVendor: InviteVendor[];
#OneToMany(() => QuoteMilestone, (milestone) => milestone.rfq)
milestone: QuoteMilestone[];
#ManyToMany(() => Users, (requestVendors: Users) => requestVendors.id, {
cascade: true,
})
#JoinTable({
name: 'rfqs_vendors',
joinColumn: {
name: 'rfqId',
referencedColumnName: 'id',
},
inverseJoinColumn: {
name: 'userId',
referencedColumnName: 'id',
},
})
vendors: Users[];
}
I have tried in many ways but not able to find any way out please help
and any help would be highly appriciated
I currently have a nestjs service /api/permission
To create a new permission I send the following parameters for REQUEST:
{"Id":"","EsName":"rxe2x","EnName":"rxi2x","Name":"rxclv2x","Type":"64","Role":"7,8,9"}
And RESPONSE is
{"status":201,"message":"Permission has been saved successfully.","permission":{"Id":"200","EsName":"rxe2x","EnName":"rxi2x","Name":"rxclv2x","Type":"64","Role":"7,8,9"}}
I want the Role parameter to also be saved, in the PermissionRole table, each role with an entry like this:
RoleId
PermissionId
7
200
8
200
9
200
on my permission.service.ts
async createPermission(permission: CreatePermissionDto) {
const exist = await this.permissionRepository.findOne({
where: { Name: permission.Name },
});
if (exist)
throw new ConflictException(
'The permission already exists.',
HttpStatus.CONFLICT.toString(),
);
const newPermission = await this.permissionRepository.save(permission);
return Object.assign(
{},
{
status: HttpStatus.CREATED,
message: 'Permission has been saved successfully.',
permission: newPermission,
},
);
}
summary of my Permission.entity.ts
import { Role } from './Role.entity';
#Entity('Permission', { schema: 'dbo' })
export class Permission extends BaseEntity {
#PrimaryGeneratedColumn({
type: 'int',
name: 'Id',
})
Id: number;
#Column('varchar', {
nullable: false,
name: 'Name',
})
Name: string;
#Column('int', {
nullable: false,
name: 'Type',
})
Type: number;
#Column('varchar', {
nullable: false,
name: 'EnName',
})
EnName: string;
#Column('varchar', {
nullable: false,
name: 'EsName',
})
EsName: string;
#ManyToMany(
() => Role,
(Role: Role) => Role.permissions,
)
roles: Role[];
}
summary of my Role.entity.ts
import { Permission } from './Permission.entity';
import { User } from './User.entity';
#Entity('Role', { schema: 'dbo' })
export class Role extends BaseEntity {
#PrimaryGeneratedColumn({
type: 'int',
name: 'Id',
})
Id: number;
#Column('varchar', {
nullable: false,
length: 50,
name: 'Name',
})
Name: string;
#Column('varchar', {
nullable: true,
length: 100,
name: 'AccessLevel',
})
AccessLevel: string;
#Column('bigint', {
nullable: true,
name: 'VALAccount',
})
VALAccount: string;
#Column('bit', {
name: 'CanModified',
default: '1',
})
CanModified: string;
#ManyToMany(
() => Permission,
(Permission: Permission) => Permission.roles,
{
nullable: false,
eager: true,
},
)
#JoinTable({
name: 'PermissionRole',
})
permissions: Permission[];
#ManyToMany(
() => User,
(User: User) => User.roles,
)
users: User[];
#ManyToMany(
() => User,
(User: User) => User.rolesCanAssign,
)
usersCanAssign: User[];
}
You have to define many to many like this in Permission.entity.ts
#ManyToMany(() => Role , (role) => role.id)
#JoinTable({
name: 'Permission_Role',
joinColumn: {
name: 'permissionId',
referencedColumnName: 'id'
},
inverseJoinColumn: {
name: 'roleId',
referencedColumnName: 'id'
}
})
roles: Role[];
And in permission.service.ts
async createPermission(permission: CreatePermissionDto) {
const exist = await this.permissionRepository.findOne({
where: { Name: permission.Name },
});
if (exist)
throw new ConflictException(
'The permission already exists.',
HttpStatus.CONFLICT.toString(),
);
const newPermissionDao = this.permissionRepository.create(permission);
newPermissionDao.roles = permission.roles.map((role) => {roleId: role, permissionId: newPermissionDao.id} );
const newPermission = await this.permissionRepository.save(newPermissionDao);
return Object.assign(
{},
{
status: HttpStatus.CREATED,
message: 'Permission has been saved successfully.',
permission: newPermission,
},
);
}
Basically you need to create an array of object for many to many relation like below:
permission.roles = [{
roleId: 1,
permissionId: 1
},
{
roleId: 2,
permissionId: 1
}];
I have PublicationsEntity with a OneToMany relation to LikesEntity, i'm trying to sort my publications by likes, and return all of the likes with the user associated in likes, and all the comments to display them front-side
#Entity('publications')
export class PublicationsEntity {
#PrimaryGeneratedColumn()
id: number;
#Column({ type: 'longtext' })
publication: string;
#ManyToOne(type => UsersEntity, users => users.publications, {
onDelete: "CASCADE" })
user: UsersEntity;
#OneToMany(type => CommentsEntity, comments =>
comments.publication, { onDelete: "CASCADE" })
comments: CommentsEntity[];
#OneToMany(type => LikesEntity, likes => likes.publication, { onDelete: "CASCADE" })
likes: LikesEntity[];
}
there is my likes entity who contains user and i'm trying to return each user on each like after with the sorted publications :
#Entity('likes')
export class LikesEntity {
#PrimaryGeneratedColumn()
id: number;
#Column()
like: number;
#ManyToOne(type => UsersEntity, user => user.likes, { eager: true })
user: UsersEntity;
#ManyToOne(type => PublicationsEntity, publication => publication.likes)
publication: PublicationsEntity;
}
my users entity :
#Entity('users')
export class UsersEntity {
#PrimaryGeneratedColumn()
id: number;
#Column({ nullable: true })
userName: string;
#OneToMany(type => PublicationsEntity, publications =>
publications.user, { onDelete: "CASCADE" })
publications: PublicationsEntity[];
#Column()
email: string;
#Column({ select: false })
password: string;
#OneToMany(type => CommentsEntity, comment => comment.user, { onDelete: "CASCADE" })
comments: CommentsEntity[];
#OneToMany(type => LikesEntity, like => like.user, { onDelete: "CASCADE" })
likes: LikesEntity[];
#OneToMany(type => PublicationsEntity, publications => publications.user, { onDelete: "CASCADE" })
publications: PublicationsEntity[];
#CreateDateColumn({ type: "timestamp", default: () => "CURRENT_TIMESTAMP(6)" })
createdAt: Date;
#UpdateDateColumn({ type: "timestamp", default: () => "CURRENT_TIMESTAMP(6)", onUpdate: "CURRENT_TIMESTAMP(6)" })
updatedAt: Date;
}
and there is my publicationsService
async getPublications() {
return await getConnection()
.createQueryBuilder(PublicationsEntity, 'publications')
.leftJoinAndSelect('publications.likes', 'likes')
.leftJoinAndSelect('publications.user', 'user')
.leftJoinAndSelect('publications.comments', 'comments')
.leftJoinAndSelect('comments.children', 'children')
//.orderBy()
.getMany()
}
some data on my likes table : https://i.stack.imgur.com/wJ0l5.png
First of all, I think you should fix something on ur entities
In your PublicationsEntity
#OneToMany(type => LikesEntity, likes => likes.publication, { onDelete: "CASCADE" })
likes: LikesEntity[]; // this one should be an array
and for LikesEntity
#ManyToOne(type => PublicationsEntity, publication => publication.likes)
publication: PublicationsEntity;
For your question, I assume that you want to return the Publication sorted by number of likes, to do that you should add in your query:
async getPublications() {
return await getConnection()
.createQueryBuilder(PublicationsEntity, 'publications')
.leftJoin('publications.likes', 'likes')
.addSelect("COUNT(likes.publication) AS total_likes" )
.orderBy('total_likes', 'ASC') // or DESC
.groupBy("likes.publication")
.getRawMany()
}
UPDATE
As I understand from your comments below, I think you need to use the map and sort function to get the result you needs :
async getPublications() {
return await getConnection()
.createQueryBuilder(PublicationsEntity, 'publications')
.leftJoinAndSelect('publications.likes', 'likes')
.leftJoinAndSelect('publications.user', 'user')
.leftJoinAndSelect('publications.comments', 'comments')
.leftJoinAndSelect('comments.children', 'children')
//.orderBy()
.getMany().then(data => data.map(a => ({ ...a, total_likes:a.likes.length })).sort(function (a, b) {
return b.total_likes - a.total_likes; // this's going to sort by DESC if you want to make the opposite just do a.total_likes - b.total_likes
})
)
}