Prisma Many-to-Many relations with findMany (select) - mysql

I am facing an error with Prisma, it does not recognize my request which seems quite simple to me. I need to use "select" to retrieve only certain fields from the table.
Post model:
model Post {
id String #id #default(cuid())
title String
createdAt DateTime? #default(now())
categories CategoriesOnPosts[]
keywords KeywordsOnPosts[]
##map("posts")
}
Category model:
model Category {
id String #id #default(cuid())
name String
createdAt DateTime? #default(now())
posts CategoriesOnPosts[]
##map("categories")
}
CategoriesOnPosts model:
model CategoriesOnPosts {
postId String
categoryId String
post Post #relation(fields: [postId], references: [id])
category Category #relation(fields: [categoryId], references: [id])
##id([postId, categoryId])
##map("categoriesPosts")
}
My Prisma query:
export const getPosts = async () =>
await prisma.post.findMany({
select: {
id: true,
title: true,
categories: {
select: {
name: true,
slug: true,
},
},
createdAt: true,
},
orderBy: [
{
createdAt: 'desc',
},
],
});
I get the following error and I don't know how to fix it.
Unknown field categories for select statement on model Post.
Available options are listed in green.

Related

Prisma Nodejs, relation with ids to get the name

im in trouble to find a way to make this, i want in "serieName" the name of the serie, but always return de id, i dont know what i need to do to make this relation correct, im trying to find a solution, but nothing works.
this is my models:
in Series model, i have a name, and in Figures_table i made a relation with him, and i want to get only the name when i pass the id for the field, so if Series have id: 1, name: "something", i want to show "something" not the id, but only show id.
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
model Series {
id Int #unique #default(autoincrement())
serie String
Figures_table Figures_table[]
}
model Manufacturers {
id String #unique
manufacturer String
Figures_table Figures_table[]
}
model Figures_table {
id String #unique
name String #db.LongText
category String #db.LongText
price String #db.LongText
specifications String #db.LongText
releaseInfo String #db.LongText
details String #db.LongText
serieName Int
manufacturerName String #db.VarChar(191)
createdAt DateTime #default(now())
manufacturer Manufacturers #relation(fields: [manufacturerName], references: [id])
series Series #relation(fields: [serieName], references: [id])
Images Images[]
}
model Images {
id Int #unique #default(autoincrement())
link String
figureID String
figureReferenceID Figures_table #relation(fields: [figureID], references: [id])
}
Here's a script which would include the name of the Series while fetching a record from the figures_table
import { PrismaClient, Prisma } from '#prisma/client';
const prisma = new PrismaClient({
log: ['query', 'info', 'warn'],
});
async function main() {
await prisma.figures_table.create({
data: {
category: 'Figures Table Category',
details: 'Figures Table Details',
name: 'Figures Table Name',
id: '1',
price: '1',
releaseInfo: 'Figures Table Release Info',
specifications: 'Figures Table Specifications',
series: {
create: {
serie: 'Series 1',
},
},
manufacturer: {
create: {
manufacturer: 'Manufacturer 1',
id: '1',
},
},
},
});
const figures_table = await prisma.figures_table.findUnique({
where: {
id: '1',
},
include: {
series: true,
},
});
console.log(figures_table);
}
main()
.catch((e) => {
throw e;
})
.finally(async () => {
await prisma.$disconnect();
});
We used the include section to get the details of Series relation.
Response:
{
id: '1',
name: 'Figures Table Name',
category: 'Figures Table Category',
price: '1',
specifications: 'Figures Table Specifications',
releaseInfo: 'Figures Table Release Info',
details: 'Figures Table Details',
serieName: 1,
manufacturerName: '1',
createdAt: 2022-10-11T12:33:37.768Z,
series: { id: 1, serie: 'Series 1' }
}

Using explicit Many to Many Relation in Prisma

I have the following User and Group models that share a many-to-many relation:
model User {
id String #id #default(uuid())
email String #unique
groups UsersGroups[]
##map("user")
}
model Group {
id String #id #default(uuid())
name String
users UsersGroups[]
##map("group")
}
model UsersGroups {
user User #relation(fields: [userId], references: [id])
userId String #map(name: "user_id")
group Group #relation(fields: [groupId], references: [id])
groupId String #map(name: "group_id")
##id([userId, groupId])
##map("users_groups")
}
I'm having trouble using the connect API in Prisma to connect the users and groups. Here's what I have:
await prisma.group.update({
where: {
id: groupId,
},
data: {
users: {
connect: users.map((user) => ({ id: user.id })),
},
},
include: { users: true },
});
That doesn't work and here is the error I'm getting in the console:
PrismaClientValidationError:
Invalid `prisma.group.update()` invocation:
{
where: {
id: '64ce24c7-3054-42f2-b49f-4cdb52cf1bc7'
},
data: {
users: {
connect: [
{
id: '0b3f4a51-0efe-4b0a-8763-e71bc8091b86'
~~
}
]
}
},
include: {
users: true
}
}
Unknown arg `id` in data.users.connect.0.id for type UsersGroupsWhereUniqueInput. Available args:
type UsersGroupsWhereUniqueInput {
userId_groupId?: UsersGroupsUserIdGroupIdCompoundUniqueInput
}
From that above, it looks as though it's attempting to connect a user with id: '0b3f4a51-0efe-4b0a-8763-e71bc8091b86' (which is a user that exists) to the group with id: '64ce24c7-3054-42f2-b49f-4cdb52cf1bc7' (which also exists).
I'd be very grateful if someone could point out where I'm going wrong as I've been going in circles with this for a while now...
You are using an explicit many-to-many relation, cf. https://www.prisma.io/docs/concepts/components/prisma-schema/relations/many-to-many-relations#explicit-many-to-many-relations
I.e. you have defined the model UsersGroups yourself.
As a consequence, you would have to manage/create the records in this table yourself and connect it with the entry in the third table, e.g. like this (haven't tested it):
prisma.group.update({
where: {
id: groupId,
},
data: {
users: { create: { user: { connect: { id: userId } } } },
},
include: { users: true },
});
or if you want to loop over an list:
prisma.group.update({
where: {
id: groupId,
},
data: {
users: {
create: users.map((user) => ({
user: { connect: { id: user.id } },
})),
},
},
include: { users: true },
});
I would suggest to replace groups UsersGroups[] and users UserGroups[] with
userGroups UsersGroups[] in the schema to make it clearer.
As an alternative to explicit relationships you could try to use implicit many-to-many relations in the schema like this:
model User {
id String #id #default(uuid())
email String #unique
groups Group[]
##map("user")
}
model Group {
id String #id #default(uuid())
name String
users User[]
##map("group")
}
cf. https://www.prisma.io/docs/concepts/components/prisma-schema/relations/many-to-many-relations#implicit-many-to-many-relations

Prisma - Unique constraint failed, while there is no unique field in schema

I am using prisma with three models,
model User {
id String #id #default(cuid())
name String #unique #db.VarChar(35)
email String #unique #db.VarChar(512)
password String #db.VarChar(1024)
details String #default("") #db.VarChar(512)
avatar String #default("/default-avatar.svg") #db.VarChar(150)
activity String #default("Online") #db.VarChar(25)
likes Int #default(0)
suggestions Boolean #default(false)
verified Boolean #default(false)
blockedUsers BlockedUser[]
comments Comment[]
communities Community[]
communityMembers CommunityMember[]
followers Follower[]
friends Friend[]
messages Message[]
posts Post[]
openDMs DM[]
interests UserInterest[]
##map("user")
}
model Community {
id String #id #default(cuid())
title String #unique #db.VarChar(35)
details String #db.VarChar(512)
memberID String?
membersUser User[]
members CommunityMember[]
interests CommunityInterest[]
posts Post[]
##map("community")
}
model CommunityMember {
id String #id #default(cuid())
nickname String?
userID String
communityID String
user User #relation(fields: [userID], references: [id])
community Community #relation(fields: [communityID], references: [id])
owner Boolean
##map("community_member")
}
I have a route in my backend that causes the problem. It creates a new community member table with prisma client and connects the existing user and community to itself, with their ids.
When I do this, I get an error: Unique constraint failed on the constraint: community_member_communityID_key
Here is the code with the creation of the community member:
await prisma.communityMember.create({
data: {
nickname: response.account.name,
user: {
connect: { id: response.account.id }
},
community: {
connect: { id: communityID }
},
owner: false
}
});
I have tried dropping the database and resetting the prisma migrations, with no luck.
When I view the table in mysql, it is apparent that the communityID and userID fields are set as unique, so I think this problem has to do with prisma migrate.
Does anybody know what's happening, and how I can successfully create these fields without them being unique?
Why are you using connect? According to documentation
https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries#nested-writes
In nested writes you should use create:
await prisma.communityMember.create({
data: {
nickname: response.account.name,
user: {
create: [
{id: response.account.id}
]
},
community: {
create: [
{id: communityID}
]
},
owner: false
}
})

Prisma delete many to many relationship with Composite Key

I have this schema here:
model label {
title String #id #db.VarChar(16)
color String #db.VarChar(16)
labelplaylist labelplaylist[]
}
model labelplaylist {
playlistId Int
labelId String #db.VarChar(16)
label label #relation(fields: [labelId], references: [title])
playlist playlist #relation(fields: [playlistId], references: [id])
##id([playlistId, labelId])
##index([labelId], name: "labelId")
}
model playlist {
id Int #id #default(autoincrement())
createdAt DateTime? #default(now()) #db.DateTime(0)
title String #db.VarChar(100)
labelplaylist labelplaylist[]
##index([userId], name: "userId")
}
And I would like to delete only the relation between the label and the playlist table. I tried it to do like this:
const deleteRelation = await prisma.labelplaylist.delete({
where: {
playlistId_labelId:
},
})
I have the primary key of the label and playlist table, but I don't know how I get the primary key => playlistId_labelId.
Thank's for helping out.
Here's the syntax for queries with composite key
const deleteRelation = await prisma.labelplaylist.delete({
where: {
playlistId_labelId: {
playlistId: playListIdVariable, //replace with appropriate variable
labelId: labelIdVariable, //replace with appropriate variable
},
},
});
You can read more in the get record by compound ID or compound unique identifier subsection of the CRUD Reference Guide in the Prisma docs. This reference shows reading data, but the where condition is similar for delete and update as well.
Since it's an explicit many-to-many relationship, nested deleteMany only deletes relation table records, acting like a disconnect. You can then write your query like that:
await req.db.playlist.update({
data: {
labels: {
deleteMany: {},
},
},
where: {
id: labelId,
},
})
or the other way around.
It won't delete your related table records but only the links between them.

TypeORM save nested objects

I'm working on a express (with TypeORM) + ReactJS app.
The problem is that I have 3 entities linked by OneToMany relationship like this:
customer
product(linked to customer)
model(linked to product)
import { Product } from './product.entity'
#Entity('customer')
export class Customer extends BaseEntity{
#PrimaryGeneratedColumn()
readonly id: number;
#Column ({name: 'name'})
name : string;
#Column ({name: 'test', nullable: true})
test : string;
#OneToMany(() => Product, product => product.customer)
// #JoinColumn({ name: 'product_id' })
products: Product[]
}
import {Customer} from './customer.entity'
import {Model} from './model.entity'
#Entity('product')
export class Product extends BaseEntity{
#PrimaryGeneratedColumn()
readonly id: number;
#Column ({name: 'name'})
name : string;
#Column ({name: 'test', nullable: true})
test : string;
#Column ({name: 'deleted', nullable: true})
deleted : string;
#ManyToOne(() => Customer, customer => customer.products)
#JoinColumn({ name: 'customer_id' })
customer: Customer;
#OneToMany(() => Model, model => model.product)
#JoinColumn({ name: 'customer_id' })
models: Model[]
}
import { Product } from "./product.entity";
#Entity('model')
export class Model extends BaseEntity{
#PrimaryGeneratedColumn()
readonly id: number;
#Column ({name: 'name'})
name : string;
#Column ({name: 'size', nullable: true})
size : string;
#Column ({name: 'deleted', nullable: true})
deleted : string;
#ManyToOne(() => Product, product => product.models)
#JoinColumn({ name: 'product_id' })
product: Product;
}
The save method from Express is:
static add = async(req: Request, res)=> {
const connection = getConnection();
const queryRunner = connection.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
try {
let customer: Customer
customer = await queryRunner.manager.getRepository(Customer).findOneOrFail(req.body.id)
const productsReq: Array<Product> = req.body.products
productsReq.forEach(async (product: any) => {
let updatedProd = {...product, customer: customer}
const savedProd = await queryRunner.manager.getRepository(Product).save({...updatedProd})
product.models.forEach(async (model: any) => {
let updatedModel = {...model, product: savedProd}
await queryRunner.manager.getRepository(Model).save(updatedModel)
});
});
await queryRunner.commitTransaction();
return res.send('Saving done')
} catch (error) {
console.log(error)
await queryRunner.rollbackTransaction();
res.status(500).send('Some error occurs');
} finally {
}
}
Currently, in the DB, I have the following data:
1 customer with id 30
name
id
test
first customer
30
test column
1 product with id 119 linked to customer 30
id
name
test
customer_id
deleted
119
first product
test column
30
2 models with ids: 90 and 91 linked to product 119
id
name
size
deleted
product_id
91
second model witout id
2000
119
90
first model with id
1000
119
Next, from React i'm trying to update only the model with id 90 and add a new model.
(So i'm not sending to backend all the models, model with id 91 is not sent).
The JSON object sent from frontend to backend looks like this:
{
"id": 30,
"name": "first customer",
"test": "test column",
"products": [
{
"id" : 119,
"name": "first product",
"test": "test column",
"models": [
{
"id": 90,
"name": "first model with id",
"size": 1000
},
{
"name": "second model witout id",
"size": 2000
}
]
}
]
}
But the problem is in DB the foreing key "product_id" on table "model" is set to null for model with id 91 and a new row(92) is inserted.
The result is:
|id|name|size|deleted|product_id|
|--|----|----|-------|----------|
|91|second model witout id|2000|||
|90|first model with id|1000||119|
|92|second model witout id|2000||119|
How can I add a new model and update an existing one without sending all the existing models in DB ?
I think i found a solutions.
I changed the save method from Express like this:
static add = async(req: Request, res)=> {
const connection = getConnection();
const queryRunner = connection.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
try {
let customer: Customer
let customerReq: any = req.body
customer = await queryRunner.manager.getRepository(Customer).findOneOrFail(req.body.id)
const productsReq: Array<Product> = req.body.products
productsReq.forEach(async (product: any) => {
let updatedProd = {...product, customer: customer}
let addedModels: Model[] = []
product.models.forEach(async (model: any) => {
const updatedModel = await queryRunner.manager.getRepository(Model).save({...model, product: product})
addedModels.push(updatedModel)
});
const existingProd = await queryRunner.manager.getRepository(Product).save({...updatedProd})
await (await existingProd.models).push(...addedModels)
const savedProd = await queryRunner.manager.getRepository(Product).save({...updatedProd})
});
await queryRunner.commitTransaction();
return res.send('Adding ok')
} catch (error) {
console.log(error)
await queryRunner.rollbackTransaction();
res.status(500).send('Something went terribly wrong');
} finally {
console.log('release')
// await queryRunner.release();
}
}
It's strange becouse 2 transaction are started:
query: START TRANSACTION
query: SELECT "Customer"."id" AS "Customer_id", "Customer"."name" AS "Customer_name", "Customer"."test" AS "Customer_test" FROM "customer" "Customer" WHERE "Customer"."id" IN ($1) -- PARAMETERS: [30]
query: SELECT "Customer"."id" AS "Customer_id", "Customer"."name" AS "Customer_name", "Customer"."test" AS "Customer_test" FROM "customer" "Customer" WHERE "Customer"."id" IN ($1) -- PARAMETERS: [30]
query: COMMIT
query: SELECT "Model"."id" AS "Model_id", "Model"."name" AS "Model_name", "Model"."size" AS "Model_size", "Model"."deleted" AS "Model_deleted", "Model"."product_id" AS "Model_product_id" FROM "model" "Model" WHERE "Model"."id" IN ($1) -- PARAMETERS: [132]
query: SELECT "Product"."id" AS "Product_id", "Product"."name" AS "Product_name", "Product"."test" AS "Product_test", "Product"."deleted" AS "Product_deleted", "Product"."customer_id" AS "Product_customer_id" FROM "product" "Product" WHERE "Product"."id" IN ($1) -- PARAMETERS: [119]
query: INSERT INTO "model"("name", "size", "deleted", "product_id") VALUES ($1, $2, DEFAULT, $3) RETURNING "id" -- PARAMETERS: ["second model witout id",2000,119]
release
Morgan --> POST 200 /test # Tue, 04 May 2021 14:28:08 GMT ::ffff:127.0.0.1 from undefined PostmanRuntime/7.28.0
query: START TRANSACTION
query: SELECT "Product"."id" AS "Product_id", "Product"."name" AS "Product_name", "Product"."test" AS "Product_test", "Product"."deleted" AS "Product_deleted", "Product"."customer_id" AS "Product_customer_id" FROM "product" "Product" WHERE "Product"."id" IN ($1) -- PARAMETERS: [119]
query: UPDATE "model" SET "size" = $2 WHERE "id" IN ($1) -- PARAMETERS: [132,8000]
query: COMMIT