Can't disconnect relation of explicit many-to-many - node.js

I'm trying to delete some users which are related to a group.
Here is the schema:
model User {
id String #id #default(cuid())
username String
email String #unique
password String?
group GroupUser[]
}
model Group {
id String #id #default(cuid())
name String
user GroupUser[]
}
model GroupUser{
userId String
user User #relation(fields: [userId],references: [id],onDelete: Cascade,onUpdate:Cascade)
groupId String
group Group #relation(fields: [groupId],references: [id],onDelete: Cascade,onUpdate: Cascade)
##id([userId,groupId])
}
The code to delete the users:
async deleteUsersFromGroup(id: string, userData: UpdateGroupDto): Promise<number> {
const deletedUsers = await prisma.group.update({
where: {
id: id,
},
data: {
user: { disconnect: /* trying to put the array of users id here */ },
},
});
return deletedUsers.length;
}
The problem is that I want to give the userID inside of the disconnect but it is asking me for userId_groupId which is the relational key.

You would need to delete the record from the connecting table i.e. GroupUser.
You can try something like this:
await prisma.groupuser.delete({
where: {
userId_groupId: {
userId: 'userId',
groupId:'groupId'
}
}
});
If the connecting records are deleted then both the entities would be disconnected.

Since I wanted to delete multiple users at the same time I used the map function inside userId, resorting to prisma.groupUser.delete().
async deleteUsersFromGroup(id: string, userData: DeleteGroupDto): Promise<any> {
const response = await prisma.groupUser.deleteMany({
where: {
groupId: id,
userId: { in: userData.users.map(user => user.userId) },
},
});
return response.count
}

Related

Prisma and MongoDB: Many-to-many nested create only populates one side of the relation

I have a schema set up where Users can create many Photos, and then attach those photos to a single Post. Other users can repost others' photos, so I need a many to many relation between Photo and Post:
model User {
id String #id #default(auto()) #map("_id") #db.ObjectId
photos Photo[]
posts Post[]
...
}
model Photo {
id String #id #default(auto()) #map("_id") #db.ObjectId
user User #relation(fields: [userId], references: [id])
userId String #db.ObjectId
posts Post[] #relation(fields: [postIds], references: [id])
postIds String[] #db.ObjectId
...
}
model Post {
id String #id #default(auto()) #map("_id") #db.ObjectId
poster User #relation(fields: [posterId], references: [id])
posterId String #db.ObjectId
photos Photo[] #relation(fields: [photoIds], references: [id])
photoIds String[] #db.ObjectId
...
}
I am able to create photos and posts just fine, but the problem is when I try to create a new post that connects the existing photos. This is my query:
return await prisma.user.update({
where: {id},
data: {
posts: {
create: {
photoIds,
},
}
}
})
This works for the Posts side of the relation, the Post's photoIds are populated correctly, however the Photo's postIds are left empty. I am not sure how to connect the other side of the relation.
The bizarre thing is that when I do a Post count query on the Photo, it returns the correct count number:
await prisma.user.findUnique({where: { id }}).photos({
where: { id: photoId },
select: {
...
_count: {
select: {
posts: true,
}
},
}
})
This query returns the correct post count for each photo, even though it's postIds list is empty. There must be something simple I am missing here, I have read the Prisma docs on many-to-many relations for MongoDB over and over again but can't seem to figure it out.
The secret sauce here is the "include" fields when you create or update.
Instead of updating the user type, just create the photos with the correct userId and the "include" field and it'll automatically link them, such that when you query for users, it'll have attached photos:
Instead of:
return await prisma.user.update({
where: {id},
data: {
posts: {
create: {
photoIds,
},
}
}
});
Use:
return await prisma.photo.create({
data: {
...your photo data
userId: '123'
},
include: {
user: true
}
})

Update parent and upsert child in Prisma

I have a parent-child relation like below:
model User {
id Int #id #default(autoincrement())
authId String #unique #default("N/A") #map("auth_id") #db.VarChar(128)
email String #unique #db.VarChar(256)
firstName String #map("first_name") #db.VarChar(64)
lastName String #map("last_name") #db.VarChar(64)
profile Profile?
}
model Gender {
id Int #id #default(autoincrement())
text String
Profile Profile[]
}
model MedicalCondition {
id Int #id #default(autoincrement())
text String
Profiles Profile[]
}
model Profile {
id Int #id #default(autoincrement())
dob String #db.VarChar(32)
weightLbs Float #map("weight_lbs")
heightIn Int #map("height_in") #db.SmallInt
user User #relation(fields: [userId], references: [id], onDelete: Cascade)
userId Int #unique #map("user_id")
gender Gender #relation(fields: [genderId], references: [id])
genderId Int #map("gender_id")
lifeStyle LifeStyle? #relation(fields: [lifeStyleId], #map("life_style_id")
medicalConditions MedicalCondition[]
}
I'm quite new to node.js and Prisma and I'm wondering is it possible to update User and upsert Profile together like below?
const updateWithProfile = (where: IUserUniqueInput, data: IUserUpdate, medicalConditions?: IMedicalConditionWhereUniqueInput[]): Promise<IUser> => {
return user.update({
where,
data:{
...data,
profile: {
upsert:{
????
},
}
}
});
};
Here is my working solution for upsert one-to-one and many-to-many relations:
const update = async (
data: IProfileUpdate,
medicalConditions?: IMedicalConditionWhereUniqueInput[]
): Promise<IProfile> => {
return profile.upsert({
where: {
userId: data.userId,
},
update: {
...data,
// set removes previous values in mapping table and connect inserts new IDs
medicalConditions: { set: [], connect: medicalConditions }
},
create: {
...data,
medicalConditions: { connect: medicalConditions }
}
});
};

Update document in MongoDB via NodeJS

So my knowledge of NodeJS and MongoDD are non-existent (just need to do a small code update for a friend) and I'm stuck.
Need to update a single document inside a collection via a unique id but can't seem to do it.
Here's the Model (I've trimmed it down and cut out all unnecessary data). I'm trying to update the field notes inside a transaction.
In short each entry in the given (an Agent) table will have a collection of multiple Transactions & Documents. I need to update a specific Transaction with the unique _id that is auto generated.
import { Schema, model } from 'mongoose';
interface Transaction {
first_name: string;
last_name: string;
type: string;
notes: string;
}
interface Agent {
org_id: number;
transactions: Array<Transaction>;
documents: Array<string>;
}
const transactionSchema = new Schema<Transaction>({
first_name: { type: String },
last_name: { type: String },
type: { type: String },
notes: String,
});
const transactionsSchema = new Schema<Agent>({
org_id: { type: Number },
transactions: [transactionSchema],
documents: [documentTypesSchema],
});
const AgentTransaction = model<Agent>(
'agent_transaction_table',
transactionsSchema
);
export default AgentTransaction;
Here's what I tried but didn't work (obviously), again I've trimmed out all unnecessary data. Just to clarify, the endpoint itself works, but the DB update does not.
import AgentTransaction from '../models/transaction'; // the above model
transaction.put('/notes', async (req, res) => {
const { org_id, transaction_id, notes } = req.body;
try {
const notesResult = await AgentTransaction.updateOne({
'transactions._id': transaction_id,
}, {
$set: {
'notes': notes
},
});
res
.status(200)
.json({ message: 'Updated', success: true, notesResult });
} catch (error) {
res.status(400).send(error);
}
});
So I figured it out. Maybe it'll help someone else as well.
const notesResult = await AgentTransaction.updateOne({
'transactions._id': { $in: [trunc2] },
}, {
$set: {
'transactions.$.notes': notes
},
});
The main issue was that the payload object needed to target the collection folder + the wildcard + the field, not just only the field.

Prisma upsertmany issue Provided List<Json>, expected SimilarCreateWithoutMovieInput

using "#prisma/client": "^2.25.0"
on prisma.upsertOneMovie. Provided List<Json>, expected SimilarCreateWithoutMovieInput or SimilarCreateWithoutMovieInput or SimilarUncheckedCreateWithoutMovieInput or SimilarUncheckedCreateWithoutMovieInput:
[0] type SimilarCreateWithoutMovieInput {
[0] id: Int
[0] backdrop_path: String
[0] title: String
[0] name: String
[0] release_date: DateTime
[0] overview: String
[0] show: TVShowCreateNestedOneWithoutSimilarInput
[0] }
code
const { PrismaClient } = require('#prisma/client')
const prisma = new PrismaClient()
...
let result = await apiRequest
let similar = result.similar.results.map(similar_movie => {
return {
id: similar_movie.id,
backdrop_path: similar_movie.backdrop_path,
title: similar_movie.title,
name: similar_movie.name,
release_date: similar_movie.release_date,
overview: similar_movie.overview,
movieId: result.id
}
})
const movie = {
id: result.id,
...
similar: {
upsert: similar
}
}
const upsertMovie = await prisma.movie.upsert({
where: { id: movie.id },
update: movie,
create: movie,
})
here is the schema.prisma
model Movie {
id Int #id
...other fields and attributes...
similar Similar[]
}
model Similar {
id Int #id #default(autoincrement())
tmdb_id Int #unique
backdrop_path String
title String?
name String?
release_date String
overview String
movie Movie? #relation(fields: [movie_id], references: [id])
show TVShow? #relation(fields: [show_id], references: [id])
movie_id Int?
show_id Int?
}
Similar is supposed to be an array [] of other Movies nested in a specific Movie object, cant be itself
I do not have experience with prisma upsert on generated types and I am getting the above error. Im expecting to upsert a Movie and at the same time upsert multiple records of Similar that are related to Movie.
i have tried using connectOrCreate but it does not support creating multiple records of similar while creating one record of movie as expected
how do I achieve that?
resources
https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries#nested-writes
https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries#connect-or-create-a-record
Issue was that prisma createMany is not available on sqlite, and on the postgresql connector, it is.
const movie = {
...result,
similar: {
connectOrCreate: result.similar.map(s => ({
create: s,
where: { id: s.id },
}))
}
}
const upsertMovie = await prisma.movie.upsert({
where: { id: movie.id },
update: movie,
create: movie,
})

Field name different from column name in db

I just started using graphql with mysql, i would like to know if it is possible to use a name in the graphql query different from the column name in my data base.
For example i have a table users with the columns userName and password, when i define the type for the schema i have the following:
const unidadesMedidaInternaType = new GraphQLObjectType({
name: 'unidadesMedidaInterna',
fields: () => ({
userName: { type: GraphQLID },
password: { type:GraphQLString }
})
});
the resolver:
resolve (parent, args) {
return pool.query(`SELECT * FROM users`);
}
so i have to query like this:
{
users {
userName,
password
}
}
i would like to have different names in the query like this:
{
users {
Name,
secret
}
}
i tried changing the names of the fields in the type definition but the result of the query is full of nulls values.
In order to have different names in the queries you have 2 options:
Option 1: Use aliases to run the query:
You can run your query with aliases like
{
users {
Name: userName,
secret: password
}
}
In this case you are just renaming the fields name on execution time, so the original names will still be available to query.
Option 2: Map the query result to the GraphQLObject type.
First rename the fields:
const unidadesMedidaInternaType = new GraphQLObjectType({
name: 'unidadesMedidaInterna',
fields: () => ({
Name: { type: GraphQLID },
secret: { type:GraphQLString }
})
});
Then map the result of the query to match the fields:
resolve (parent, args) {
const result = pool.query(`SELECT * FROM users`);
// If the result of the query is an array then you have to map its items
return { Name: result.userName, secret: result.password }
}

Resources