MongoDB + Express How to Optimize the code? - mongodb

Hello everybody i have this bad code for me how i can optimize it ?
If i used SQL i can do used inner Queries in one query...
"User" it's only object from mongoose
getProfile: async (req, res) => {
const { id } = req.params;
try {
const {
image,
name,
gender,
about,
email,
phone,
address
} = await User.findById({ _id: id }).select('image name gender about email phone address');
const subscriptions = await Subscriber.countDocuments({ userId: id });
const subscribers = await Subscriber.countDocuments({ subscriberId: id });
const user = {
image,
name,
gender,
subscriptions,
subscribers,
about,
email,
phone,
address
};
res.json(user);
} catch (err) {
console.log(err);
}
}
PS.
I only study with this technologies
If i used spread operator of result of my query from User i have like this:
And that what i have in result
module.exports = {
getProfile: async (req, res) => {
const { id } = req.params;
try {
const [data, subscriptions, subscribers] = await Promise.all([
User.findById( { _id: id },
{
__v: false,
password: false,
date: false,
_id: false
},
),
Subscriber.countDocuments({ userId: id }),
Subscriber.countDocuments({ subscriberId: id })
])
const user = {
...data._doc,
subscriptions,
subscribers
}
res.json(user);
} catch (err) {
console.log(err);
}
}

Since all of your queries are independent, the best we can do is execute all of them parallelly with Promise.all(). Try something like this:
getProfile: async (req, res) => {
const { id = _id } = req.params;
try {
const getUser = User.findById({ _id }).select('image name gender about email phone address');
const getSubscriptions = Subscriber.countDocuments({ userId: id });
const getSubscriber = Subscriber.countDocuments({ subscriberId: id });
const [userData, subscriptions, subscribers] = await Promise.all([getUser, getSubscriptions, getSubscriber]);
const user = {
...userData,
subscriptions,
subscribers,
};
res.json(user);
} catch (err) {
console.log(err);
}
}
Hope this helps :)

You can embed subscriptions [array of documents] in the User model. But bear in mind that could put limitations on your api, if subscriptions might be accessed regardless of its user.

Related

How find in mongoose by an array property

I have defined my Conversation scheme like this:
const { Schema, model } = require("mongoose");
const ConversationSchema = Schema(
{
members: {
type: Array,
},
},
{ timestamps: true }
);
module.exports = model("Conversation", ConversationSchema);
My problem is that when I want to create a conversation model I search first if there is already a conversation.
const newConversation = async (req, res = response) => {
try {
const { senderId, receiverId } = req.body;
const conversation = await Conversation.find({
members: { $in: [senderId, receiverId] },
});
if (conversation.length === 0) {
const dbConversation = new Conversation({
members: [senderId, receiverId],
});
await dbConversation.save();
return res.status(201).json({
ok: true,
conversation: dbConversation
});
} else {
return res.status(403).json({
ok: false,
msg: "Conversation already exist",
});
}
} catch (err) {
return res.status(500).json({
ok: false,
msg: "Please contact with administrator",
});
}
};
senderId and receivedId are the ids of the users that are in that conversation, but it doesn't work.
How can I make it check if there is already a conversation with both ids?
Per the comments, we came to understand that the thing that wasn't working about the current code was always taking the code path that returned the message that the "Conversation already exist". This meant that the following query was always returning data:
const conversation = await Conversation.find({
members: { $in: [senderId, receiverId] },
});
The logic here does not match the logic implied in the question. This syntax uses the $in operator to find documents whose members array has at least one of the values passed to it (here the senderId and the receiverId).
To instead find documents where both of those people are present in the members array, you want to use the $all operator instead:
const conversation = await Conversation.find({
members: { $all: [senderId, receiverId] },
});
Working Mongo Playground example here.

Prisma: how to write transaction where results from one query are used by another query

I'm working on a project with Next.js and Prisma. In one of my API routes, I have a three queries. The results of the first and second queries are used in the third query. I'd like to do all three operations as a transaction and then return the data from the first query in the response.
I'm familiar with using prisma.$transaction but I don't know how to write it in this case where results #1 and #2 are used by query #3. Here are the queries as they are written now. Thanks in advance!
const { boardId } = req.body
const { description, status, title } = req.body.task
const createTask = await prisma.task.create({
data: {
board: boardId,
description,
status,
title
}
})
const statusArray = await prisma.board.findUnique({
where: {
id: boardId
},
select: {
[status]: true
}
})
const updateBoardStatusArray = await prisma.board.update({
where: {
id: boardId
},
data: {
[status]: {
set: [...statusArray[status], createTask.id]
}
}
})
// return data from first query
res.status(201).json({task: createTask})
Here you go:
const { boardId } = req.body;
const { description, status, title } = req.body.task;
const [createTask] = await prisma.$transaction(async (prisma) => {
const createTask = await prisma.task.create({
data: {
board: boardId,
description,
status,
title,
},
});
const statusArray = await prisma.board.findUnique({
where: {
id: boardId,
},
select: {
[status]: true,
},
});
const updateBoardStatusArray = await prisma.board.update({
where: {
id: boardId,
},
data: {
[status]: {
set: [...statusArray[status], createTask.id],
},
},
});
return [createTask, statusArray, updateBoardStatusArray];
});
// return data from first query
res.status(201).json({ task: createTask });
You can learn more about Interactive Transaction here

Adding multiple items to mongodb

I have this schema with
const userSchema = new mongoose.Schema(
{
skills: [{ name: { type: String, unique: true }, level: { type: Number } }],
and I am trying, after getting an array of objects from the client, to add all of them at once under a user in MongoDB
my old implementation when it was only an array is this one below. I have no idea how to go about it now tho. Could anyone help me?
const { email } = session;
const { skill } = req.body;
if (req.method === 'POST') {
try {
const user = await User.findOne({ email });
const updatedUser = await User.findOneAndUpdate(
{ email },
{ skills: [...user.skills, { name: skill }] }
);
const { email } = session;
// object from the client
const { skills } = req.body;
if (req.method === 'POST') {
try {
const updatedUser = await User.findOneAndUpdate(
{ email },
{ $set: {skills} }
);
the best is getting the array correctly formatted from front-end, if not use a map before call findOneAndUpdate

Update email only if it doesn't already exist in mongoDB database

I'm using this route to allow a user to change their email address. However, it currently lets them add an email address that is already used by another user. How can I prevent this (and send an alert in this situation). Also, I'm wondering if .findOneAndUpdate() is appropriate here as it may stop after finding the first one.
Thanks
app.post('/changeUserEmail', function (req, res) {
db.collection("userDB").findOneAndUpdate(
{username: req.user.username},
{ $set: {email: req.body.newEmail}},
{new: true},
(err, data) => {
if (err) {
console.log(err);
}
console.log(null, data);
}
)
res.render(process.cwd() + "/views/options", {
username: req.user.username,
email: req.body.newEmail,
alert: "Email changed"
});
});
You could first check if the email exists before you update something
app.post("/changeUserEmail", async function(req, res) {
let { username } = req.user;
let { newEmail } = req.body;
let emailExist = await db.collection("userDB").findOne({ email: newEmail });
if (emailExist)
return res.render(process.cwd() + "/views/options", {
alert: "Email already exists"
});
await db
.collection("userDB")
.findOneAndUpdate(
{ username },
{ $set: { email: newEmail } },
{ new: true }
);
res.render(process.cwd() + "/views/options", {
username,
email,
alert: "Email changed"
});
});

How to find all users except current user with Mongoose

I am trying to display a list of users but the logged-in user shouldn't see himself in the list. I can't make the request to get all users but current user to work.
router.get("/", auth, async (req, res) => {
try {
const users = await User.find({ user: { $ne: req.user.id } }).select([
"email",
"username",
"bio"
]);
res.json(users);
} catch (err) {
console.error(err.message);
res.status(500).send("Servor Error");
}
});
module.exports = router;
This request below gets the current user and it works.
router.get("/", auth, async (req, res) => {
try {
const user = await User.findOne({
user: req.user.id
});
if (!user) {
return res.status(400).json({ msg: "There is no profile for this user" });
}
res.json(user);
} catch (err) {
console.error(err.message);
res.status(500).send("Servor error");
}
});
module.exports = router;
You need to use _id field inside the query filter instead of user:
const users = await User.find({ _id: { $ne: req.user.id } }).select([
"email",
"username",
"bio"
]);