Query Building with Knex/Postgres (nested Json) - javascript

I'm trying to build a query that will change all 'assets' status to 'Pending Transfer' where location equals the location in the request's body data.
An entry on the "location" column in my "assets" table is as follows:
{"site":"Xxxxx, XX","site_loc":{"shelf":"89","unit":"89"}}
This is the knex query I'm trying to build:
async function bulkUpdate(req, res){
const site = req.body.data;
const data = await knex('assets')
.whereRaw(`location -> 'site' = '${site.physical_site_name}'`)
.update("status", "Pending Transfer") //todo: update history as well
.returning('*')
.then((results) => results[0]);
res.status(200).json({ data });
}
There error I'm getting is:
message: `update "assets" set "status" = $1 where location -> 'site' = 'Xxxxx, XX' returning * - operator does not exist: json = unknown`
I've also tried using:
async function bulkUpdate(req, res){
const site = req.body.data;
const data = await knex('assets')
// .whereRaw(`location -> 'site' = '${site.physical_site_name}'`)
.whereJsonPath('location', '$.site', '=', `${site.physical_site_name}`)
.update("status", "Pending Transfer") //todo: update history as well
.returning('*')
.then((results) => results[0]);
res.status(200).json({ data });
}
Where I get the error:
message: `update "assets" set "status" = $1 where jsonb_path_query_first("location", $2) #>> '{}' = $3 returning * - function jsonb_path_query_first(json, unknown) does not exist`
I can't tell if this is because I'm using the '->' operator incorrectly, or if there is a different comparison I should use other than '='. Should I not be using the apostrophe's around site?
I'm fairly new at this, but I've been trying to research this and have been trying various ways and have yet to figure it out.
Any help on understanding this better would be much appreciated. Thank you.

Ahh...
And the answer is:
.whereRaw(`location ->> 'site' = '${site.physical_site_name}'`)

Related

NodeJS - TypeScript - URL Query doesn't work if multiple parameters

I have the following controller in my route:
export const getHotels = async (
req: Request,
res: Response,
next: NextFunction
) => {
try {
const hotels = await Hotel.find(req.query).limit(+req.query.limit);
res.status(200).json(hotels);
} catch (err) {
next(err);
}
};
In my database, I have hotels with a featured property (boolean) that I retrieve with Mongoose and reduce the results with its limit method.
I noticed that my query returns an empty array if I have several parameters no matter what if I call (GET): /api/hotels?featured=true&limit=1
It works fine if the controller is await Hotel.find().limit(+req.query.limit); and the URL /api/hotels?limit=1
Or if controller is await Hotel.find(req.query); and URL /api/hotels?featured=true
Does anyone have an idea of what could be the issue? Many thanks.
When your GET request is /api/hotels?featured=true&limit=1, the req.query content is:
{
featured: "true",
limit: "1"
}
...therefore the Hotel.find(req.query) looks for documents with both "featured" field as "true" AND "limit" field as "1"... hence it does not find anything.
You could make sure to extract only necessary fields, e.g. with Lodash pick utility:
Hotel
.find(pick(req.query, ["featured"]))
.limit(+req.query.limit)
I realised I wasn't using the object bracket with find() before which is why mongoDB could only take the 1st argument.
Example:
const { limit, ...others } = req.query;
const hotels = await Hotel.find({
...others,
}).limit(+limit);
By doing so, I can add any parameter in my GET request and don't need to use Lodash to pick a specific parameter.

Sequelize delete query runs, but does not resolve Promise

I have a node js server that is creating and deleting from database using Sequelize. When i create new user in "Users" table, query normally runs and server returns response. But when i try to delete user from "Users" table, query runs but promise isn't resolved, therefore i get no response from server. Here is
my code:
const { User } = require("./models")
const user = {id: "...."} //Parsed delete request from client, id is not undefined
User.destroy({
where: {
id: user.id,
},
})
.then(res.status(200).clearCookie("refresh-token"));
.catch(res.status(400));
What i see in console:
Executing (default): DELETE FROM "Users" WHERE "id" = '6d3edbab-03b8-429b-b249-a9d3ba6bce7a'
And after a while:
DELETE /api/user/delete - - - - ms [2021-3-14 14:17:11]
I delete stuff from other tables too and they work, so it seems that Users table is somewhat special. Whats wierd is that when i look in database i see that record was deleted. I have no idea what is happening.
Thanks for help!
I solved my issue by creating a new function that opens a new Sequelize connection and
uses that to delete records in db. Here it is:
function deleteUsr(id, res) {
const { Sequelize } = require("sequelize");
if (!/^([0-9a-z]){8}-([0-9a-z]){4}-([0-9a-z]){4}-([0-9a-z]){4}-([0-9a-z]){12}$/.test(id)) {
res.status(400).send("Provide valid UUID")
}
const seq = new Sequelize(
"connection string"
);
seq
.authenticate()
.then(console.log("yipeee"))
.catch(err => console.error(err));
seq
.query(`delete from "Users" where id='${id}'`)
.then(x => {
res.status(200).clearCookie("refresh-token").send(x);
seq.close();
})
.catch(err => {
res.status(400).send(err);
seq.close();
});
}
Avoid using this function if your input isn't sanitized properly, because anyone who is signed could delete any user if using this. I am taking uuid from verified jwt access token and comparing it to encrypted refresh token, so that user cannot even input anything into the function.
Hope it helped!

GET an object from mongodb using mongoose

The problem is probably simple, but my 2 AM brain can't understand what's going on anymore. I'm trying to create a profile page that shows basic public info. The way I'm trying to make it work is pulling out the users username from mongodb when registered the account by his specific _id. If needed, verification I use is JWT.
app.post('/api/user-profile', async (req,res) => {
const { token } = req.body
if(!token) {
return res.json({ status: 'error', error: 'not logged in' })
}
try {
const user = jwt.verify(token, JWT_SECRET)
const userid = user.id
const result = User.findOne({ userid })
console.log(result)
// return res.json({ status: 'ok', name: result })
} catch(error) {
// return res.json({ status: 'error', error: 'something went wrong' })
console.log(error)
}
})
I'm not sure what function should I use, findOne() or findById(). I tried to look at the documentation at mongoose, but the explanation is a bit too hard for me to understand.
P.S User = the user registration model. If needed I can paste in the code.
use findById instead of findOne if userid is _id and use await before the query, so do like this:
const result = await User.findById(userid)
if you want to use findOne :
const result = await User.findOne({"_id" : userid})
if you want a plain object javascript use .toObject after query like this:
const result = await User.findById(userid).toObject()
console.log(result)
I don't have a lot of experience with mongoose but worked with Mongo quite a lot. findOne is a direct correspondent of Mongo's findOne function which receives a query in the format {"key": "expectedValue"}. If you want to use it to get data by id the query is {"_id": user.id}.
Because fetching data by id is a common case, the lib added the method findByID which receives an ID, and then formats the query and makes an internal call to findOne.
For anyone interested, the answer is just like Mohammad Yaser Ahmadi said. Everything works fine, and by getting the username I did:
const user = jwt.verify(token, JWT_SECRET)
const userid = user.id
const result = await User.findById(userid)
const usersName = result.username
console.log(usersName)

Working SQL yields Syntax Error in pg-promise

I have the following code in one of my endpoints:
let setText = ''
for (const [ key, value ] of Object.entries(req.body.info)) {
setText = setText.concat(`${key} = ${value}, `)
}
// Last character always an extra comma and whitespace
setText = setText.substring(0, setText.length - 2)
db.one('UPDATE students SET ${setText} WHERE id = ${id} RETURNING *', { setText, id: req.body.id })
.then(data => {
res.json({ data })
})
.catch(err => {
res.status(400).json({'error': err.message})
})
It is supposed to dynamically generate the SQL from the request body. When I log the created SQL, it generates correctly. It even works when I directly query the database. However, whenever I run the endpoint, I get a syntax error that's "at or near" whatever setText is. I've tried using slice instead of substring with no change.
You should never concatenate values manually, as they are not escaped properly then, and open your code to possible SQL injections.
Use the tools that the library offers. For an UPDATE from a dynamic object, see below:
const cs = new pgp.helpers.ColumnSet(req.body.info, {table: 'students'});
const query = pgp.helpers.update(req.body.info, cs) +
pgp.as.format(' WHERE id = ${id} RETURNING *', req.body);
db.one(query)
.then(data => {
res.json({ data });
})
.catch(err => {
res.status(400).json({error: err.message})
});

check if object in array of objects - javascript

I know i have to use some but for some reason i cant seem to get it right. i have a collection in my mongodb database of posts. each post has an array of objects named "likes" that references the users that liked this post. so in my backend i want to check if the user exists in the likes array of the post. if it does not exist then like the post, else return with an appropriate message on my react frontend. The code i will include always returns false from some so a user can like a post infinite times.
exports.postLike = async (req, res, next) => {
const postId = req.query.postId;
const userId = req.query.userId;
console.log('postId: ' + postId);
try{
const post = await Post.findById(postId).populate('creator').populate('likes');
const user = await User.findById(userId);
if (!post.likes.some(post => post._id === user._id)){
post.likes.push(user);
console.log('liked a post');
const result = await post.save();
res.status(200).json({ message: 'Post liked!', post: result });
} else {
console.log('Post already liked!');
res.status(200).json({ message: 'Post already liked!', post: post });
}
}catch (err) {
if (!err.statusCode) {
err.statusCode = 500;
}
next(err);
}
};
i clearly haven't understood, yet, how some works so if you can help that would be great. also if you have any other solution that would be good in this case then please post it. i tried some random codes with indexOf and includes for checking but it didn't work either. i am not sure which is the right way to check if the user object is included in the "likes" array of objects. i would prefer not to write any function of my own to check this, i want to do it using an existing function/method provided by javascript.
Going to offer a different route here. You are fetching all the data including a join to the creator and likes just to add a like to the collection. This is a little wasteful and can be achieved by just doing an update and use $addToSet which will add the like if it does not exist.
You then just check nModified in the result to know if it was added or not. So you can have:
const result = await Post.updateOne(
{
id: 1
},
{
$addToSet: {
likes: {
userId: mongoose.Types.ObjectId(req.query.userId)
}
}
}
);
console.info(result.nModified === 1);
Alternatively, you can use some as follows using === to compare type and value:
posts.likes.some(like => like.userId.toString() === req.query.userId)
MongoDB.ObjectId is a wrapper around a primitve, just like Number or Boolean. And just like
new Boolean(true) === new Boolean(true)
will be false, your comparison will fail too. You have to take out the primitive for comparison:
post._id.valueOf() === user._id.valueOf()

Categories

Resources