Prisma throwing error: Ambiguous relation detected - sql

I'm using prisma and trying to model a referral table for my Postgres database. Not sure if the db schema is correct, but I have a table with a referId, userId: 1 to 1, and referredUserId: 1 to many.
model Referral {
id Int #id #default(autoincrement())
createdAt DateTime #default(now())
updatedAt DateTime #default(now())
referId String // Auto Generate Random String
userId Int #unique
user User #relation(fields: [userId], references: [id])
referredUsersId Int[]
referredUsers User[] #relation(fields: [referredUsersId], references: [id])
}
I'm not sure exactly how to reference these in the User model. I tried
Referral Referral?
UsersReferred Referral[]
But I get an error
Error validating model "User": Ambiguous relation detected
What's the correct way to model a referral table and how can I do it in prisma?

There are multiple relations between the same two models so you would need to specify the name for the relations.
model User {
id String #id
Referral Referral? #relation("UserReferral")
UsersReferred Referral[] #relation("ReferredUsers")
}
model Referral {
id Int #id #default(autoincrement())
createdAt DateTime #default(now())
updatedAt DateTime #default(now())
referId String
userId String #unique
user User #relation(fields: [userId], references: [id], name: "UserReferral")
referredUsersId String
referredUsers User #relation(fields: [referredUsersId], references: [id], name: "ReferredUsers")
}
Here's the reference for Disambiguating relations: Reference

Related

Using junction tables created in schema within Spring Boot JDBC

I'm trying out pre-defining my database structure using SQL schema and then utilising it within my Kotlin code. I'm using Spring Boot and Spring Data JDBC.
Here's what I currently have so far:
My User class:
data class User(
#Column(name = SqlQueries.Users.Entries.id) // "user_id"
val id: String?,
#Column(name = SqlQueries.Users.Entries.firstName) // "user_first_name"
var firstName: String = "Joe",
#Column(name = SqlQueries.Users.Entries.lastName) // "user_last_name"
var lastName: String = "Bloggs",
#Column(name = SqlQueries.Users.Entries.username) // "user_username"
var username: String = "${firstName}.${lastName}",
#Column(name = SqlQueries.Users.Entries.password) // "user_password"
#JsonIgnore
var password: String = "password1",
#Column(name = SqlQueries.Users.Entries.isActive) // "user_is_active"
val isActive: Boolean = true,
)
My UserRole class:
data class UserRole(
#Column(name = SqlQueries.Lookups.UserRoles.Entries.id)
val id: Int? = null,
#Column(name = SqlQueries.Lookups.UserRoles.Entries.roleName)
val name: String = "",
)
My Schema:
-- ===================================================================================
-- Lookup Tables
-- ===================================================================================
-- Creates our User Table if one does not exist within the database already.
CREATE TABLE IF NOT EXISTS table_users(
user_id VARCHAR(60) DEFAULT RANDOM_UUID() UNIQUE PRIMARY KEY,
user_first_name VARCHAR NOT NULL,
user_last_name VARCHAR NOT NULL,
user_username VARCHAR NOT NULL UNIQUE,
user_password VARCHAR NOT NULL,
user_is_active VARCHAR NOT NULL
);
-- ===================================================================================
-- Lookup Tables
-- ===================================================================================
-- Creates our Roles Lookup Table if one does not already exist within the database.
CREATE TABLE IF NOT EXISTS lookup_roles(
role_id SMALLINT AUTO_INCREMENT UNIQUE PRIMARY KEY,
role_name VARCHAR NOT NULL UNIQUE
);
-- ===================================================================================
-- Junction Tables
-- ===================================================================================
-- Creates our User/Role Junction table if one does not already exist within the database.
-- This will be the foundation of a many to many relationship between the two entities.
CREATE TABLE IF NOT EXISTS junction_user_role(
user_id VARCHAR(60),
role_id SMALLINT,
CONSTRAINT pk_user_role PRIMARY KEY (user_id, role_id),
CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES table_users (user_id),
CONSTRAINT fk_role FOREIGN KEY (role_id) REFERENCES lookup_roles (role_id)
);
As you can see, I've created a User, Role and User/Role table. This is designed to have a many to many relationship.
I'm in the dark with being able to "access" the relationship as a variable which I can use later on.
In my previous "prototype" which had a different design concept I used the following format:
User class:
...
#ManyToMany(fetch = FetchType.EAGER)
#Column(name = "user_roles")
var userRoles: MutableSet<Role> = mutableSetOf(),
...
How would I define and implement this with the new way of doing it?
I don't really speak Kotlin, but I try anyway. Please use ChatGPT or personal experience to fix Kotlin errors ;-)
The first step is to identify the aggregates an what belongs to which aggregate.
I'd suggest that you have a UserRole and a User aggregate, with the later owning the relationship.
This implies that you need to add the relationship to the User, which is your aggregate root for the User aggregate.
Since Role is a different aggregate you'd reference it by id, and you need a separate entity to hold that id
data class User (
...
#MappedCollection(idColumn="user_id")
val roles: Set<RoleRef>
)
data class RoleRef(
val roleId: AggregateReference<Role, Int>
)
If you want to navigate from UserRole to all the User entities having that role, you'd create a repository method in the UserRepository for this.
How this works is detailed in https://spring.io/blog/2018/09/24/spring-data-jdbc-references-and-aggregates
Also related: Spring Data JDBC many to many relationship management

Prisma ORM optional relations for user models

I am using Prisma ORM with PostgreSQL backend for my database, I have users as students and admins and they have slightly different fields so I created a base model User for common info as well as two more models Admin and Student for further unique info but I can't create an admin (a user referencing an admin) without also referencing it to a student, can anybody help with schema structure?
// schema.prisma
enum Role {
SUPERADMIN
ADMIN
STUDENT
}
model User {
id Int #id #default(autoincrement())
citizenId String #unique
hash String
role Role #default(STUDENT)
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
student Student? #relation(fields: [citizenId], references: [citizenId], map: "user_student_fk")
admin Admin? #relation(fields: [citizenId], references: [citizenId], map: "user_admin_fk")
}
model Admin {
id Int #id #default(autoincrement())
citizenId String #unique
name String
schoolId Int
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
user User?
school School #relation(fields: [schoolId], references: [id])
}
model Student {
id Int #id #default(autoincrement())
citizenId String #unique
name String
classId Int
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
user User?
class Class #relation(fields: [classId], references: [id])
results TestResult[]
}
// ...

How to link many to many items in Prisma without duplicates

I have many to many relationships between Product and Menu.
model Product {
id Int #id #default(autoincrement())
name String
price Float
description String?
product_category ProductCategory #relation(fields: [category_id], references: [id])
category_id Int
menus MenuProducts[]
created_at DateTime #default(now())
updated_at DateTime #updatedAt
##map("products")
}
model Menu {
id Int #id #default(autoincrement())
name String
products MenuProducts[]
created_at DateTime #default(now())
updated_at DateTime #updatedAt
##map("menus")
}
model MenuProducts {
menu_id Int
menu Menu #relation(fields: [menu_id], references: [id])
product_id Int
product Product #relation(fields: [product_id], references: [id])
created_at DateTime #default(now())
updated_at DateTime #updatedAt
##map("menu_products")
##id([product_id, menu_id])
}
In the menu update, I want to assign many products to the menu.
I've tried like this:
this.prisma.menu.update({where: {id}, data: {...updateMenuDto, products: {connect: {id: 6}}}});
but I received this error:
The required connected records were not found. Expected 1 records to be connected after connect operation on one-to-many relation 'MenuToMenuProducts', found 0.
Anyway, I've tried like this:
this.prisma.menuProducts.create({data:{menu: {connect: {id: id}},product: {connect: {id: 6}}}})
and it worked, but on the second call I saw what the line was duplicated. How I can link multiple products to menu without inserting duplicates?
To remove duplicates from the MenuProducts model you can define a unique constraint on the combination of product_id and menu_id which will restrict having duplication of product and menu id.
model MenuProducts {
menu_id Int
menu Menu #relation(fields: [menu_id], references: [id])
product_id Int
product Product #relation(fields: [product_id], references: [id])
created_at DateTime #default(now())
updated_at DateTime #updatedAt
##map("menu_products")
##id([product_id, menu_id])
##unique([product_id, menu_id])
}
Here is a reference of ##unique constraint.

prisma introspect generate invalid model

Prisma version : ^2.26.0
Prisma/client : ^2.26.0
#relation error msg in : Error parsing attribute "#relation": The type of the field userId in the model posts is not matching the type of the referenced field id in model users.
Why prisma created invalid model or something wrong with my code after i run npx prisma introspect?
It cause cannot to enter the prisma studio since those error in my code.
Any solution or thought could share to make me know better for this?
Below is my sql:
-- Create a custom type
CREATE TYPE "user_role_enum" AS ENUM ('user', 'admin', 'superadmin');
-- Create a table
CREATE TABLE "users"(
"id" BIGSERIAL PRIMARY KEY NOT NULL,
"name" VARCHAR(255) NOT NULL,
"email" VARCHAR(255) UNIQUE NOT NULL,
"role" user_role_enum NOT NULL DEFAULT('user')
);
-- Create a table
CREATE TABLE "posts"(
"id" BIGSERIAL PRIMARY KEY NOT NULL,
"title" VARCHAR(255) NOT NULL,
"body" TEXT,
"userId" INTEGER NOT NULL,
FOREIGN KEY ("userId") REFERENCES "users"("id")
);
-- Create data
INSERT INTO "users" ("name", "email", "role")
VALUES('John Doe', 'john#email.com', 'admin'),
('jane Doe', 'jane#email.com', 'admin'),
('Ahmed Hadjou', 'ahmed#hadjou.com', 'user');
INSERT INTO "posts" ("title", "body", "userId")
VALUES('Hello World!!', 'Hey guys, I see a rainbow through this prisma :D', 1),
('So do I', 'It looks cooool!!!', 2),
('It does', 'Yeahhh', 1);
Generate for model from prisma introspect
model posts {
id BigInt #id #default(autoincrement())
title String #db.VarChar(255)
body String?
userId Int
users users #relation(fields: [userId], references: [id]) // Error parsing attribute "#relation": The type of the field `userId` in the model `posts` is not matching the type of the referenced field `id` in model `users`.
}
model users {
id BigInt #id #default(autoincrement())
name String #db.VarChar(255)
email String #unique #db.VarChar(255)
role user_role_enum #default(user)
posts posts[] // error in here
}
enum user_role_enum {
user
admin
superadmin
}
Just change Int to BigInt userId in Post Model , it will work

My GORM Primary ID is getting set to an 18 digit number, how can i force this number to start at 1

I'm using GORM to handle my database operations. My struct is as follows
type Job struct {
gorm.Model
JobID int `gorm:"primary_key"`
Jobitem []Jobitem
SubID int `sql:"not null"`
StartDateTime string `sql:"not null"`
JobStatus string `sql:"not null"`
}
When I insert into the table using this struct, my primary key is 18 digits long, for example: 399758776912773121
Is it possible to get this id to begin at 1 and increment from there?
You're using both gorm.Model and defining your own primary key. So my guess is that gorm automatically thinks you're trying to use Composite Primary Key, since your struct actually looks like this:
type Job struct {
// gorm.Model ↓
ID uint `gorm:"primary_key"` // Gorm uses this
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time `sql:"index"`
// gorm.Model ↑
JobID int `gorm:"primary_key"` // And also detects this
Jobitem []Jobitem
SubID int `sql:"not null"`
StartDateTime string `sql:"not null"`
JobStatus string `sql:"not null"`
}
Long story short, have a look there for some documentation on gorm.Model
Thanks Depado,
It looks like this is built in behavior by the database and not specifically related to GORM. The ID fields are generated using the unique_rowid() function in cockroachdb which is generated by a combination of the timestamp and id of the node executing the insert.