Prisma schema - Create a relation field from multiple possible foreign keys (OR relation) - prisma

How to create a relation field in prisma schema that target 2 possible relation scalar fields ?
For instance, in soccer, let's say we've got the following 2 models:
model Match {
team1Id Int
team1 Team #relation("team1", fields: [team1Id], references: [id])
team2Id Int
team2 Team #relation("team2", fields: [team2Id], references: [id])
}
model Team {
id Int #default(autoincrement()) #id
name String
matches Match[] #relation( /* What to put here ? */ ) // <----
}
What to put in the Team.matches relation field definition in order to allow Team.matchs to contain any match the team played from any side, as team1 or as team2 ?

This is not possible in Prisma right now! I created an issue in our repo for technical specifications to think about ways for improving this!
Workaround
With Prisma you always need to have a relation field on both sides per relation. This means you need to have two relation fields on Team, one which represents the matches where the team played "as team 1", another where it played "as team 2".
model Match {
team1Id Int
team1 Team #relation("team1", fields: [team1Id], references: [id])
team2Id Int
team2 Team #relation("team2", fields: [team2Id], references: [id])
##id([team1Id, team2Id])
}
model Team {
id Int #default(autoincrement()) #id
name String
matchesAsTeam1 Match[] #relation("team1")
matchesAsTeam2 Match[] #relation("team2")
}

Related

enum in prisma instead of table

I have two tables in my Prisma db. One is for admins and the other is for users, and since they differ a lot from each other I decided not to create an isAdmin field. They have both in common the establishment that is described as a table (one-to-many relation), but it has only one field (name). I'd like to know whether it is a good idea to create an Enum to represent this field. My only doubt is that, since the values of the enums can change (adding more fields dynamically), it is not as good as creating a table
model Admin {
id String #id #default(uuid())
pin Int
establishment Establishment #relation(fields: [establishmentId], references: [id])
establishmentId Int
}
model User {
id String #id #default(uuid())
name String
balance Float #default(0)
establishment Establishment #relation(fields: [establishmentId], references: [id])
establishmentId Int
}
model Establishment {
id Int #id #default(autoincrement())
name String
}
This is how I've imagined the new data source to be like
model Admin {
id String #id #default(uuid())
pin Int
establishment Establishment
}
model User {
id String #id #default(uuid())
name String
balance Float #default(0)
establishment Establishment
}
enum Establishment {
ESTABLISHMENT1
ESTABLISHMENT2
}
If you are sure that you won't need to store any more fields for Establishment then the enums approach is good. But in case in future you are envisioning that there could be other common fields, then you would need to duplicate them in Users and Admins Table and Enums approach won't be a fit.
Another approach could be to use the check constraint on database level to only allow two valid values i.e. ESTABLISHMENT1 and ESTABLISHMENT2, this way you won't even need to define enums.

Reuse same model for two fields in Prisma

My goal is for this:
I have a table of professions. A person can have two professions - a primary and secondary profession.
model Person {
id String #id #default(cuid())
PrimaryProfessionId String?
Secondary String?
PrimaryProfession Profession #relation(fields: [PrimaryProfessionId], references: [id])
SecondaryProfession Profession #relation(fields: [SecondaryProfessionId], references: [id], name: "secondaryProfession")
}
model Profession {
id String #id #default(cuid())
name String
Primary Person?
Secondary Person? #relation("secondaryProfession")
}
Trying to copy this: Prisma - How to point two fields to same model? but it doesnt work.
With current code I am getting error: Error parsing attribute "#relation": A one-to-one relation must use unique fields on the defining side.
What should I fix to make this work?

How can I filter by a value inside a pivot table (using Prisma)?

I'm building a bike rental app where users can reserve bikes for a certain number of days. Bikes and users have a many-to-many relationship, there's a bike_user pivot table that contains the information about reservation start and end dates. See the diagram:
My schema looks kinda like this:
model User {
id String #id #default(cuid())
username String #unique
email String #unique
rented_bikes BikeUser[]
}
model Bike {
id String #id #default(cuid())
model String
color String
rented_by_users BikeUser[]
}
model BikeUser {
user User #relation(fields: [userId], references: [id])
userId String
bike Bike #relation(fields: [bikeId], references: [id])
bikeId String
reservation_start_date DateTime
reservation_end_date String
##id([userId, bikeId])
}
My goal is to take a range of dates, and filter the bikes, showing only the ones that haven't been reserved between these two dates.
Can you please help me figure out how to do this?

What is the best way to model a one-to-many relationship that also has a "selected" object in Prisma?

In our app, a user can be a member of many organizations. While viewing the site, they are generally viewing it through one of those organizations. This is the selected organization.
Is the following model the best way to model that? Is there a way to model it so you don't have to have the selectedBy and selectedById on the Membership and just have the selectedMembershipId on the User with a foreign key to the Membership?
model User {
id Int #id #default(autoincrement())
memberships Membership[] #relation("Memberships")
selectedMembership Membership? #relation("Selection")
}
model Membership {
id Int #id #default(autoincrement())
user User #relation("Memberships", fields: [userId], references: [id])
userId Int
selectedBy User? #relation("Selection", fields: [selectedById], references: [id])
selectedById Int? #unique
organization Organization #relation(fields: [orgId], references: [id])
orgId Int
role MemberRole
##unique([userId, orgId])
}
It's not possible to do precisely what you're hoping to. The syntax for relations in prisma calls for having a relation field (To the best of my understanding, that's what you would prefer not to have in your schema).
Since it's a one-to-one relation, if you prefer, you could do it the other way around, and have a selectedMembershipId foreign key on the User side and only have the selectedBy relation field on the Membership side like this:
model User {
id Int #id #default(autoincrement())
memberships Membership[] #relation("Memberships")
selectedMembership Membership? #relation("Selection", fields: [selectedMembershipId], references: [id])
selectedMembershipId Int? #unique
}
model Membership {
id Int #id #default(autoincrement())
user User #relation("Memberships", fields: [userId], references: [id])
userId Int
selectedBy User? #relation("Selection")
...
}
However, this is really a matter of preference regarding which side to keep the foreign key. In my opinion, the way you're handling your schema is the most reasonable way to model the relationship in Prisma.

One-way One to Many relationship using part of a composite key

I have a model in EF similar to this
Person
PK Guid Id
PK DateTime DateSynced
Test
PK Guid Id
FK Guid PersonId
In Entity Framework 6.2, I really only care about Navigation Property on Person with reference to a collection of Tests. I do not need a property of Test.Persons or anything like that.
I really just want to have Person.Tests where Test.PersonId = Id regardless of DateSynced. There will eventually be many persons with the same Id, each with a different DateSynced DateTime.
Is this doable or do I need a Many-To-Many with an intermediate table?
I understand that EFCore has a concept of Alternate Keys and I thought I might could leverage that in this effort, but there does not seem to be a corresponding functionality in EF 6.2
Edit
I have the following Fluent rule in my OnModelCreating override.
modelBuilder.Entity<Test>()
.HasRequired(t => t.Person)
.WithMany(p => p.Tests)
.HasForeignKey(t => t.PersonId);
I get the following exception complaining about the Dependent and Principal role constraints:
The number of properties in the Dependent and Principal Roles in a relationship constraint must be identical.
As I understand it, this is because I am referencing an entity with a 2-part composite key by only one part of that key. What I am looking for is a way to do exactly that.
The foreign key on the second table must match the PK on the first.
For example, using Int for the key type for simplicity.
If I have a Person with a PK of ID: 1, SyncDate: 2019-05-22
Then I add a second variant: PK of ID: 1, SyncDate: 2019-05-23
If I go to add a "Test" record, which Person record would it reference with a FK Person ID of 1? It would reference both records, hence EF cannot support a reference of "HasRequired" pointing to a single Person record.
To reference one variant of Person ID 1, your Test record will need both a PersonId and a SyncDate to identify the record:
public class Test
{
public Guid Id { get; set; }
public Guid PersonId { get; set; }
public DateTime SyncDate { get; set; }
public virtual Person Person { get; set; }
}
modelBuilder.Entity<Test>()
.HasRequired(t => t.Person)
.WithMany(p => p.Tests)
.HasForeignKey(t => new { t.PersonId, t.SyncDate });
Tables in the database cannot reference each other based on a partial FK unless a many to 1. I.e.
Person
------
PK: PersonID
PersonComment
-----------
PK: PersonId
PK: CommentDate
In this example a Person can have many PersonComments based on the PersonID link, while a Comment can resolve back to the Person via the Person ID. As a FK.
In a table structure that has:
Person
------
PK: PersonID
PK: Version
PersonComment
-------------
PK: PersonCommentID
PersonId
PersonId in the PersonComment cannot be a FK to Person because it doesn't reflect the PK to the Person table. You can legally have this table structure, but PersonId is just a dumb, unconstrained column. You can query all Person records manually using it, but you will get all Versions of the person. There are no constraints etc. to ensure that the Person ID on a comment matches an Id on the Person table.
If you don't care about the versions of a Person You can have a Test entity with a Person ID, but EF can't associate that to Person entities, you'll have to load Person records manually from the Context.
When it comes to the purpose behind your schema structure, I would suggest looking at possible alternatives. For instance, if your goal is to track versioned data I would suggest looking at something like:
Person
PK: PersonId
** Person Fields
PersonHistory
PK: PersonHistoryId
FK: PersonId
VersionDate
** Person Fields
Test
PK: TestId
FK: PersonId (if applies to current person, or PersonHistoryId if a specific version)
Then "Person" reflects the person in it's current state, containing a collection reflecting the History. From there you can prevent modifying the Person fields via private setters and DDD-style methods which would be responsible for composing a new History record based on the current Person data before updating the Person values. This way a Person record can be historical and preserve it's ID for related entities.