In datamodel.graphql
type Ride {
rideId: String
productId: String
passenger: Passenger
origin: Origin
destination: Destination
dateTime: DateTime
feedback: String
}
type Passenger {
id: ID! #unique
firstName: String
lastName: String
}
type Destination {
# The unique ID of the destination.
id: ID! #unique
latitude: Float
longitude: Float
address: String
}
type Origin {
id: ID! #unique
latitude: Float
longitude: Float
address: String
}
type Report {
productId: String
passenger: Passenger
description: String
}
I deployed this data model and generates a MySql Db, auto queries, mutations with this.
It creates a "Ride", "Passenger", "Report" and "Origin" table in MySql. But it didn't create any column for passenger, origin, destination in "Ride" table.
It separates creates a relation table for this like _PassengerToRide, _OriginToRide, and _DestinationToRide.
Lack of relation establishment in "Ride" table, I couldn't get the details from Passenger, Origin and Destination tables when I query "rides()". Is this the correct way to define the datamodel.graphql. (edited)
Based on your description, this query should "just work":
query {
rides {
feedback
passenger {
id
}
origin {
id
}
destination {
id
}
}
}
Prisma uses the relation table approach you mentioned to keep track if relations between two nodes, for example table _OriginToRide relates to relation #relation(name: "OriginToRide") from your datamodel.
You don't have to change anything on the SQL level to connect the relations afterwards.
Note: The above applies to Prisma database connectors with activated migrations. If your connector doesn't do migrations, different approaches to represent relations are supported. The respective datamodel to support this can be generated by introspecting your existing database schema.
Related
I'm trying to implement batching and caching with the Facebook DataLoader. Let's say I have the following schema (taken from here):
type Post {
id: ID! #id
title: String!
published: Boolean! #default(value: false)
author: User
comments: [Comment!]!
}
type User {
id: ID! #id
name: String
posts: [Post!]!
comments: [Comment!]!
}
type Comment {
id: ID! #id
text: String!
post: Post!
writtenBy: User!
}
I am working on a tricky resolver which lists all comments created by the same user under the same post for a given comment. To retrieve a single entry I would go like:
const fetchCommentsByUserForSamePost = async (commentId: string, userId: string): Promise<Comment[]> => {
const comments = await this.prisma.comment.findOne({ where: { id: commentId } })
.post()
.comments({
where: {
writtenBy: { id: userId }
}
})
return comments;
}
This works well for a single query, but I would like to batch the queries. In raw SQL I'd return commentId and userId in every row, such that I can group the results by these fields. But I can't find a way to return the original commentId with Prisma to generalize the query to work with a list of commentId - userId pairs.
Is there a way to accomplish this with Prisma, or I am missing something? I am aware that making two requests could solve this, but that would result in an involved logic, plus I'd rather avoid making two DB roundtrips.
Prisma 2.0 already has a Dataloader built in exactly for this purpose. This means your resolvers might do multiple calls to findOne but those will be batched into one big SQL query under the hood. So there should be no need for you to implement this optimization on your own.
I am using amplify graphql api. Where I have an Item model with different attributes.
I am trying to create an autocomplete or autosuggest api with user-added input.
type Item #model
#searchable
{
id: ID!
name: String!
description: String
category: String
fullAddress: String!
street: String
area: String
district: String
city: String
state: String!
zip: Int!
Right now I aam querying it like
query SearchEvent {
searchEvents(filter:{
name: {
ne: $query
}
}) {
items {
id
name
}
}
}
But this gives me only full word match to a particular key, like this example is match of name.
How do I query to get a response of any match or suggestions from any key of the item object?
For example, if my item name is Laptop and api query is la it should return the laptop item and other matching items name.
Likewise, if api query is ala it should return the Alabama, Alaska names including the item name matching with ala with a limit to 10 let's say.
Anyway is this possible? Any lead will eb helpful.
I have a table in a database that stores products to buy (let's call the table Product). It's necessary to track changes of every product. I have 2 ideas for versioning records.
First is to create a new record in Product by copying the product and override the fields that differ and keep the reference in the record for the older and newer version. In that case a record in Product table is read-only except the field that indicate whether the product is archived or not.
Second is to create 2 tables: Product and ArchivisedProduct. The Product's records are editable, but for each change is created a new record in ArchivisedProduct where differences only are stored (so except an id, all fields are nullable) and tables' fields hold references to each other.
Do you know any tool that could manage that process and works well with Node.js, Prisma, PostgreSQL and Apollo? For such use winston/papertrail was recomended for me, but as I read the docs it seems that it only creates logs.
Exemplary database structure for more clearance:
1st example:
type Product {
id: Int! #id
name: String!
price: Float!
archived: Boolean!
productVersionIds: [Product]!
}
2nd example:
type Product {
id: Int! #id
name: String!
price: Float!
archivisedProductIds: [ArchivisedProduct]! #relation(name: "ProductToArchiva")
}
type ArchivisedProduct {
id: Int! #id
name: String
price: Float
product: Product! #relation(name: "ProductToArchiva")
}
Depending on how many products you intend to store, it may be simpler to have each Product version stored in the ProductVersion model, and then keep tabs on the latest Product (the "head") in a Product model.
You'd have:
type ProductVersion {
id: Int!
version: Int!
name: String!
price: Float!
##id([id, version])
}
type Product {
productId: Int! #id
headVersion: Int!
productVersion: Product! #relation(fields: [productId, headVersion], references: [id, version])
}
For each change to a Product, you'd store the new ProductVersion containing the information, and update the corresponding Product to point the headVersion to the new ProductVersion. That would all be part of a single transaction to ensure consistency.
To query your list of products you'd use the Product object and join the ProductVersion.
If you store a lot of products and joining is a concern, you con consider having a copy of the whole ProductVersion data in the Product instead of using a relation through the headVersion field.
Note that it also would imply you'd compute diff at runtime, and not have them stored directly in the database itself.
Okay, so I'm starting to dig into graphql a little bit, and I've built an api using koa, type-graphql, and sequelize-typescript. Everything works pretty well.... I managed to get a query working, and even managed to optimize a little bit by using graphql-fields to filter the columns I query in the database... However when I've aliased a field name, I can't seem to get the mapped name.....
For example, given the following ObjectType/Sequelize Model....
export interface IDepartment {
departmentId: number;
name: string;
description: string;
}
#ObjectType()
#Table({ underscored: true })
export class Department extends Model<Department> implements IDepartment {
#Field({ name: 'id' })
#PrimaryKey
#Column({ field: 'department_id'})
public departmentId: number;
#Field()
#Length({ max: 100 })
#Column
name: string;
#Field()
#Length({ max: 100 })
#AllowNull
#Column
description: string;
}
and sample query....
query {
department(name: "Test Dept") {
id
name,
description
}
}
sample resolver...
async department(#Arg('name') name: string, #Info() info: GraphQLResolveInfo) {
return Department.findOne({
where: { name }
});
}
This works just fine.... but when I do
async department(#Arg('name') name: string, #Info() info: GraphQLResolveInfo) {
let fields = Object.keys(getFields(info))
return Department.findOne({
attributes: fields,
where: { name }
});
}
(getFields is graphql-fields), it fails because the query specified field name id, which is what graphql-fields returns, but the column name is department_id (sequelize model name departmentId).
I've gone through the schema with a fine tooth comb, using the introspectionFromSchema function to see a detailed copy of my schema, but nowhere is there a mention of departmentId or department_id.... However I know it's out there somewhere because when I exclude the attributes field from my sequelize query, even though sequelize returns departmentId as the property name, when I return it from my resolver and it reaches the client, the property name is id.
Any help would be appreciated.... I'm trying to optimize everything by only fetching requested properties and not the entire object. I could always store the maps as separate constants and use those in my #Field definition, but I want to do that as a last resort, however if I can I'm trying to keep the code as lean as possible....
Thank you all in advance.
Unfortunately, the name option was introduced mostly to support resolvers inheritance. Using this for mapping the schema field names is a kinda undocumented feature so it's doesn't provide any mapping or exposing mapping metadata.
Using the name option for input or args types will be even worse - it will result in no access to the fields and the properties being undefined.
For now my recommendation is to just keep it simple and don't map the field names until a proper fix arrives.
All the examples I've found for Foxx.Model schemas are flat - i.e. they don't include nested objects.
I'm trying to add a hash to save geo info on a model like this:
var Foo = Foxx.Model.extend({
schema: {
name: joi.string().required(),
location: joi.object().keys({
lat: joi.number(),
lng: joi.number()
})
}
});
This shows up in the Foxx interface Data Type as this:
foo {
name (string),
location (object, optional)
}
How do I get it to show the key names 'lat' and 'lng' for the location object?
Or am I thinking about this incorrectly?
You are using it correctly and it will work and check your object correctly. This is just a limitation of the documentation tool used in the admin interface of ArangoDB.