public async getAllCategories(
em: EntityManager,
): Promise<UserCategory> {
return await em
.createQueryBuilder(UserCategory, "uc")
.leftJoinAndSelect('uc.post','p')
.leftJoinAndSelect('uc.category', 'uc_c')
.select([
'count(*)',
'uc.category_id',
'uc_c.icon',
'uc_c.title',
"category_id"
])
.groupBy(['uc.category_id', 'uc_c.id'])
.orderBy({ 'count(*)': QueryOrder.DESC })
.execute()
}
Getting the error of post does not have the relation with userCategory
Related
Tables:
users
user_category
categories
user_id (PK)
user_id (PK)
category_id (PK)
name
category_id (PK)
name
I'm having problems with concurrency on node, I'll describe an example to reproduce the error I have:
When receiving 5 requests in parallel to perform a PUT on the same existing user and their categories:
PUT users/userId123
body
{
"name": "other name"
"categories" : [
"categoryId123",
"categoryId456",
"categoryId789",
]
}
Upon receiving this payload in a simulation, first a DELETE query is performed on user_category and then an INSERT on user_category to insert the new categories.
It turns out that as the node is a single thread, the main thread competes, and when performing 5 identical requests in parallel, it is happening to perform two DELETES of two different requests and then the 2 INSERTs, which will generate a primary key error in the table user_category, I already tried to use a transaction for these two queries, but the error still occurs, blocking the main thread until doing these two operations is not an option defied the loss in performance.
Attempt-1
Below is an excerpt from UserController code that does this
if (Array.isArray(user.categories)) {
await userCategoriesRepository.deleteHardByUserId(user.userId)
const userCategories = []
for (const categoryId of user.categories) {
userCategories.push({userId: user.userId, categoryId})
}
await userCategoriesRepository.save(userCategories)
}
UserRepository:
save (userCategories: ICreateUserCategoryDTO[]): Promise<void> {
try {
return this.repository.insert(userCategories)
} catch (error) {
throw new Error(error)
}
}
deleteHardByUserId (userId: string): Promise<void> {
try {
return this.repository.delete({ userId })
} catch (error) {
throw new Error(error)
}
}
Attempt-2 (Transaction)
Below is an excerpt from UserController code that does this
if (Array.isArray(user.categories)) {
const userCategories = []
for (const categoryId of user.categories) {
userCategories.push({userId: user.userId, categoryId})
}
await userCategoriesRepository.updateWithTransaction(userId, userCategories)
}
UserRepository:
async updateWithTransaction (userId: string, userCategories: ICreateUserCategoryDTO[]) {
try {
return getManager().transaction(async transaction => {
await transaction.delete(UserCategory, { userId })
await transaction.insert(UserCategory, userCategories)
})
} catch (error) {
throw new Error(error)
}
}
QUERY LOGGING
case: 1 request (SUCESS)
query: START TRANSACTION // REQUEST 1
query: DELETE FROM "user_category" WHERE "user_id" = $1 -- PARAMETERS: ["userId123"] // REQUEST 1
query: INSERT INTO "user_category"("user_id", "category_id") VALUES ($1, $2) -- PARAMETERS: ["userId123", "categoryId123"] // REQUEST 1
query: COMMIT // REQUEST 1
case: 2 or more requests parallel (ERROR PK)
query: START TRANSACTION // REQUEST 1
query: DELETE FROM "user_category" WHERE "user_id" = $1 -- PARAMETERS: ["userId123"] // REQUEST 1
query: START TRANSACTION // REQUEST 2
query: DELETE FROM "user_category" WHERE "user_id" = $1 -- PARAMETERS: ["userId123"] // REQUEST 2
query: INSERT INTO "user_category"("user_id", "category_id") VALUES ($1, $2) -- PARAMETERS: ["userId123", "categoryId123"] // REQUEST 1
query: COMMIT // REQUEST 1
query: INSERT INTO "user_category"("user_id", "category_id") VALUES ($1, $2), ($3, $4) -- PARAMETERS: ["userId123", "categoryId123", "userId123", "categoryId456"] // REQUEST 2 --> PK ERROR
query: COMMIT // REQUEST 2
Details:
node 16.13.1
typeorm 4.3.2
pg 8.4.0
npm 8.3.0
Postgres database 13.2
When I try to find a row that was soft deleted , even with {with_Deleted : true}, returns null , but when the row was not soft-deleted it returns normal. Is there a way that it can return soft deleted rows?
conjunto-simulacoes service :
async getCorteById(id : number): Promise<ConjuntoSimulacoes>{
return await this.conjuntoSimulacoesRepository.findOne({withDeleted : true,relations : ['corte'],where : {id}});
}
conjunto-simulacoes Controller :
#Get('/corte/:id')
#UseGuards(AuthGuard('jwt'))
async getCorteBySimulacao(#Param('id') id : number){
return await this.conjuntoSimulacoesService.getCorteById(id);
}
conjunto-simulacoes entity :
#ManyToOne(() => Cortes , corte => corte.conjunto_simulacoes )
corte : Cortes;
cortes entity :
#OneToMany(() => ConjuntoSimulacoes , conjunto_simulacoes => conjunto_simulacoes.corte )
conjunto_simulacoes : ConjuntoSimulacoes[]
I fixed doing a new query , in my last query the {with_Deleted : true} was searching inside the table conjunto simulacoes and not in the table cortes.
New query:
async getCorteByIdWithDeleted(id : number){
return await this.conjuntoSimulacoesRepository.query(`SELECT * FROM conjunto_simulacoes
as conjunto LEFT JOIN cortes as corte on corte.id = conjunto."corteId"
WHERE conjunto.id=${id}`);
}
When the query result Entity[] is directly returned from the controller, the #Transform defined in the entity can take effect normally, but when returning data such as {datalist: Entity[]}, it is found that the method in #Transform is not executed
[Google Translate ~]
entity
import { Transform } from 'class-transformer';
import {CreateDateColumn, Entity} from 'typeorm';
#Entity({ name: 't_articles' })
export class ArticleEntity {
...
#Transform((v) => {
console.log(123);
return new Date(v).toLocaleString();
})
#CreateDateColumn()
create_time: Date;
...
}
controller
const [datalist, count] = await this.articleRepository.findAndCount({skip, take, where});
return datalist // ===> transformed
return {datalist} // ===> untransformed, and '123' is not printed
If you are using NestJs make sure you have:
app.useGlobalPipes(
new ValidationPipe({
transform: true
})
);
I'm trying to get all the records from the table.
controller:
#Get('types')
async getTypes(): Promise<PageTypeRO[]> {
return this.pageService.findTypes();
};
service:
async findTypes(): Promise<PageTypeRO[]> {
return await this.pageTypePropsRepository.find();
}
interface (RO):
export interface PageTypeRO {
readonly id: number
}
I expect to get an array with objects in which only the "id" field, but teach all the fields from the table.
You have to set columns you want to get,
To make it work for you, you should edit FindTypes function:
async findTypes(): Promise<PageTypeRO[]> {
return await this.pageTypePropsRepository.find({ select: ["id"] });
}
I'm using MEAN Stack to build a grading app. I'm trying to get a list of items from the database into my Angular component via a service code but i keep getting core.js:14597 ERROR TypeError: Cannot read property 'map' of undefined.
//scoring.service.js
import { Scoring } from './scoring.model';
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs';
import { map } from 'rxjs/operators';
import { HttpClient } from '#angular/common/http';
import { Router } from '#angular/router';
#Injectable({providedIn: 'root'})
export class ScoringService {
private scoring: Scoring[] = [];
private updatedScores = new Subject<Scoring[]>();
constructor(private http: HttpClient, private router: Router){}
getScoring() {
this.http.get<{message: string, scores: any}>('http://localhost:3000/api/scoring')
.pipe(map((scoringData) => {
return scoringData.scores.map(score => {
status = score.IsActive ? 'checked' : 'unchecked';
return {
id: score._id,
Criteria: score.Criteria,
MaxPoints: score.MaxPoints,
IsActive: status,
DateCreated: score.DateCreated,
DateModified: score.DateModified
};
});
}))
.subscribe((transformedScores) => {
this.scoring = transformedScores;
this.updatedScores.next([...this.scoring]);
});
}
}
The code is supposed to list the items and map a boolean field from true or false to checked or unchecked respectively. But nothing is being listed at all. The error i'm getting is "Cannot read property 'map' of undefined." And I've used the same set of code in another component to list items without getting the error. Please, help me here, I will appreciate. Thanks.
Put some breakpoints in the pipe(map(scoringData)) part and see what you get.
The exception is throwed because you actually assume that "scoringData" returning an object formated like this :
{"scores": [ << an array of something >> ]}
But if scoringData is null, you tried to used the .map function to an undefined result.
Quick fix can be :
.pipe(map((scoringData) => {
return (scoringData && scoringData.scores || []).map(score => {
status = score.IsActive ? 'checked' : 'unchecked';
return {
id: score._id,
Criteria: score.Criteria,
MaxPoints: score.MaxPoints,
IsActive: status,
DateCreated: score.DateCreated,
DateModified: score.DateModified
};
});
}))
Better way consist to filter your results before using the map in your pipe.
It depend on which library of RxJS you are using, but normaly you can do something like that :
.pipe(filter(scoringData => !!scoringData && !!scoringData.scores && scoringData.scores.length), map((scoringData)
And the import of the filter operator should be the same as map operator :
import { filter } from 'rxjs/operators';