Given these schemas:
model awayinfo {
PlayerID Int #id
IsAway Boolean
playerinfo playerinfo #relation(fields: [PlayerID], references: [ID])
}
model playerinfo {
name String #db.VarChar(15)
ID Int #id #unique #default(autoincrement())
awayinfo awayinfo?
}
How would I create a prisma SQL update for the awayinfo table if the only identifier I have would be the Name of a player and not the ID?
what I tried:
I try to pass something into the WHERE part as seen below:
const result = await prisma.awayinfo.update({
where: {
PlayerID: {
name: name
}
},
data: {
IsAway: true,
}
});
but it always gives me the Error:
Invalid `prisma.awayinfo.update()` invocation:
Argument PlayerID: Got invalid value
{
name: 'Dummy'
}
on prisma.updateOneawayinfo. Provided Json, expected Int.
I got pretty desperate and even tried wonky selects like this one
const result = await prisma.awayinfo.update({
where: {
PlayerID: {
playerinfo: {
Where: {
name: name
},
select: {ID: true},
}}
}, .....
but obviously this would not work aswell.
I wonder what I am missing here and I cannot find any example of a condition within the WHERE clause in the prisma documentation
There are two issues here, the first is in your Prisma schema/data model and the second is in your query.
Making name unique
Firstly, if you want to uniquely identify a record in the playerinfo table with just the name property, this property must be unique. Otherwise, it's not possible for your database (and Prisma) to know which player info record to update in case there are multiple records with the same name property.
Update your schema accordingly:
model playerinfo {
name String #unique #db.VarChar(15)
ID Int #id #unique #default(autoincrement())
awayinfo awayinfo?
}
Rewriting the update query
Since the where condition in your update references a property playerinfo model, that is where you should begin your query. Here's what it looks like:
const data = await prisma.playerinfo.update({
where: {
name: name
},
data: {
awayinfo: {
update: {
IsAway: true
}
}
}
})
Related
I am using Vue and Apollo and I am making a querie that looks just like the box below.
After I get the API response, I would like to call a method from my methods object. However Vue, doesn't give me acess to it within apollo object.
I would like to know how can I call one of my methods, but only after I am sure I got that response, without having to manually trigger it with a button or something else.
apollo: {
materials: {
query: gql`
query allMaterials($tenantId: ID, $name: String) {
tenantMaterials(tenantId: $tenantId, name: $name) {
edges {
node {
name
materialType {
name
id
}
brand
vendor
size
unit
inventory
createdAt
updatedAt
isActive
updatedBy
id
}
}
totalCount
}
}
`,
variables() {
return {
name: null
};
},
fetchPolicy: "cache-and-network",
update: response => {
return response.tenantMaterials.edges;
//I want to call a function/method after this response
},
skip: false
},
}
Use update(data) or result(result, key)
update(data) {return ...} to customize the value that is set in the
vue property, for example if the field names don't match.
result(ApolloQueryResult, key) is a hook called when a result is
received (see documentation for ApolloQueryResult (opens new window)).
key is the query key in the apollo option.
https://apollo.vuejs.org/api/smart-query.html
I want to find the highest value in a specific column of a specific table. It should be very simple.
this is the documentation of LB4 https://loopback.io/doc/en/lb2/Where-filter But I didn't find it there.
We did this through a custom repository method where we execute a select max() query and have a custom controller method (i.e. /next-id) that calls it.
Repository method:
async nextId(): Promise<any> {
return this.dataSource
.execute('select MAX(id)+5 as nextId from route_lookup')
.then(data => {
if (data[0].NEXTID === null) {
data[0].NEXTID = 1005;
}
return data[0].NEXTID;
});
}
Controller method:
#get('/route-lookups/next-id')
#response(200, {
description: 'Next avaialble id for route lookup',
content: {
'application/json': {
schema: {
type: 'number',
},
},
},
})
async nextId(): Promise<number> {
return await this.routeLookupRepository.nextId();
}
Within the Loopback Filter Documentation they do mention a way to achieve this, even though it's not as obvious.
/weapons?filter[where][effectiveRange][gt]=900&filter[limit]=3
Essentially you can do the following:
Identify the column of interest.
Use the gt operator to set a min number
Add order if you wanted to ensure the sorting order is as expected.
Limit the results to 1.
Here is a code example:
Employees.find({
where: {
age: {
gt: 1
}
},
order: 'age ASC',
limit: 1
})
Please let me know if this is what you were going for or if you need some more support.
I'm working on project with prisma nowadays.
I designed m to n relationship table. Below is my code.
schema.prisma
model Artists {
id Int #id #default(autoincrement())
artistName String?
Albums Albums[]
Artists_Tracks Artists_Tracks[]
}
model Artists_Tracks {
id Int #id #default(autoincrement())
trackId Int?
artistId Int?
Artists Artists? #relation(fields: [artistId], references: [id])
Tracks Tracks? #relation(fields: [trackId], references: [id])
##index([artistId], name: "Artists_Tracks_artistId_foreign_idx")
##index([trackId], name: "Artists_Tracks_trackId_foreign_idx")
}
model Tracks {
id Int #id #default(autoincrement())
trackName String?
albumTrackNumber Int?
albumId Int?
Albums Albums? #relation(fields: [albumId], references: [id])
Artists_Tracks Artists_Tracks[]
##index([albumId], name: "Tracks_albumId_foreign_idx")
}
This is my prisma code. What I want to do is search by trackname and get all tracks's information with artist's name.
+edit)
What I've tried is
// first try
optObj.include = {
Albums: {
select: { cover: true },
},
Artists_Tracks: {
Albums: true
},
};
// second try
optObj.include = {
Albums: {
select: { cover: true },
},
Artists_Tracks: true,
Artists: true,
};
const result = await prisma.tracks.findMany(optObj);
and use promise.all to get artist's name for each track. Is there any way to do that in once by one query? I'm pretty sure there would be more better way to do this. Thank you in advance.
As per your schema, the include will not contain the Artist relation. It will have to be included from the Artists_Tracks relation like this:
const result = await prisma.tracks.findMany({
include: {
Albums: {
select: { cover: true },
},
Artists_Tracks: { include: { Artists: true } },
},
})
This way you can get the Artist for each track present.
I'm learning graphql and I think I've spot one flaw in it.
Suppose we have schema like this
type Hero {
name: String
friends: [Person]
}
type Person {
name: String
}
and two queries
{
hero {
name
friends {
name
}
}
}
and this
{
hero {
name
}
}
And a relational database that have two corresponding tables Heros and Persons.
If my understanding is right I can't resolve this queries such that for the first query the resulting sql query would be
select Heros.name, Persons.name
from Heros, Persons
where Hero.name = 'Some' and Persons.heroid = Heros.id
And for the second
select Heros.name, Persons.name from Heros
So that only the fields that are really needed for the query would be loaded from the database.
Am I right about that?
Also if graphql would have ability to return only the data that's needed for the query, not the data that's valid for full schema I think this would be possible, right?
Yes, this is definitely possible and encouraged. However, the gist of it is that GraphQL essentially has no understanding of your storage layer until you explicitly explain how to fetch data. The good news about this is that you can use graphql to optimize queries no matter where the data lives.
If you use javascript, there is a package graphql-fields that can simplify your life in terms of understanding the selection set of a query. It looks something like this.
If you had this query
query GetCityEvents {
getCity(id: "id-for-san-francisco") {
id
name
events {
edges {
node {
id
name
date
sport {
id
name
}
}
}
}
}
}
then a resolver might look like this
import graphqlFields from 'graphql-fields';
function getCityResolver(parent, args, context, info) {
const selectionSet = graphqlFields(info);
/**
selectionSet = {
id: {},
name: {},
events: {
edges: {
node: {
id: {},
name: {},
date: {},
sport: {
id: {},
name: {},
}
}
}
}
}
*/
// .. generate sql from selection set
return db.query(generatedQuery);
}
There are also higher level tools like join monster that might help with this.
Here is a blog post that covers some of these topics in more detail. https://scaphold.io/community/blog/querying-relational-data-with-graphql/
In Scala implementation(Sangria-grahlQL) you can achieve this by following:
Suppose this is the client query:
query BookQuery {
Books(id:123) {
id
title
author {
id
name
}
}
}
And this is your QueryType in Garphql Server.
val BooksDataQuery = ObjectType(
"data_query",
"Gets books data",
fields[Repository, Unit](
Field("Books", ListType(BookType), arguments = bookId :: Nil, resolve = Projector(2, (context, fields) =>{ c.ctx.getBooks(c.arg(bookId), fields).map(res => res)}))
)
)
val BookType = ObjectType( ....)
val AuthorType = ObjectType( ....)
Repository class:
def getBooks(id: String, projectionFields: Vector[ProjectedName]) {
/* Here you have the list of fields that client specified in the query.
in this cse Book's id, title and author - id, name.
The fields are nested, for example author has id and name. In this case author will have sequence of id and name. i.e. above query field will look like:
Vector(ProjectedName(id,Vector()), ProjectedName(title,Vector()),ProjectedName(author,ProjectedName(id,Vector()),ProjectedName(name,Vector())))
Now you can put your own logic to read and parse fields the collection and make it appropriate for query in database. */
}
So basically, you can intercept specified fields by client in your QueryType's field resolver.
This works:
db.users.update( { _id:ObjectId("1234")}, { $set: { active: 1 } } )
because:
If the field does not exist, $set will add a new field with the specified value, provided that the new field does not violate a type constraint.
So active is created with value "1" and I'm happy, because that's what I wanted.
But
db.users.update( { _id:ObjectId("1234")}, { $set: { profile: { active: 1 } } } )
This, instead of writing "active:1" as a new row inside profile, will erase all the data of "profile" and then, when 'profile' is empty, finally insert "active:1". I just lost the data of 25k users (profile.name,profile.age, etc). Could someone explain why?
The $set operator replaces the value of a field with the specified value.
To update an embedded field, use the dot notation. When using the dot notation, enclose the whole dotted field name in quotes:
db.users.update( { _id:ObjectId("1234")}, { $set: { "profile.activeactive": 1 } } )
The problem with your code is that when you did
db.users.update( { _id:ObjectId("1234")}, { $set: { profile: { active: 1 } } } )
It will update the value of profile field in the user collection, by replacing the content of profile field with { active: 1 } as the behaviour you got.
To update a particular field('active' in this case)in a particulardocument({ profile: { active: 1 } in this case)you have to use thedot notation. For example:
db.users.update( { _id:ObjectId("1234")}, { $set: { "profile.active": 1 } } )
It will also do an update, but now it will update the active field of the profile document in the usercollection.
Explaination
The $set operator replaces the value of a field with the specified
value.
If the field does not exist, $set will add a new field with the
specified value, provided that the new field does not violate a type
constraint. If you specify a dotted path for a non-existent field,
$set will create the embedded documents as needed to fulfill the
dotted path to the field.
If you specify multiple field-value pairs, $set will update or create
each field.
For ref: "http://docs.mongodb.org/manual/reference/operator/update/set/"