Why fields in same hierarchy get deleted when updating in Prisma? - mongodb

I am trying to implement updation using prisma.
here is my data structure.
{
"name":"toy",
"data":{
"sports":{
"currentState":"false",
"oldState":"true"
}
}
Here is my logic to update currentState from false to true.
const updatedSource = await prisma.sources.update({
where: {
name: 'toy'
},
data: {
data: {
update:{
sports: {
currentState: "true"
}
}
}
},
})
Here is the schema file:
type SourcesData {
sports SourcesDataState
}
type SourcesDataState {
currentState String
oldState String
}
model sources {
id String #id #default(auto()) #map("_id") #db.ObjectId
data SourcesData
name String #unique
}
The above logic updates the currentState field but deletes the oldState field from the database.
Please guide me on how to update currentState in a way that oldState data persists.

Related

how to query object in mongo using prisma

I want to query by using where in object with prisma and mongo
Here is my prisma schema
model Member {
id String #id #default(auto()) #map("_id") #db.ObjectId
bank BankInfo
##map("members")
}
type BankInfo {
bankNo String
}
const member = await this.prisma.member.findFirst({
where: { bank: { bankNo: 'test' } },
});
I got an error
Error:
Invalid prisma.member.findFirst() invocation:
You can try to use equal operator ($eq) like that :
const member = await this.db.member.find({
bank: { $eq: { bankNo: 'test' } }
})

Prisma: how to delete a record where it may or may not have children

I am trying to figure out how to delete a record created in my graphql, apollo, prisma app, where the record I am deleting may have a has many relationship to another model.
I have a model called IssueGroups. IssueGroups have many issues.
I have a model with:
import * as Prisma from "#prisma/client"
import { Field, ObjectType } from "type-graphql"
import { BaseModel } from "../shared/base.model"
#ObjectType()
export class IssueGroup extends BaseModel implements Prisma.IssueGroup {
#Field()
title: string
#Field()
description: string
#Field(type => String)
issues: Prisma.Issue[];
}
A resolver with:
import { Arg, Mutation, Query, Resolver } from "type-graphql"
import { IssueGroup } from "./issueGroup.model"
import { IssueGroupService } from "./issueGroup.service"
import { IssueGroupInput } from "./inputs/create.input"
import { Inject, Service } from "typedi"
#Service()
#Resolver(() => IssueGroup)
export default class IssueGroupResolver {
#Inject(() => IssueGroupService)
issueGroupService: IssueGroupService
#Query(() => [IssueGroup])
async allIssueGroups() {
return await this.issueGroupService.getAllIssueGroups()
}
#Query(() => IssueGroup)
async issueGroup(#Arg("id") id: string) {
return await this.issueGroupService.getIssueGroup(id)
}
#Mutation(() => IssueGroup)
async createIssueGroup(#Arg("data") data: IssueGroupInput) {
return await this.issueGroupService.createIssueGroup(data)
}
// : Promise<IssueGroup[]> {
#Mutation(() => IssueGroup)
async updateIssueGroup(
#Arg("id") id: string,
#Arg("data") data: IssueGroupInput
) {
return await this.issueGroupService.updateIssueGroup(id, data)
}
#Mutation(() => IssueGroup)
async deleteIssueGroup(#Arg("id") id: string) {
return await this.issueGroupService.deleteIssueGroup(id)
}
}
// private readonly issueGroupService: IssueGroupService
// #Query(() => [IssueGroup])
// async issueGroups(): Promise<IssueGroup[]> {
// return this.issueGroupService.findAll()
// }
// #Query(() => IssueGroup)
// async issueGroup(#Arg("id") id: string): Promise<IssueGroup> {
// return this.issueGroupService.findOne(id)
// }
// #Mutation(() => IssueGroup)
// async createIssueGroup(
// #Arg("data") data: IssueGroupInput
// ): Promise<IssueGroup> {
// return this.issueGroupService.create(data)
// }
A service with:
import { prisma } from "../../lib/prisma"
import { Service } from "typedi"
import { IssueGroupInput } from "./inputs/create.input"
import { Resolver } from "type-graphql"
import { IssueGroup } from "./issueGroup.model"
#Service()
#Resolver(() => IssueGroup)
export class IssueGroupService {
async createIssueGroup(data: IssueGroupInput) {
return await prisma.issueGroup.create({
data,
})
}
async deleteIssueGroup(id: string) {
return await prisma.issueGroup.delete({ where: { id } })
}
async updateIssueGroup(id: string, data: IssueGroupInput) {
const issueGroup = await prisma.issueGroup.findUnique({ where: { id } })
if (!issueGroup) {
throw new Error("Issue not found")
}
return await prisma.issueGroup.update({ where: { id }, data })
}
async getAllIssueGroups() {
return (await prisma.issueGroup.findMany({orderBy: {title: 'asc'}}))
}
async getIssueGroup(id: string) {
return await prisma.issueGroup.findUnique({
where: {
id,
},
})
}
}
When I try to delete the issueGroup (which does not currently have any issues, I can see an error that points to issue.delete and says:
operation failed because it depends on one or more records that were
required but not found. Record to delete does not exist.
In my form I have:
import * as React from "react"
import { gql } from "#apollo/client"
import type { IssueGroupInput } from "lib/graphql"
import { QueryMode, Role, SortOrder, useAllIssueGroupsQuery, useDeleteIssueGroupMutation } from "lib/graphql"
const __ = gql`
query AllIssueGroups {
allIssueGroups {
id
title
description
}
}
mutation deleteIssueGroup($id: String!) {
deleteIssueGroup(id: $id) {
id
title
description
issues // I have tried both with and without issues in this list
}
}
`
export default function IssueGroups() {
const [deleteIssueGroup] = useDeleteIssueGroupMutation()
const { data, loading, refetch } = useAllIssueGroupsQuery({
fetchPolicy: "cache-and-network",
})
const allIssueGroups = data?.allIssueGroups
const onDeleteIssueGroup = (id: string) => {
return (
deleteIssueGroup({ variables: { id } }).then(() => refetch())
)
}
return (
<List>
{data?.allIssueGroups.map((issueGroup) => (
<ListItem key={issueGroup.id}>
{issueGroup.title}{issueGroup.description}
<Button onClick={() => onDeleteIssueGroup(issueGroup.id)}>Delete</Button>
</ListItem>
))}
</List>
)
}
I have seen this page of the prisma documentation and I think I have followed its advice for how to deal with an issue when the parent issueGroup is deleted.
model IssueGroup {
id String #id #default(dbgenerated("gen_random_uuid()")) #db.Uuid
title String
description String
issues Issue[]
createdAt DateTime #default(now()) #db.Timestamptz(6)
updatedAt DateTime #default(now()) #updatedAt #db.Timestamptz(6)
}
model Issue {
id String #id #default(dbgenerated("gen_random_uuid()")) #db.Uuid
title String
description String
issueGroup IssueGroup #relation(fields: [issueGroupId], references: [id], onDelete: SetNull, onUpdate: Cascade)
issueGroupId String #db.Uuid
subscribers UserIssue[]
createdAt DateTime #default(now()) #db.Timestamptz(6)
updatedAt DateTime #default(now()) #updatedAt #db.Timestamptz(6)
}
However, VS code thinks this is an error. It gives me an error message that says:
The onDelete referential action of a relation should not be set to
SetNull when a referenced field is required. We recommend either to
choose another referential action, or to make the referenced fields
optional.
My db is psql, which the prisma docs suggest, should allow SetNull
PostgreSQL PostgreSQL is the only database supported by Prisma that
allows you to define a SetNull referential action that refers to a
non-nullable field. However, this raises a foreign key constraint
error when the action is triggered at runtime.
For this reason, when you set postgres as the database provider in the
(default) foreignKeys relation mode, Prisma warns users to mark as
optional any fields that are included in a #relation attribute with a
SetNull referential action. For all other database providers, Prisma
rejects the schema with a validation error.
I can see from this page of the docs that lists cannot be optional, so I dont think I can follow the advice in the relational page of the docs and make the issueGroup issues[] as optional.
How can i delete the issue (if there were any) at the same time as I delete the issue group?
Just make issueGroup optional
model IssueGroup {
...
issues Issue[]
...
}
model Issue {
...
issueGroup IssueGroup? #relation(fields: [issueGroupId], references: [id], onDelete: SetNull, onUpdate: Cascade)
issueGroupId String? #db.Uuid
...
}
The actual message from prisma is saying that
Prisma warns users to mark as optional any fields that are included in
a #relation attribute with a SetNull referential action
Your relationship has onDelete: SetNull - so mark it as optional

How to define models with nested objects in prisma?

I am trying to migrate from mongoose to Prisma. Here is the model I have defined in mongoose which contains nested objects.
const sourceSchema = new Schema(
{
data: {
national: {
oldState: {
type: Array
},
currentState: {
type: Array
}
},
sports: {
oldState: {
type: Array
},
currentState: {
type: Array
}
}
}
}
);
Please guide me on how can I write the model in Prisma for the mongoose schema with nested objects.
You are looking for composite types to store nested objects in Prisma.
Here's how you can define the models:
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mongodb"
url = env("DATABASE_URL")
}
model Source {
id String #id #default(auto()) #map("_id") #db.ObjectId
national stateType
sports stateType
}
type stateType {
oldState oldState
currentState currentState
}
type oldState {
type String[]
}
type currentState {
type String[]
}

Referenced objects in DB returning null in GraphQL Query

I have the following database model:
Each Mediablock contains a reference to exactly one UTS object and one Media object.
Each UTS object contains rawText and normalisedText
Each Media object contains a url and a timestamp
My schema.prisma looks like this:
datasource db {
provider = "mongodb"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model Mediablock {
id String #id #default(auto()) #map("_id") #db.ObjectId // gen a new, unique id
UTS UTS #relation(fields: [utsId], references: [id])
utsId String #unique #map("uts_id") #db.ObjectId
Media Media #relation(fields: [mediaId], references: [id])
mediaId String #unique #map("media_id") #db.ObjectId
}
model UTS {
id String #id #default(auto()) #map("_id") #db.ObjectId
rawText String
normalisedText String
createdAt DateTime #default(now())
Mediablock Mediablock?
}
// // Mediablocks contain a Video object and connect back to the Mediablock.
// // mediablockId must have #db.ObjectId to match up with Mediablock's id type
model Media {
id String #id #default(auto()) #map("_id") #db.ObjectId
url String
createdAt DateTime #default(now())
Mediablock Mediablock?
}
My resolvers look like this:
const { PrismaClient } = require('#prisma/client');
const prisma = new PrismaClient();
//This resolver retrieves mediabooks from the "mediabooks" array above.
module.exports = {
Query: {
allMediablocks: () => prisma.mediablock.findMany(),
allMedia: () => prisma.media.findMany(),
allUTS: () => prisma.uts.findMany(),
},
};
And my typedefs looks like this:
module.exports = `
type Mediablock {
id: ID!
uts: UTS
media: Media # can be null when the text is generated first
}
type UTS {
id: ID!
rawText: String!
normalisedText: String!
}
type Media {
id: ID!
url: String!
createdAt: String!
}
# The "Query" type is special: it lists all of the available queries that
# clients can execute, along with the return type for each. In this
# case, the "allMediablocks" query returns an array of zero or more Mediablocks (defined above).
type Query {
allMediablocks: [Mediablock]
allMedia: [Media]
allUTS: [UTS]
}
`;
My seed file looks like this:
const { PrismaClient } = require('#prisma/client');
const prisma = new PrismaClient();
const mediaData = [
{
UTS: {
create: {
rawText: 'Welcome',
normalisedText: 'welcome',
},
},
Media: {
create: {
url: 'https://www.youtube.com/watch?v=kq9aShH2Kg4',
createdAt: '2022-09-29T12:00:00.000Z',
}
}
}
];
async function main() {
console.log(`Started seeding ...`);
for (const d of mediaData) {
const mediablock = await prisma.Mediablock.create({
data: d,
});
console.log(`Created Mediablock with id: ${mediablock.id}`);
}
console.log(`\nSeeding complete.`);
}
main()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (e) => {
console.error(e);
await prisma.$disconnect();
process.exit(1);
});
My problem is that when I attempt to query allMediablocks, I can't get any of the UTS or Media data.
query allMediaBlocks {
allMediablocks {
uts {
normalisedText
}
media {
url
}
}
}
// response
{
"data": {
"allMediablocks": [
{
"uts": null,
"media": null
}
]
}
}
I just get null values for both, when in fact, the database (MongoDB) contains references to both of these objects in other tables.
What am I doing wrong? Are my resolvers incorrect?
Is my schema structured incorrectly for MongoDB?
I've fixed it by changing my schema.prisma to be:
datasource db {
provider = "mongodb"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model Mediablock {
id String #id #default(auto()) #map("_id") #db.ObjectId // gen a new, unique id
utsId String #unique #map("uts_id") #db.ObjectId
uts UTS #relation(fields: [utsId], references: [id])
mediaId String #unique #map("media_id") #db.ObjectId
media Media #relation(fields: [mediaId], references: [id])
}
model UTS {
id String #id #default(auto()) #map("_id") #db.ObjectId
rawText String
normalisedText String
createdAt DateTime #default(now())
mediablock Mediablock?
}
// // Mediablocks contain a Video object and connect back to the Mediablock.
// // mediablockId must have #db.ObjectId to match up with Mediablock's id type
model Media {
id String #id #default(auto()) #map("_id") #db.ObjectId
url String
createdAt DateTime #default(now())
mediablock Mediablock?
}
My resolver to be:
const { PrismaClient } = require('#prisma/client');
const prisma = new PrismaClient();
//This resolver retrieves mediabooks from the "mediabooks" array above.
module.exports = {
Query: {
allMediablocks: () => prisma.mediablock.findMany({
include: {
media: true,
uts: true
}
}),
allMedia: () => prisma.media.findMany(),
allUTS: () => prisma.uts.findMany(),
},
};
And my typedefs to be:
module.exports = `
type Mediablock {
id: ID!
uts: UTS
media: Media # can be null when the text is generated first
}
type UTS {
id: ID!
rawText: String!
normalisedText: String!
}
type Media {
id: ID!
url: String!
createdAt: String!
}
# The "Query" type is special: it lists all of the available queries that
# clients can execute, along with the return type for each. In this
# case, the "allMediablocks" query returns an array of zero or more Mediablocks (defined above).
type Query {
allMediablocks: [Mediablock]
allMedia: [Media]
allUTS: [UTS]
}
`;
I also changed the way my seed function works:
const { PrismaClient } = require('#prisma/client');
const prisma = new PrismaClient();
const mediaData = [
{
uts: {
create: {
rawText: 'Welcome',
normalisedText: 'welcome',
createdAt: '2022-09-29T12:00:00.000Z',
},
},
media: {
create: {
url: 'https://www.youtube.com/watch?v=kq9aShH2Kg4',
createdAt: '2022-09-29T12:00:00.000Z',
}
}
}
];
async function main() {
console.log(`Started seeding ...`);
for (const d of mediaData) {
const mediablock = await prisma.Mediablock.create({
data: d,
});
console.log(mediablock);
}
console.log(`\nSeeding complete.`);
}
main()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (e) => {
console.error(e);
await prisma.$disconnect();
process.exit(1);
});
Now, using the same GraphQL query:
query allMediaBlocks {
allMediablocks {
uts {
normalisedText
}
media {
url
}
}
}
I get the following (desired) response:
{
"data": {
"allMediablocks": [
{
"uts": {
"normalisedText": "welcome"
},
"media": {
"url": "https://www.youtube.com/watch?v=kq9aShH2Kg4"
}
}
]
}
}

Unknown argument error when creating record

I'm encountering some interesting behaviour when using Prisma ORM. It is related to Prisma's generated types, and I've been skimming the docs trying to find out more, but there doesn't seem to be much info about generated types in there (please correct me if I'm mistaken). Here's the behaviour:
Say I have a model with two 1-1 relations (Profile in the example below):
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id Int #id #default(autoincrement())
name String
profile Profile?
}
model Profile {
id Int #id #default(autoincrement())
name String
userId Int?
user User? #relation(fields: [userId], references: [id])
photoId Int?
photo Photo? #relation(fields: [photoId], references: [id])
}
model Photo {
id Int #id #default(autoincrement())
url String
profile Profile?
}
The following code works when creating a new profile:
const user = await prisma.user.create({ data: { name: "TestUser" } });
const profile = await prisma.profile.create({
data: {
name: "TestProfile",
user: { connect: { id: user.id } },
photo: { create: { url: "http://example.com/img" } },
},
});
... but this fails with an error:
const user = await prisma.user.create({ data: { name: "TestUser" } });
const profile = await prisma.profile.create({
data: {
name: "TestProfile",
userId: user.id,
photo: { create: { url: "http://example.com/img" } },
},
});
The error is:
Unknown arg userId in data.userId for type ProfileCreateInput. Did you mean user? Available args:
type ProfileCreateInput {
  name: String
  user?: UserCreateNestedOneWithoutProfileInput
  photo?: PhotoCreateNestedOneWithoutProfileInput
}
Why is the second create-profile code invalid?
Prisma essentially generates two type definitions for a create query. This is implemented with a XOR type, which ensures that only one definition out of two is fully specified and passed to the query:
export type ProfileCreateArgs = {
/* ... */
data: XOR<ProfileCreateInput, ProfileUncheckedCreateInput>;
}
The definitions are called checked and unchecked, the former using nested fields and the latter using raw ids:
export type ProfileCreateInput = {
id?: number;
/* ... */
user?: UserCreateNestedOneWithoutProfileInput;
photo?: PhotoCreateNestedOneWithoutProfileInput;
}
export type ProfileUncheckedCreateInput = {
id?: number;
/* ... */
userId?: number;
photoId?: number;
}
Which basically means that you either provide all references as connect, create etc. relations (checked) or as raw ids (unchecked). You cannot mix the styles, this is not supported.