Prisma how can I update only some of the models fields in update() - express

I have a Prisma model with lets say 10 fields.
Think User model with firstname, lastname, address, e-mail , phone, mobile, age etc.
I am trying to write a update method for this, where I most of the times only want to update some or only 1 of the fields. Not the whole User. If the field is not sent with the request, I want to keep the value from the db.
What would the best practice be for this. Should I check for all fields to be in the req object?
How could I write this for prisma?
Example on how I would like it to work:
req = {firstname: 'Bob', email: 'bob#bob.bob', etc}
const updateUser = await prisma.user.update({
where: {
email: 'viola#prisma.io',
},
data: {
req.firstname ? (email: req.firstname) : null,
req.email ? (email: req.email) : null,
req.address? (email: req.address) : null,
},
})
Or should I check for values to be present in req and build the data object in 10 versions:
let customDataObject = {}
if (req.firstname) {
customDataObject.firstname = req.firstname
}
if (req.email) {
customDataObject.email= req.email
}
const updateUser = await prisma.user.update({
where: {
email: 'viola#prisma.io',
},
data: customDataObject,
})

The undefined property is used in Prisma to do exactly what you're trying to achieve. Basically, when a field is assigned undefined it means ignore this and do nothing for this field. You can learn more about this in the article about null and undefined in the docs.
This is what your update query should look like.
// assuming email, firstname and address fields exist in your prisma schema.
const updateUser = await prisma.user.update({
where: {
email: 'viola#prisma.io',
},
data: {
firstname: req.firstname || undefined, // if req.firstname is falsy, then return undefined, otherwise return it's value.
email: req.email || undefined,
address: req.address || undefined
},
})

Related

TypeORM select data from nested relations

Using
await this.budgetRepository.createQueryBuilder("budget")
.leftJoinAndSelect("budget.contact", "contact")
.leftJoinAndSelect("contact.photo", "contactPhoto")
.getMany();
I get a list with objects like this:
Budget {
id: 1,
unnecessary_property1: something,
contact: Contact {
unnecessary_property2: something,
photo: Photo {
unnecessary_property3: something,
url: "url.com"
},
},
}
But I want to select only the necessary properties in the nested objects (relations) and get a list of objects like this:
Budget {
id: 1,
contact: Contact {
photo: Photo {
url: "url.com"
},
},
}
How is that possible with TypeORM?
This is possible but we have to select everything manually with .select()
await this.budgetRepository.createQueryBuilder("budget")
.leftJoinAndSelect("budget.contact", "contact")
.leftJoinAndSelect("contact.photo", "contactPhoto")
.select(['budget.id', 'contactPhoto.url']
.getMany();
If you're using repository pattern that you will be achieve the similar result with:
await this.budgetRepository.find({
relations: ["contact", "contact.photo"]
})
You would have to use the .select() function and pass the given properties you want for each entity.
for your example:
const user = await createQueryBuilder("budget")
.leftJoinAndSelect("budget.contact", "contact")
.leftJoinAndSelect("contact.photo", "contactPhoto")
.select([/* everything from budget */, 'contact.photo.url'....]) // added selection
.getMany();

what is the proper way to get my google account profile image (URL) in NodeJS

see below error message and source code, doesn't work even after I added person fields of photos:
TypeError: Cannot read properties of undefined (reading 'url')
var p = google.people("v1");
p.people.get(
{
resourceName: "people/me",
personFields: "names,emailAddresses,photos",
auth: oauth2Client,
},
function (err, user) {
if (err) { return; }
res.json({
name: encoder.htmlEncode(user.displayName),
picture: user.image.url,
});
}
);
You should have a look into the docs. The people.get request returns a Person object. And such a Person does not have an image property, so you cant't access user.image.url because user.image does not exist. Neither does user.displayName, btw.
What a Person actually looks like is as follows (the ... meaning there are additional properties too, look up in the docs, if you need more)
{
...
coverPhotos: [{url: "https://url.to/image", ...}],
...
names: [{ displayName: "John Doe", ...}],
...
}
So to get the name and picture, first of all, instead of photos you need to request coverPhotos. Then you can extract the desired values, as follow:
p.people.get(
{
resourceName: "people/me",
personFields: "names,emailAddresses,coverPhotos",
auth: oauth2Client,
},
function (err, user) {
if (err) { return; }
let
name = user.names?.[0]?.displayName || "John Doe",
url = user.coverPhotos?.[0]?.url
res.json({
name: encoder.htmlEncode(name),
picture: user.image.url,
});
});
I used the conditional chaining ?. here, to make sure, this code does not run into an exception if for instance coverPhotos does not exist or is an empty array.

updateMany can't find argument

I have a query:
createNotification: async (_, args, {req, res}) => {
const followedBy = await prisma.user.updateMany({
where: {
following: {
some: {
id: req.userId
},
},
},
data: {
notifications: {
create: {
message: args.message,
watched: false,
},
},
},
})
And User and Notification models:
model User {
id Int #id #default(autoincrement())
email String #unique
name String
user_name String #unique
password String
movies Movie[]
notifications Notification[]
followedBy User[] #relation("UserFollows", references: [id])
following User[] #relation("UserFollows", references: [id])
}
model Notification {
id Int #id #default(autoincrement())
link String?
movie_id Int?
message String
icon String?
thumbnail String?
user User #relation(fields: [userId], references: [id])
userId Int
watched Boolean
}
When I run my query I get an error:
Unknown arg `notifications` in data.notifications for type UserUpdateManyMutationInput. Did you mean `email`? Available args:
type UserUpdateManyMutationInput {
email?: String | StringFieldUpdateOperationsInput
name?: String | StringFieldUpdateOperationsInput
user_name?: String | StringFieldUpdateOperationsInput
password?: String | StringFieldUpdateOperationsInput
}
The strange thing is that this works:
const followedBy = await prisma.user.findUnique({
where: {id: req.userId},
include: {
followedBy: true,
},
});
followedBy.followedBy.map(async(user) => {
await prisma.user.update({
where: {id: user.id},
data: {
notifications: {
create: {
message: args.message,
watched: false,
},
},
},
});
});
But this isn't making the best of what Prisma offers.
As of September 2021, Prisma does not support mutating nested relations in a top-level updateMany query. This is what the typescript error is trying to tell you, that you can only access email, name, user_name and password fields inside data. There's an open feature request for this which you could follow if you're interested.
For the schema that you have provided, here's a possible workaround that's slightly less readable but more optimized than your current solution.
createNotification: async (_, args, {req, res}) => {
// get the followers array of req.userId
const followedBy = await prisma.user.findUnique({
where: { id: req.userId },
include: {
followedBy: true,
},
});
// array of notification objects to be created, one for each follower of req.userId
let messageDataArray = followedBy.followedBy.map((user) => {
return {
userId: user.id,
message: args.message,
watched: false,
};
});
// do a bulk createMany.
// Since it's one query, it should be more optimized than running an update for each user in a loop.
await prisma.notification.createMany({
data: messageDataArray,
});
};
If you're interested, here's the docs reference for the kinds of nested updates that are possible.

Why is my SQL data returning undefined in my array methods?

The First array returned when I console log person
The second array that returns when I console log person
I am trying to create a function that pulls the data from 2 tables in my SQL database. I am using async/await, which is still new to me. The issue is somewhere in the two array methods; for some reason they are returning data as undefined.
async function updateRole() {
console.log('hi');
//cycle through both arrays and create new arrays with same information using maps
//return object with employee and role info
const allEmployees = await db.promise().query(`SELECT * FROM employees`);
const allRoles = await db.promise().query(`SELECT * FROM roles`);
console.log(allEmployees);
const employeeChoices = allEmployees.map((person) => {
return {
name: `${person.first_name} ${person.last_name}`,
value: person.id
}
})
const roleChoices = allRoles.map((role) => {
return {
name: role.title,
value: role.id
}
})
const { employeeId, roleId } = await inquirer.prompt([
{
type: 'list',
name: 'employeeId',
message: 'Which employee would you like to update?',
choices: employeeChoices
},
{
type: 'list',
name: 'roleId',
message: 'What is their new role?',
choices: roleChoices
}])
await db.promise().query(`UPDATE employees SET role_id = ? WHERE id = ?`, [roleId, employeeId])
console.log('Successfully updated employee!');
askQuestions();
Update: I added screenshots fo the console log for person. role returns the same format, but obviously different data. I am unsure what the array of ColumnDefinition objects does, or why it's there.

Redux Update a state with an array of objects

I have a state like this:
this.state ={
photos: [
{ description: 'someDescription', id: 1 },
{ description: 'someDescription', id: 2 },
{ description: 'someDescription', id: 3 }
]
}
How can I update one of the descriptions only?
Or I have to do something like
this.setState({ photos: newArrayOfObjectsWithOnlyOneUpdatedDescription })
You can create a funtion to do it for you like this:
const updatePhoto = (id, desc) =>
this.state.photos.map((obj) =>
obj.id === id ? Object.assign(obj, { description: desc }) : obj)
map function will return a new array, so you won't need to do a manual state cloning stuff.
Then reuse it as you need it:
this.setState({ photos: updatePhoto(2, 'new desc') })
You can create copy of state, then update copied state and setState
let stateCopy = Object.assign({}, this.state); // get the current state copy
stateCopy.photos = stateCopy.photos.slice(); // get photos object
stateCopy.photos[key] = Object.assign({}, stateCopy.photos[key]);
stateCopy.photos[key].description = 'new decription'; // update description of specific key
this.setState(stateCopy); // set state