PRISMA - 'where' with deeply nested relation - prisma

I'm trying to use 'where' on the deeply nested relation (we can paraphrase 'on relation from relation').
Unfortunately as for 2022-06 in Prisma documentation there is only info about using 'include' with deeply nested relations, nothing about deeply nested relations with 'where'.
Simplified types in Schema.graphql:
type Entity {
id: ID!
company: Company
}
type Company {
id: ID!
entity: Entity!
status: CompanyStatus!
}
type CompanyStatus {
id: ID!
companies: [Company]
}
I want to query Entities and get only these ones, that have companies, that have exact status (let's say with status that has an ID: 1).
I've tried in multiple ways:
1)
function entities(parent, args, context, info) {
const where = {
company: { status: {id: 1} }
}
return context.prisma.entities.findMany({where});
}
Returns:
Unknown arg `status` in where.company.status for type CompanyRelationFilter. Did you mean `is`? Available args:\ntype CompanyRelationFilter {\n is?: CompanyWhereInput | Null\n isNot?: CompanyWhereInput | Null\n
It says to try with 'is', so here we have a try with 'is':
2)
function entities(parent, args, context, info) {
const where = {
company: { is: { status: {id: 1} } }
}
return context.prisma.entities.findMany({where});
}
Returns:
Unknown arg `is` in where.company.is for type CompanyWhereInput. Did you mean `id`? Available args:\ntype CompanyWhereInput {\n AND?: CompanyWhereInput | List<CompanyWhereInput>\n OR?: List<CompanyWhereInput>\n NOT?: CompanyWhereInput | List<CompanyWhereInput>\n id?: IntFilter | String\n entity_id?: IntFilter | Int\n entity?: EntityRelationFilter | EntityWhereInput\n status?: CompanyStatusRelationFilter | CompanyStatusWhereInput\n status_id?: IntFilter | Int
So the advice about using 'is' doesn't seem to work. It advises to use 'status' or 'status_id', which doesn't work (as in the first example).
Anyone knows whether use 'where' with deeply nested relations is possible with Prisma? If yes, how to perform it?

A friend on mine helped me to find a solution.
You need to use double 'is':
function entities(parent, args, context, info) {
const where = {
company: { is: { status: { is {id: 1} } } }
}
return context.prisma.entities.findMany({where});
}

Related

How to update composite type model in Prisma?

I am trying to implement updation in a composite-type model in Prisma.
Here is my data structure:
{
"name":"toy",
"data":{
"sports":{
"currentState":"false"
},
"business":{
"currentState":"false"
}
}
}
Here I my code for updating:
const updatedSource = await prisma.sources.update({
where: {
name: 'toy'
},
data: {
data: {
sports: {
currentState: "true"
}
}
},
})
Here is my schema file
type SourcesData {
business SourcesDataState
sports SourcesDataState
}
type SourcesDataState {
currentState StateData[]
}
type StateData {
title String
url String
}
model sources {
id String #id #default(auto()) #map("_id") #db.ObjectId
data SourcesData
name String #unique
}
When I execute the above logic I get error as:Unknown arg `sports` in data.data.sports for type SourcesDataUpdateEnvelopeInput. Did you mean `set`? Available args:
Please guide what I am missing while updating.
The TypeScript should be pretty helpful in telling you what arguments you can or cannot use when interacting with Prisma. I strongly recommend using a code editor that includes TypeScript typehinting/Intellisense so you can see errors and warnings about your TypeScript usage as you are developing with Prisma.
Where it says Available args in your error, that should tell you the arguments that prisma.sports.update actually expects. If I had to guess (this may not be accurate, but you HAVE to look at the TypeScript to know exactly what it's supposed to be), it should look something like this:
const updatedSource = await prisma.sources.update({
where: {
name: 'toy'
},
data: {
data: {
update: {
sports: {
update: {
currentState: {
set: ["true"]
}
}
}
}
}
},
})
I strongly recommend reading Prisma's documentation on updating related/nested records: https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries#update-a-specific-related-record
let typeEncounter = await prisma.encounter.update({
where: {
id
},
data: {
[property]: {
update: {
[subProperty] : value,
},
},
},
}
)
I get a receive the error Unknown arg update in data..update
I have seen some people mention nesting updates but no official documentation and can't seem to get this straightened out. Anybody have any ideas? The property and subproperty are largely irrelevant here, just examples. The code works fine aside from updated a subfield of a type (mongoDB prisma). Without the update the entire type gets overwritten rather than the selected field.

Type 'Document<any>' is not assignable to type 'Pick<Pick<_LeanDocument<TicketDoc>

I have an error that is clear as mud on my terminal:
TSError: тип Unable to compile TypeScript: [orders-depl-9fbcb84ff-ngt2z
orders] src/models/ticket.ts(47,5): error TS2322: Type 'Document'
is not assignable to type 'Pick<Pick<_LeanDocument, "_id" |
"__v" | "id" | "title" | "price" | "isReserved">, "_id" | "__v" | "id"
| "title" | "price"> | QuerySelector<...> | undefined'.
[orders-depl-9fbcb84ff-ngt2z orders] Type 'Document' is missing
the following properties from type
'Pick<Pick<_LeanDocument, "_id" | "__v" | "id" | "title" |
"price" | "isReserved">, "_id" | "__v" | "id" | "title" | "price">':
title, price
It's referencing this model file:
import mongoose from 'mongoose';
import { Order, OrderStatus } from './order';
interface TicketAttrs {
title: string;
price: number;
}
export interface TicketDoc extends mongoose.Document {
title: string;
price: number;
isReserved(): Promise<boolean>;
}
interface TicketModel extends mongoose.Model<TicketDoc> {
build(attrs: TicketAttrs): TicketDoc;
}
const ticketSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
price: {
type: Number,
required: true,
min: 0
}
}, {
toJSON: {
transform(doc, ret) {
ret.id = ret._id;
delete ret._id;
}
}
});
ticketSchema.statics.build = (attrs: TicketAttrs) => {
return new Ticket(attrs);
};
// Run query to look at all orders. Find an order where the
// ticket is the ticket just found *and* the order status is *not* cancelled.
// If we find an order from that means the ticket *is* reserved
ticketSchema.methods.isReserved = async function () {
// this === the ticket document that I just called 'isReserved' on
const existingOrder = await Order.findOne({
ticket: this,
status: {
$in: [
OrderStatus.Created,
OrderStatus.AwaitingPayment,
OrderStatus.Complete
]
}
});
return !!existingOrder;
};
const Ticket = mongoose.model<TicketDoc, TicketModel>('Ticket', ticketSchema);
export { Ticket };
I don't see where I have made a syntax error anywhere and I have no idea what this type of Pick is. It seems like the issue is that ticket is not ticket but should be equal to this, except it's not.
I know it's been a long time. But, I saw this answer in user Jonathan's Udemy questions and answers and I think it's better than disabling TS verification:
Not a big fan of ts-ignore because of reasons stated by Korey above.
I suspect the cause of the problem is the extra isReserved() method added to a Ticket document.
Hence, instead of searching by the Ticket document (this), I searched via the id of the Ticket document (this.id). This should work fine.
ticketSchema.methods.isReserved = async function() {
const existingOrder = await Order.findOne({
ticket: this.id, // Ticket id
status: {
$in: [
OrderStatus.Created,
OrderStatus.AwaitingPayment,
OrderStatus.Complete
]
}
})
return !!existingOrder
}
Wow, this was one of those gotchas in TypeScript where you have to have deep knowledge to figure out. So here is how you make the error go away:
// Run query to look at all orders. Find an order where the
// ticket is the ticket just found *and* the order status is *not* cancelled.
// If we find an order from that means the ticket *is* reserved
ticketSchema.methods.isReserved = async function () {
// this === the ticket document that I just called 'isReserved' on
const existingOrder = await Order.findOne({
//#ts-ignore
ticket: this,
status: {
$in: [
OrderStatus.Created,
OrderStatus.AwaitingPayment,
OrderStatus.Complete
]
}
});
return !!existingOrder;
};
I normally would not do this as it bypasses the TS checks that we rely on to make sure everything is type safe. For now though, it does the trick as the error was just bizarre since it was not technically anything I did wrong in developing the code, just a TypeScript quirk I guess.

"QueryFailedError: operator does not exist", How to use PostgreSql array contains operator with typeorm?

I am using TypeOrm with TypeScript and I have a products table like this.
export enum ColorTone {
Cambridge = 'GREY_TONE',
Nicolock = 'RED_TONE',
Keystone = 'BLUE_TONE',
TechoBloc = 'YELLOW_TONE',
}
#Entity('product')
export class Product {
#Column()
name: string;
#Column({
type: 'enum',
enum: Brand,
array: true,
default: [],
})
colorTones: ColorTone[]; // available colors for a product
}
I want to find all products containing a specific color tone available.
I tried this using TypeOrm query.
...
constructor(#InjectRepository(Product) private productsRepository: Repository<Product>) {}
...
findProducts(colorTone: ColorTone): Promise<Product[]> {
return this.productsRepository.createQueryBuilder('product')
.where('product.colorTones #> ARRAY[:colorTone]', { colorTone });
}
But I get this console error.
QueryFailedError: operator does not exist: product_colortones_enum[] #> text[]
...
Not sure if there is a way to do the typecasting.
I tried to find the solution all day and any relevant link or reference link will be appreciated.
You need to cast to the correct type.
.where('product.colorTones #> ARRAY[:colorTone::product_colortones_enum]', { colorTone });
If typeorm gets confused by the colon salad, move the cast to the array
.where('product.colorTones #> ARRAY[:colorTone]::product_colortones_enum[]', { colorTone });

How to define parameters/arguments to a list resolver?

I have this schema and the corresponding resolvers:
const schema = buildSchema(
`
type Query {
posts(id: Int): [Post]
}
type Post {
id: Int!,
title: String,
date: String
}`
);
const resolvers = {
posts(root, { id }, context, info) {
console.log(id); // Undefined
return [
{
id: 0,
date: '21/04/2018',
title: 'Post 1'
},
{
id: 1,
date: '07/10/2018',
title: 'Post 2'
}
];
},
Post(postObj) {
return {
id: postObj.id,
title: postObj.title,
date: postObj.date
}
}
}
The problem is that when I query for posts with an specified id, like this:
query {
posts(id: 0) {
title
}
}
... I get an error that says I haven't defined such argument (id).
I defined the id argument according to the GraphQL Docs. Any suggestions of what may be causing this error and how to solve it?
When you use buildSchema, you effectively prevent yourself from being able to define custom resolvers for a given field in your schema. Instead, the default resolver will always be used. The default resolver simply takes the "parent" or "root" object for a given field, looks up the property on that parent object with the same name as the field and returns its value.
You can "get away" with this for simpler schemas by passing in a root object along with your schema. This root object then becomes the parent object referenced by the default resolver, but only for top level fields (like each field you define for your Query type). So in this case, when GraphQL resolves your query, the default resolver sees a posts property on the parent object and returns that. Because posts is actually a function, it calls the function first and then returns the value, but the arguments it calls it with are not the same arguments that a resolver is called with.
Resolvers receive four parameters -- 1) the "root" or "parent" value, 2) arguments, 3) context and 4) an "info" object containing additional data about the request. Any function called by the default resolver will only get the last 3 parameters (so no "root" value).
In other words, you should change your root object to look more like this:
const root = {
posts({ id }, context, info) {
return [
{
id: 0,
date: '21/04/2018',
title: 'Post 1'
},
{
id: 1,
date: '07/10/2018',
title: 'Post 2'
}
];
},
}
However, does it this way will only let you handle top-level fields like queries. You will not be able to customize resolver behavior for fields on other types, like Post. To do that, you should use graphql-tools' makeExecutableSchema.

Implementing pagination in vanilla GraphQL

Every tutorial I have found thus far has achieved pagination in GraphQL via Apollo, Relay, or some other magic framework. I was hoping to find answers in similar asked questions here but they don't exist. I understand how to setup the queries but I'm unclear as to how I would implement the resolvers.
Could someone point me in the right direction? I am using mongoose/MongoDB and ES5, if that helps.
EDIT: It's worth noting that the official site for learning GraphQL doesn't have an entry on pagination if you choose to use graphql.js.
EDIT 2: I love that there are some people who vote to close questions before doing their research whereas others use their knowledge to help others. You can't stop progress, no matter how hard you try. (:
Pagination in vanilla GraphQL
// Pagination argument type to represent offset and limit arguments
const PaginationArgType = new GraphQLInputObjectType({
name: 'PaginationArg',
fields: {
offset: {
type: GraphQLInt,
description: "Skip n rows."
},
first: {
type: GraphQLInt,
description: "First n rows after the offset."
},
}
})
// Function to generate paginated list type for a GraphQLObjectType (for representing paginated response)
// Accepts a GraphQLObjectType as an argument and gives a paginated list type to represent paginated response.
const PaginatedListType = (ItemType) => new GraphQLObjectType({
name: 'Paginated' + ItemType, // So that a new type name is generated for each item type, when we want paginated types for different types (eg. for Person, Book, etc.). Otherwise, GraphQL would complain saying that duplicate type is created when there are multiple paginated types.
fields: {
count: { type: GraphQLInt },
items: { type: new GraphQLList(ItemType) }
}
})
// Type for representing a single item. eg. Person
const PersonType = new GraphQLObjectType({
name: 'Person',
fields: {
id: { type: new GraphQLNonNull(GraphQLID) },
name: { type: GraphQLString },
}
})
// Query type which accepts pagination arguments with resolve function
const PersonQueryTypes = {
people: {
type: PaginatedListType(PersonType),
args: {
pagination: {
type: PaginationArgType,
defaultValue: { offset: 0, first: 10 }
},
},
resolve: (_, args) => {
const { offset, first } = args.pagination
// Call MongoDB/Mongoose functions to fetch data and count from database here.
return {
items: People.find().skip(offset).limit(first).exec()
count: People.count()
}
},
}
}
// Root query type
const QueryType = new GraphQLObjectType({
name: 'QueryType',
fields: {
...PersonQueryTypes,
},
});
// GraphQL Schema
const Schema = new GraphQLSchema({
query: QueryType
});
and when querying:
{
people(pagination: {offset: 0, first: 10}) {
items {
id
name
}
count
}
}
Have created a launchpad here.
There's a number of ways you could implement pagination, but here's two simple example resolvers that use Mongoose to get you started:
Simple pagination using limit and skip:
(obj, { pageSize = 10, page = 0 }) => {
return Foo.find()
.skip(page*pageSize)
.limit(pageSize)
.exec()
}
Using _id as a cursor:
(obj, { pageSize = 10, cursor }) => {
const params = cursor ? {'_id': {'$gt': cursor}} : undefined
return Foo.find(params).limit(pageSize).exec()
}