I'm using Prisma 3.7.0 and Postgresql 13. I need to create a many-to-many relation between two models that has some additional data, but I don't know how to do this in a Prisma schema. The fields I want to add are not appropriately added to either table.
Here's some simplified (contrived) SQL to illustrate the goal:
It's pretty straight-forward in SQL, but I don't quite understand from the Prisma docs how to do it.
CREATE TABLE animal (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL -- e.g. "tiger" or "armadillo"
)
CREATE TABLE animal_bodyparts (
animal INT,
bodypart INT,
-- I want to add these two fields to the relation.
count INT, -- How many of this bodypart the animal has
is_vital BOOLEAN, -- Does the creature die if this part is damaged?
PRIMARY KEY (animal, bodypart)
CONSTRAINT fk_animal FOREIGN KEY (animal) REFERENCES animal(id),
CONSTRAINT fk_bodypart FOREIGN KEY (bodypart) REFERENCES bodypart(id)
)
CREATE TABLE bodypart (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL -- e.g. "head" or "leg"
)
And here's the best I could figure out with Prisma so far:
model Animal {
id Int #id #default(autoincrement)
name String // e.g. "tiger" or "armadillo"
parts BodyPart[]
}
model BodyPart {
id Int #id #default(autoincrement)
name String // e.g. "head" or "leg"
animals Animal[]
}
But where do I put the count and is_vital columns in the Prisma models? I see in a one-to-many relation there's some way to add some info through the #relation tag, but I don't think it addresses this need.
You can do it as follow:
datasource mysql {
provider = "mysql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
generator erd {
provider = "prisma-erd-generator"
output = "entity-relationship-diagram.svg"
}
model Animal {
id Int #id #default(autoincrement())
name String
animalAndBodyParts AnimalAndBodyParts[] #relation()
}
model AnimalAndBodyParts {
id Int #id #default(autoincrement())
count Int
isVital Boolean #map("is_vital")
animal Animal #relation(fields: [animalId], references: [id])
animalId Int
bodyPart BodyPart #relation(fields: [bodyPartId], references: [id])
bodyPartId Int
}
model BodyPart {
id Int #id #default(autoincrement())
name String
animalAndBodyParts AnimalAndBodyParts[] #relation()
}
It will generate a database as show the image:
You can save data in this way:
const { PrismaClient } = require('#prisma/client')
const prisma = new PrismaClient()
const saveData = async () => {
const animal = await prisma.animal.create({
data: {
name: 'Lion',
animalAndBodyParts: {
create: {
count: 2,
isVital: true,
bodyPart: {
create: {
name: 'Eye',
},
},
},
},
},
include: {
animalAndBodyParts: {
include: {
bodyPart: true,
},
},
},
})
console.log(JSON.stringify(animal, null, 2));
}
saveData()
And the data will look like in the image down below
Related
I have a schema below based on which I need to find defaultSubject with respect to SchoolCode
I have tried multiple permutations and combinations but I couldn't find out the relation between SchoolCode and defaultSubject
Schemas
type Query {
school(id: ID, schoolCode: String): School
}
type School {
optionalSubjects: [String!]
code: ID!
defaultSubjects: String!
defaultExams: String!
}
enum SchoolCode {
kvr
ttn
rgs
dps
ris
}
I tried:
query ($schId:ID, $schCode: String){
school(id:$schId,schoolCode:$schCode){
optionalSubjects, defaultSubjects, defaultExams, code, id, name
}
}
{
"schCode":"dps",
"schId":"23"
}
query myquery {
schools {
nodes {
name, id, defaultSubjects
}
}
}
How can I write a query that would help me find a relation between defaultSubjects and SchooCode?
I have a waitlist where users signup with their email. Then they can use their invite code and invite more users.
I am sorting users by the number of users they have invited. I am sorting like this:
const x = await prisma.user.findMany({
select: {
id: true,
email: true,
},
orderBy: {
createdUsers: {
_count: "desc",
},
},});
This is the schema:
model User {
id Int #id #default(autoincrement())
createdAt DateTime #default(now())
email String #unique
inviteCode String #unique #default(cuid())
createdById Int?
createdBy User? #relation("UserCreatedBy", fields: [createdById], references: [id], onDelete: NoAction, onUpdate: NoAction)
createdUsers User[] #relation("UserCreatedBy")
}
What i want to do is basically tell them the position of the user in the waitlist based on the number of users they have invited.
Any help will be appreciated?
I've got two models, Dictionary and DictionaryRecord with a foreign key on Dictionary.
type DictionaryRecord struct {
Id string `gorm:"primaryKey"`
Name string
DictionaryId string
}
type Dictionary struct {
Id string `gorm:"primaryKey"`
Name string
Records []DictionaryRecord
}
I'd like to make sure that when creating a dictionary with a nested record changed, to have the change reflected during the upsert.
package main
import (
// Sqlite driver based on GGO
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
func main() {
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{})
if err != nil {
panic(err)
}
db.AutoMigrate(
&DictionaryRecord{},
&Dictionary{},
)
db.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
DoUpdates: clause.AssignmentColumns([]string{"name"}),
}).Create(&Dictionary{
Name: "AAAAA",
Id: "e831ab86-db60-4a71-ba63-f20a181cd69b",
Records: []DictionaryRecord{
{
Id: "66f73e9b-61b8-4bc9-941d-80d7fd80f8f4",
Name: "will be updated",
},
},
})
}
How do you specify that the column name in the DictionaryRecord must be updated?
can you try use pointer
type DictionaryRecord struct {
Id string `gorm:"primaryKey"`
Name string
DictionaryId string
}
type Dictionary struct {
Id string `gorm:"primaryKey"`
Name string
Records []*DictionaryRecord //on this records
}
Saving a single model in sqflite is quite easy. I am trying to save the nested Model in sqfilte. Model class, table creation query, and error is explained below. Any help will be appreciated to save such nested models:
Main Model:
#JsonSerializable(explicitToJson: true)
class Album {
String? name;
int? playcount;
String? url;
ArtistDetail? artist;
List<Image>? image;
Album({this.name, this.playcount, this.url, this.artist, this.image});
factory Album.fromJson(Map<String, dynamic> json) =>
_$AlbumFromJson(json);
Map<String, dynamic> toJson() => _$AlbumToJson(this);
}
Sub-model:
#JsonSerializable()
class Image {
dynamic _text;
String? _size;
dynamic get text => _text;
String? get size => _size;
Image({dynamic text, String? size}) {
_text = text;
_size = size;
}
factory Image.fromJson(Map<String, dynamic> json) => _$ImageFromJson(json);
Map<String, dynamic> toJson() => _$ImageToJson(this);
}
Function to store image into sqflite:
// Table creation query
//'CREATE TABLE $TABLE ( $ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, $ALBUM_NAME //TEXT, $PLAY_COUNT INTEGER, $ALBUM_URL TEXT, $ARTIST_DETAIL TEXT, $IMAGES TEXT )'
//Function to perform insertion:
Future<int> insert(Album album) async {
var dbClient = await db;
return await dbClient.insert(
TABLE,
album.toJson(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
Error:
Invalid argument [{#text: https:5d72a77281bec2f7ddea87c48.png, size: small}] with type List<Map<String, dynamic>>. The only num, String, and Uint8List are supported.
Indeed nested List or Map are not supported in SQLite, Only simple types are supported (String, num and Uint8List) at the "root" level. You have to "flatten" your model somehow.
For inner objects, You could decide for example to save the Artist details as a JSON text or put each artist field in the Album object.
For list, you could as well encode the list as JSON or using a proprietary format.
You can find here an example of solutions for a nested object.
For example, the following simple map is not supported:
{
"title": "Table",
"size": {"width": 80, "height": 80}
}
It should be flattened. One solution is to modify the map structure:
CREATE TABLE Product (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT,
width INTEGER,
height INTEGER)
{"title": "Table", "width": 80, "height": 80}
Another solution is to encoded nested maps and lists as json (or other format), declaring the column
as a String.
CREATE TABLE Product (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT,
size TEXT
)
{
'title': 'Table',
'size': '{"width":80,"height":80}'
};
So I am a building a member system and every member has an array points.
But I can't seem to delete an object when a person tries to remove this point.
users(collection) -
member(document) -
"id" . --fields
"username" . --fields
"userevents" . --array
[0]
"id"
"date"
"price"
[1]
"id"
"date"
"price"
deletePoint = (userId, pointId, date, price) => {
let docRef = this.db.collection('users').doc(userId);
docRef.update({
points: this.db.FieldValue.arrayRemove({
date: date,
id: pointId,
price: price,
}),
});
};
this.db is firebase.firestore()
According to the documentation when you're using arrayRemove you have to point directly to the field that you want to remove, not the content within said field so your example would become:
deletePoint = (userId, pointId, date, price) => {
let docRef = this.db.collection('users').doc(userId);
docRef.update({
points: this.db.FieldValue.arrayRemove("0"),
});
};
And this will delete the whole content of the field (id, date, price) with an index of 0.