Sequelize - include column and getting string instead of object - mysql

I have an problem which I can't resolve in any way, I'm including user -> avatar models in PostTip model and when I want to add column with OBJECT avatar (id + src) I can't make it.
Name, email works because I'm pointing to single value not object.
Where is the problem?
PostTip.findAndCountAll({
attributes: {
include: [
** [db.sequelize.col(`"user"->"avatar"`), "avatar"],**
[db.sequelize.col(`"user"."name"`), "name"],
[db.sequelize.col(`"user"."email"`), "email"],
],
},
offset: offset,
limit: limit,
order: order,
where: {
postId: req.params.id,
},
include: [
{
model: User,
as: "user",
attributes: [],
include: [{ model: AwsAvatar, as: "avatar" }],
},
],
}).then((result) => {
console.log(JSON.stringify(result.rows, null, 2));
const data = getPagingData(result.rows, result.count, query.page, limit);
return res.send(data);
});
};
and results are:
{
"id": 1,
"userId": 1,
"postId": 1,
"tipAmount": 100,
"createdAt": "2023-01-11T22:10:26.440Z",
"updatedAt": "2023-01-11T22:10:26.440Z",
** "avatar": "(1,1,https://dh7ieyc6s2dxm.cloudfront.net/avatars/1673514260216_0e69489d-fc95-4ed8-b615-2a76ce43385.webp,\"2023-01-12 09:04:21.223+00\",\"2023-01-12 09:04:21.223+00\")",**
"name": "Test User",
"email": "test#test"
}
Im expecting to have output like that:
{
"id": 1,
"userId": 1,
"postId": 1,
"tipAmount": 100,
"createdAt": "2023-01-11T22:10:26.440Z",
"updatedAt": "2023-01-11T22:10:26.440Z",
** "avatar": {
"id": 1,
"userId": 1,
"src": "https://dh7ieyc6s2dxm.cloudfront.net/avatars/1673514260216_0e69489d-fc95-4ed8-b615-2a76ce5ff385.webp",
"createdAt": "2023-01-12T09:04:21.223Z",
"updatedAt": "2023-01-12T09:04:21.223Z"
},**
"name": "Test User",
"email": "test#test"
}
User model:
import { Optional } from "sequelize";
import {
Column,
Table,
Model,
DataType,
ForeignKey,
BelongsTo,
Default,
HasMany,
HasOne,
} from "sequelize-typescript";
import UserRole from "../../enums/user-role.enum";
import AwsAvatar from "../aws/aws-avatar.model";
import PostComment from "../post/post-comment.model";
import PostLike from "../post/post-like.model";
import PostTip from "../post/post-tip.model";
import Card from "./card.model";
import Role from "./role.model";
interface UserAttributes {
id: number;
avatar: AwsAvatar;
name: string;
setName: string;
description: string;
hashtag: string;
email: string;
password: string;
isVip: boolean;
lastUsedCardNum: string;
sendNotifications: boolean;
cards: Card[];
}
interface UserCreationAttributes
extends Optional<
UserAttributes,
| "id"
| "avatar"
| "setName"
| "description"
| "hashtag"
| "cards"
| "isVip"
| "lastUsedCardNum"
| "sendNotifications"
> {}
#Table({ tableName: "users" })
export default class User extends Model<
UserAttributes,
UserCreationAttributes
> {
#Column(DataType.STRING)
public name!: string;
#Column(DataType.STRING)
public setName!: string;
#Column(DataType.STRING)
public email!: string;
#Column(DataType.STRING)
public password!: string;
#HasOne(() => AwsAvatar)
public avatar!: AwsAvatar;
#Column(DataType.STRING)
public description!: string;
#Column(DataType.STRING)
public hashtag!: string;
#Default(false)
#Column(DataType.BOOLEAN)
public isVip!: boolean;
#Default(false)
#Column(DataType.BOOLEAN)
public subscriptionActive!: boolean;
#Column(DataType.DATE)
public subscriptionExpireDate!: Date;
#Default(false)
#Column(DataType.BOOLEAN)
public emailConfirmed!: boolean;
#Default(false)
#Column(DataType.BOOLEAN)
public invalidEmail!: boolean;
#Column
public buyedContent!: string;
#Default(0)
#Column(DataType.INTEGER)
public wallet!: number;
#Default(0)
#Column(DataType.INTEGER)
public spent!: number;
#Default(false)
#Column(DataType.BOOLEAN)
public isMessageReaded!: boolean;
#Default(false)
#Column(DataType.BOOLEAN)
public sendNotifications!: boolean;
//
#Column
public messageContainerDto!: string;
#Column(DataType.STRING)
public lastUsedCardNum!: string;
#HasMany(() => Card)
public cards!: Card[];
#ForeignKey(() => Role)
#Default(UserRole.User)
#Column(DataType.INTEGER)
public roleId!: number;
#BelongsTo(() => Role)
public role!: Role;
#HasMany(() => PostTip)
public tips!: PostTip[];
#HasMany(() => PostComment)
public comments!: PostComment[];
#HasMany(() => PostLike)
public likes!: PostLike[];
}
Avatar model:
import { Optional } from "sequelize";
import {
Column,
Table,
Model,
DataType,
ForeignKey,
BelongsTo,
} from "sequelize-typescript";
import User from "../user/user.model";
interface AwsAvatarAttributes {
id: number;
src: string;
}
interface AwsAvatarCreationAttributes
extends Optional<AwsAvatarAttributes, "id"> {}
#Table({ tableName: "aws_avatars" })
export default class AwsAvatar extends Model<
AwsAvatarAttributes,
AwsAvatarCreationAttributes
> {
#ForeignKey(() => User)
#Column(DataType.INTEGER)
public userId!: number;
#BelongsTo(() => User, { onDelete: "CASCADE" })
public user!: User;
#Column(DataType.STRING)
public src!: string;
}
I know it can be done smth like that, but cmon, I believe that there is any way to join avatar table and not for each loop every time when fetching data:
private getPostTips = (req: Request, res: Response, next: NextFunction) => {
const query = req.query as unknown as { name: string } & PaginationDto;
const { limit, offset } = getPagination(query.page, query.size);
const condition = query.name
? {
name: {
[Op.iLike]: "%" + query.name + "%",
},
}
: {};
const order: any[string] = [[query.sort, query.order]];
PostTip.findAndCountAll({
raw: true,
nest: true,
attributes: {
include: [
[db.sequelize.col(`"user"."name"`), "name"],
[db.sequelize.col(`"user"."email"`), "email"],
],
},
offset: offset,
limit: limit,
order: order,
where: { postId: req.params.id },
include: [
{
model: User,
as: "user",
attributes: [],
where: condition,
include: [{ model: AwsAvatar, as: "avatar" }],
},
],
}).then((result) => {
**const sorted: any[] = [];
for (let i = 0; i < result.rows.length; i++) {
let item = {
avatar: result.rows[i].user.avatar,
...result.rows[i],
user: undefined,
};
sorted.push(item);
}**
const data = getPagingData(sorted, result.count, query.page, limit);
return res.send(data);
});
};

Related

Sequelize ModelNotInitializedError, Object need to be added to a Sequelize instance

I get this error when i run my project.
ModelNotInitializedError: Model not initialized: Member "getTableName"
cannot be called. "Categorie" needs to be added to a Sequelize
instance.
User model :
import { Categorie } from './categorie.model';
import sequelize from './index'
import { Model } from 'sequelize-typescript'
interface UserAttributes {
firstname: string;
lastname: string;
email: string;
password: string;
phone: string;
address: string;
}
export class User extends Model<UserAttributes> implements UserAttributes {
public firstname!: string;
public lastname!: string;
public email!: string;
public password!: string;
public phone!: string;
public address!: string;
public getCategories!: BelongsToManyGetAssociationsMixin<Categorie>;
public addCategories!: BelongsToManyAddAssociationMixin<Categorie, number>;
public hasCategories!: BelongsToManyHasAssociationMixin<Categorie, number>;
public countCategories!: BelongsToManyCountAssociationsMixin;
public createCategories!: BelongsToManyCreateAssociationMixin<Categorie>;
public readonly createdAt!: Date;
public readonly updatedAt!: Date;
}
User.init({
// id: {
// type: DataTypes.INTEGER.UNSIGNED,
// autoIncrement: true,
// primaryKey: true,
// },
firstname: {
type: new DataTypes.STRING(128),
allowNull: false,
},
lastname: {
type: new DataTypes.STRING(128),
allowNull: false,
},
email: {
type: new DataTypes.STRING(128),
allowNull: false,
unique: true,
},
password: {
type: new DataTypes.STRING(128),
allowNull: false,
},
phone: {
type: new DataTypes.STRING(64),
allowNull: false,
},
address: {
type: new DataTypes.STRING(256),
allowNull: false,
},
}, {
tableName: 'User',
sequelize
})
User.belongsToMany(Categorie, {through: 'User_Cat'});
Categorie.belongsToMany(User, {through: 'User_Cat'});
sequelize.sync();
Categorie model :
import { BelongsToManyAddAssociationMixin, BelongsToManyCountAssociationsMixin, BelongsToManyCreateAssociationMixin, BelongsToManyGetAssociationsMixin, BelongsToManyHasAssociationMixin, DataTypes } from 'sequelize';
import sequelize from './index'
import { Model } from 'sequelize-typescript'
import { Unit } from './unit.model';
interface CategorieAttributes {
index: number;
title: string;
img: string;
label: string;
eval_intro: string;
eval_mid: string;
}
export class Categorie extends Model<CategorieAttributes> implements CategorieAttributes {
public index!: number;
public title!: string;
public img!: string;
public label!: string;
public eval_intro!: string;
public eval_mid!: string;
public getUnits!: BelongsToManyGetAssociationsMixin<Unit>;
public addUnits!: BelongsToManyAddAssociationMixin<Unit, number>;
public hasUnits!: BelongsToManyHasAssociationMixin<Unit, number>;
public countUnits!: BelongsToManyCountAssociationsMixin;
public createUnits!: BelongsToManyCreateAssociationMixin<Unit>;
public readonly createdAt!: Date;
public readonly updatedAt!: Date;
}
Categorie.init({
// id: {
// type: DataTypes.INTEGER.UNSIGNED,
// autoIncrement: true,
// primaryKey: true,
// },
index: {
type: DataTypes.INTEGER.UNSIGNED,
allowNull: true,
},
title: {
type: new DataTypes.STRING(256),
allowNull: false,
},
img: {
type: new DataTypes.STRING(256),
allowNull: true,
},
label: {
type: new DataTypes.TEXT,
allowNull: false,
},
eval_intro: {
type: new DataTypes.STRING(256),
allowNull: true,
},
eval_mid: {
type: new DataTypes.STRING(256),
allowNull: true,
}
}, {
tableName: 'Categorie',
sequelize
})
Categorie.belongsToMany(Unit, { through: 'Cat_Unit' });
Unit.belongsToMany(Categorie, { through: 'Cat_Unit' });
sequelize.sync();
relation file :
interface UserCatAttributes {
id: number;
UserId: number;
CategorieId: number;
prog: number;
}
export class UserCat implements UserCatAttributes {
public id!: number;
public UserId!: number;
public CategorieId!: number;
public prog!: number;
public readonly createdAt!: Date;
public readonly updatedAt!: Date;
}
And my main file :
import express from 'express';
import { Express } from "express-serve-static-core";
import { Sequelize } from 'sequelize';
import { User } from './models/user.model';
import { Categorie } from './models/categorie.model';
import { Unit } from './models/unit.model';
import { UserCat } from './models/user_cat.model';
import { initRoutes } from './routes/index';
import { sequelize_config } from './config/sequelizeConfig';
import passport from 'passport';
import cors from 'cors';
const port: number = 8080;
class Beyond {
public app: Express;
public sequelize: Sequelize;
constructor() {
this.initApp()
}
initApp() {
this.initExpress()
this.initSequelize()
}
initExpress() {
this.app = express();
this.app.use(cors({
optionsSuccessStatus: 200
}))
this.app.use(express.json());
this.app.use(passport.initialize());
this.app.use(passport.authenticate('session'));
initRoutes(this.app);
this.app.listen(port, () => {
console.log("Serveur à l'écoute sur le port : ", port)
})
}
async initSequelize() {
this.sequelize = new Sequelize(
sequelize_config.db_name,
sequelize_config.db_user,
sequelize_config.db_pw,
sequelize_config.sequelize_info as any
);
await this.sequelize.authenticate().then(() => {
console.log("Connexion ok")
}).catch(err => {
console.log("err connexion : ", err)
})
}
}
export const beyond = new Beyond();
All i want to do is a many to many relation, where User can have many Categorie and Categorie many User.
What driving me crazy is everything was working perfectly before idk what event, the tables where created and all the backend has been made with thooses models
ex
export async function getCatByUserId(id: number): Promise<Array<Categorie>> {
const user = await User.findByPk(id, { include: Categorie });
return await user.getCategories();
}
and since then no way to make it works. I'am far for being a pro so any help is appreciated.
You need to remove cross-references from model modules and define functions to register associations and call them after all your models will be registered in Sequelize instance.
See my answer here to get an idea of how to do it.

relations OneToMany - ManyToOne return null typeorm

I made a relationship between two tables that it has: Users and Tasks.
according to the Typeorm ducommentation
Models:
#Entity('tasks')
class Tasks {
#PrimaryGeneratedColumn('uuid')
id: string;
#Column()
name: string;
#Column({
type: 'varchar',
})
status: tasksStatus;
#ManyToOne(() => User, user => user.tasks)
user: User;
#Column()
description: string;
#CreateDateColumn()
created_at: Date;
#UpdateDateColumn()
updated_at: Date;
}
#Entity('users')
class User {
#PrimaryGeneratedColumn('uuid')
id: string;
#Column()
name: string;
#OneToMany(() => Tasks, task => task.user)
tasks: Tasks[];
#Column({
unique: true,
})
email: string;
#Column()
password: string;
#CreateDateColumn()
created_at: Date;
#UpdateDateColumn()
updated_at: Date;
}
Repositories:
public async findAll(): Promise<Tasks[]> {
return this.ormRepository.find({ relations: ['user'] });
}
public async findByAll(): Promise<User[]> {
return this.ormRepository.find({
relations: ['tasks'],
});
}
when I try to get a get, to get the user's listing, along with the tasks created by that user. the column value is null
and if i do a get on tasks it returns me user as null
[
{
"id": "91d9c552-64e7-4f64-b6e8-b8cfc9c6323a",
"name": "teste do teste",
"status": "NEW",
"description": "testeteste do testeteste do testeteste do teste",
"created_at": "2021-04-16T11:23:01.144Z",
"updated_at": "2021-04-16T11:23:01.144Z",
"user": null
}
]
[ {
"id": "ba2673a6-1d76-4294-98d1-3dc4556733d7",
"name": "Wesley9",
"email": "wesley19#email.com",
"password": "$2a$08$xTprPchGJj3vy3vRfa2P2OWHn5V.hqMh8gQn7323J1wi7WjeWXzbG",
"created_at": "2021-04-16T11:22:28.617Z",
"updated_at": "2021-04-16T11:22:28.617Z",
"tasks": []
}
]

Mapping between two different Angular models

I'm trying to map Order.ts to OrderAction.ts, but getting the error Cannot ready property '0' of undefined. This error is coming up when I try to map the Order properties to OrderAction properties.
Here is Order.ts:
export class Order {
OrderId: number;
FunctionStatusList?: OrderFunctionStatus[];
}
export class OrderFunctionStatus {
FunctionTypeCode: number;
AvailableActions: OrderAvailableAction[];
}
export class OrderAvailableAction {
ActionLabel: string;
ActionValue: string;
}
Here is OrderAction.ts:
export class OrderAction {
FunctionTypeCode: number;
SelectedAction: string;
OrderList: AvailableAction[];
}
export class AvailableAction {
OrderId: number;
IsAvailableAction: boolean;
AvailableActions?: OrderAvailableAction[];
}
Here is the code that I wrote:
orders: any[] = [];
orderActionList: any[] = [];
ngOnInit() {
this.orders = this.orderService.getCheckedOrders();
this.orders.forEach((order: Order, i) => {
let orderAction: OrderAction = new OrderAction();
orderAction.OrderList[i].OrderId = order.OrderId;
orderAction.FunctionTypeCode = order.FunctionStatusList[i].FunctionTypeCode;
orderAction.AvailableActions = order.FunctionStatusList[i].AvailableActions;
orderAction.IsAvailableAction = order.FunctionStatusList[i].AvailableActions.length > 0 == true || false;
this.orderActionList.push(orderAction);
});
}
Here is a sample of the Order.ts json:
{
"OrderId": "1",
"FunctionStatusList": [{
"FunctionTypeCode": "1",
"AvailableActions": [{
"ActionLabel": "1",
"ActionValue": "1"
}]
}]
}
Here is a sample of the OrderAction.ts json:
{
"FunctionTypeCode": "1",
"SelectedAction: "1",
"OrderList": [{
"OrderId": "1",
"IsAvailableActionsLoaded": "1",
"AvailableActions": [{
"ActionLabel": "1",
"ActionValue": "1"
}]
}]
}
I'm not sure where exactly you're getting the error, but I did the following and it converts Order to OrderAction: https://plnkr.co/edit/VEHAdk3qPRIFkEAU?preview
The meat of the code is this:
this.orders.forEach((order: Order, i) => {
this.orderActions.push({
FunctionTypeCode: order.FunctionStatusList[i].FunctionTypeCode,
SelectedAction: null,
OrderList: [
{
OrderId: order.OrderId,
IsAvailableActionsLoaded:
order.FunctionStatusList[i].AvailableActions.length > 0,
AvailableActions: order.FunctionStatusList[i].AvailableActions,
},
],
});
});
I left SelectedAction as null because it was unclear how this value would be set.

how to parse json to angular 7 object?

I am trying to consume a web API that returns the following data
{
"FileStatuses": {
"FileStatus": [
{
"accessTime": 0,
"blockSize": 0,
"childrenNum": 13,
"fileId": 16396,
"group": "supergroup",
"length": 0,
"modificationTime": 1553247533630,
"owner": "hduser",
"pathSuffix": "demo-data",
"permission": "755",
"replication": 0,
"storagePolicy": 0,
"type": "DIRECTORY"
},
{
"accessTime": 0,
"blockSize": 0,
"childrenNum": 7,
"fileId": 16410,
"group": "supergroup",
"length": 0,
"modificationTime": 1550659883380,
"owner": "hduser",
"pathSuffix": "instacart",
"permission": "755",
"replication": 0,
"storagePolicy": 0,
"type": "DIRECTORY"
}
]
}
}
I created a service like this and the class to parse the json response to it:
public getHadoopDirList(): Observable<FileStatus[]> {
return this.http.get<FileStatus[]>(this.webHdfsUrl, {}).pipe(map(data => data));
}
export class FileStatus {
accessTime: number;
blockSize: number;
childNum: number;
fileId: number;
group: string;
length: number;
modificationTime: number;
owner: string;
pathSuffix: string;
permission: string;
replication: number;
storagePolicy: number;
type: string;
}
i subscribed to it on the component but when i try to iterate over it on the template i get the following ERROR Error: Error trying to diff '[object Object]'. Only arrays and iterables are allowed
I think the problem is the way how to map it but I didn't know how to solve it
use http://json2ts.com/ to convert JSON to interface
Your inteface should be like below
export interface FileStatus {
accessTime: number;
blockSize: number;
childrenNum: number;
fileId: number;
group: string;
length: number;
modificationTime: any;
owner: string;
pathSuffix: string;
permission: string;
replication: number;
storagePolicy: number;
type: string;
}
export interface FileStatuses {
FileStatus: FileStatus[];
}
export interface FileStatusesRootObject {
FileStatuses: FileStatuses;
}
and then
return this.http.get<FileStatusesRootObject>(
You need to make sure the data types match. It expects a result of type FileStatus[]. Thus, on your RxJS's map(), you will need to return the right data respectively by selecting FileStatus, which contains the array of objects with the type of FileStatus
public getHadoopDirList(): Observable<FileStatus[]> {
return this.http.get<FileStatus[]>(this.webHdfsUrl, {})
.pipe(
map(data => data['FileStatuses']['FileStatus'])
);
}

Angular 4+ handling multiarray json response best practice

user.ts
import { Company } from "./company";
export class User{
token: string;
companies: Company;
name: string;
email: string;
currentCompanyID: string;
constructor(){
}
}
company.ts
export class Company{
companyId: string;
name: string;
orgNo: string;
constructor(){
}
}
service.ts
getData(): Observable<User> {
return this.http.get(this.url).map((res: Response) => res.json())
}
component.ts
//call to my api in service.ts
this.avrs.getData().subscribe(
res => {
console.log("Result");
console.log("main res: " + res);
console.log("Name: " + res.name);
console.log("companyid: " + res.currentCompanyID);
console.log("companies: " + res.companies);
console.log("token: " + res.token);
console.log("user data: " + res.companies.companyId);
console.log("user data: " + res.companies.name);
console.log("user data: " + res.companies.orgNo);
},
error => {
console.log(error);
},
() => {
}
);
}
Output
Result
main res: [object Object]
Name: undefined
companyid: 28764
companies: [object Object]
token: lkjdfjsgosdfjuguerujgoiehjiughdskjge9r8w
user data: undefined
user data: undefined
user data: undefined
Json Response example:
{
"user": {
"id": 123456,
"company_id": "28764",
"name": "TEST",
"email": "test#dummy.com",
"mobile": "91273493412412",
"locale": "en",
"companies": [
{
"companyId": "idww",
"name": "nameww",
"orgNo": "orgww",
"roles": [
{
"role": "Admin"
},
{
"role": "Guest"
}
],
"services": []
},
{
"companyId": "idqq",
"name": "nameqq",
"orgNo": "orgqq",
"roles": [
{
"role": "Admin"
},
{
"role": "Guest"
}
],
"services": []
},
],
},
"token": "lkjdfjsgosdfjuguerujgoiehjiughdskjge9r8w",
"currentCompanyID": "28764"
}
I want to get:
token,
companyid,
user -> name,
user -> companies array
The question is basically what is the best way (best practice) to handle many array in a json file in Angular 2+? As you can see with my output I'm only getting companyid and token data, and not getting companies array or user data.
Create Role Model:
export class Role{
id: number;
name: string;
constructor(attrs: any = null) {
if (attrs) {
this.build(attrs);
}
}
build(attrs: any): void {
this.id = attrs.id;
this.name = attrs.name;
}
}
Create Company Model:
export class Company{
id: number;
name: string;
roles: Array<Role>;
services: any;
constructor(attrs: any = null) {
if (attrs) {
this.build(attrs);
}
}
build(attrs: any): void {
this.id = attrs.id;
this.name = attrs.name;
this.services = attrs.services;
if(attrs.roles) {
this.roles = attrs.roles.map(
r => new Role(r)
);
}
}
}
Create User Model:
export class User{
id: number;
name: string;
email: string;
companies: Array<Company>;
constructor(attrs: any = null) {
if (attrs) {
this.build(attrs);
}
}
build(attrs: any): void {
this.id = attrs.id;
this.name = attrs.name;
this.email = attrs.email;
if(attrs.companies) {
this.companies = attrs.companies.map(
c => new Company(c)
);
}
}
}
By doing so, you create entities automatically. According to your Json Response example, you need to create another model. Let's call it Data!
export class Data{
user: User;
token: string;
currentCompanyID: string;
constructor(attrs: any = null) {
if (attrs) {
this.build(attrs);
}
}
build(attrs: any): void {
this.currentCompanyID = attrs.currentCompanyID;
this.token = attrs.token;
this.user = attrs.user;
}
}
Retrieve data from sample service method like this:
getData(): Observable<any>{
return this.http
.get('awesome_url')
.map((data: any) => new Data(data));
}
Hope I help!