Ideal way to store multiple texts in mongoose with Node JS? - javascript

I've been building a mongoose schema for texts that will be displayed across different pages, and it has end point to POST data for updating the texts.
For example, I would like to store text messages that will be displayed/updated in About Page and Contact Page
What would be the preferred way of designing the text model?
1) Model that has all messages stored in one data object
In front-end, the parent component fetches all text messages with Texts.findOne() and trickles down to pages that need it
const textsSchema = new Schema(
{
aboutMessage1: {
type: String,
required: true
},
aboutMessage2: {
type: String,
required: true
},
contactMessage1: {
type: String
},
contactMessage2: {
type: String
}
},
{ timestamps: true }
);
2) Model that contains each message--so it will have multiple objects
In fron-end, each page uses Text.findById(textId) to retrieve each message
const textSchema = new Schema(
{
// Example: name = contactMessage
name: {
type: String
},
message: {
type: String
}
},
{ timestamps: true }
);
3) Multiple models that contains texts for each page
Similar to 1) approach, texts get fetched with Texts.findOne(), but performed in each page
const aboutTextsSchema = new Schema(
{
message1: {
type: String,
required: true
},
message2: {
type: String,
required: true
},
},
{ timestamps: true }
);
const contactTextsSchema = new Schema(
{
message1: {
type: String,
},
message2: {
type: String,
},
},
{ timestamps: true }
);

The most promising option is the second one. Because first and third options are static, and if in the future, you need to add a new page or or a new message to an existing page, it will require changes in the mongoose model, and deployment for API.
But I think, instead of creating a text schema, it would better to create a page schema for your scenario.
Here I embed messages inside the page schema.
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const pageSchema = new Schema(
{
page: {
type: String
},
messages: [
new Schema({
name: {
type: String
},
message: {
type: String
}
})
]
},
{ timestamps: true }
);
module.exports = mongoose.model("Page", pageSchema);
Now we can use this post route to create a page:
router.post("/pages", async (req, res) => {
const result = await Text.create(req.body);
res.send(result);
});
We can create a page and its messages using the previous post route.
Request Body:
{
"_id": "5e4937e9e2454a2c0c162890",
"page": "About",
"messages": [
{
"_id": "5e4937e9e2454a2c0c162892",
"name": "Abou1",
"message": "About1 message..."
},
{
"_id": "5e4937e9e2454a2c0c162891",
"name": "Abou2",
"message": "About2 message..."
}
],
"createdAt": "2020-02-16T12:39:05.154Z",
"updatedAt": "2020-02-16T12:39:05.154Z",
"__v": 0
}
Response:
{
"_id": "5e4937e9e2454a2c0c162890",
"page": "About",
"messages": [
{
"_id": "5e4937e9e2454a2c0c162892",
"name": "Abou1",
"message": "About1 message..."
},
{
"_id": "5e4937e9e2454a2c0c162891",
"name": "Abou2",
"message": "About2 message..."
}
],
"createdAt": "2020-02-16T12:39:05.154Z",
"updatedAt": "2020-02-16T12:39:05.154Z",
"__v": 0
}
If later we want to add a message to a page we can use the following put route.
router.put("/pages/:id", async (req, res) => {
const result = await Page.findByIdAndUpdate(
req.params.id,
{
$push: { messages: req.body }
},
{ new: true }
);
res.send(result);
});
Request Body:
{
"name": "Abou3",
"message": "About3 message..."
}
Response:
{
"_id": "5e4937e9e2454a2c0c162890",
"page": "About",
"messages": [
{
"_id": "5e4937e9e2454a2c0c162892",
"name": "Abou1",
"message": "About1 message..."
},
{
"_id": "5e4937e9e2454a2c0c162891",
"name": "Abou2",
"message": "About2 message..."
},
{
"_id": "5e493926f905ab3300106f94",
"name": "Abou3",
"message": "About3 message..."
}
],
"createdAt": "2020-02-16T12:39:05.154Z",
"updatedAt": "2020-02-16T12:44:22.763Z",
"__v": 0
}
When client needs a page's messages, all we need to do is retrieving the page by it's id or page name:
router.get("/pages/id/:id", async (req, res) => {
const result = await Page.findById(req.params.id);
res.send(result);
});
//or
router.get("/pages/name/:name", async (req, res) => {
const result = await Page.findOne({ page: req.params.name });
res.send(result);
});

Related

How to receive object information from within an array in mongoose?

I have two models, a posts model and a category model where I have an array that stores posts by objectId
Category Model
const mongoose = require('mongoose');
const CategorySchema = new mongoose.Schema(
{
name: {
type: String,
required: true,
},
color: {
type: String,
required: true,
},
posts: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Post',
required: false,
}],
createdAt: {
type: Date,
default: Date.now,
}
},
{ timestamps: true }
);
module.exports = mongoose.model("Category", CategorySchema);
Post model
const mongoose = require('mongoose');
const PostSchema = new mongoose.Schema(
{
title: {
type: String,
required: true,
},
img: {
type: String,
required: true,
},
category: {
type: mongoose.Schema.Types.ObjectId,
ref: "Category",
},
desc: {
type: String,
required: false,
},
createdAt: {
type: Date,
default: Date.now,
}
},
{ timestamps: true }
);
module.exports = mongoose.model("Post", PostSchema);
So I created a get by id category controller
const Category = require('../../models/Category');
class FindCategory {
async find(req, res) {
const { id } = req.params;
try {
const category = await Category.findById(id);
return res.status(200).json(category);
} catch (err) {
return res.status(500).json(err);
}
}
}
module.exports = new FindCategory();
The problem is that when I make this request in my postman, it returns me for example
{
"_id": "63ac925d872445065a588f61",
"name": "Games",
"color": "#ff914d",
"posts": [
"63ac9cbccec4d9f35c4f4d1f"
],
"createdAt": "2022-12-28T19:00:45.847Z",
"updatedAt": "2022-12-28T19:49:47.119Z",
"__v": 0
}
But I would like to render the information of each post inside the "posts" array, something like that for example
{
"_id": "63ac925d872445065a588f61",
"name": "Games",
"color": "#ff914d",
"posts": [
"name": "red dead",
"img": "example.png",
"category": "games",
"desc": "test,
],
"createdAt": "2022-12-28T19:00:45.847Z",
"updatedAt": "2022-12-28T19:49:47.119Z",
"__v": 0
}
You should use the populate function provided by mongoose: https://mongoosejs.com/docs/populate.html#setting-populated-fields
Category.
findById(...).
populate('posts')

Fetching Date Data from DB returns undefined or null

I am willing to pass the dates data from my MongoDB to the antD Date Picker as disabled dates but after fetching the data of date, I get either undefined or null.
What is the approach here?
Below can be seen that my DB has date as a string converted to ISOString, but on fetching it via MongoDB, I get either undefined or null.
My main logic was to pull this date info as response.data.data in frontend after page-loading and passing this data to a useState of setUnavailable date and then assigning this setState to the disableddate function to get a boolean.
Here is my code:
The data in my DB is:
`{
"_id": {
"$oid": "63690afb0623f513b3f22704"
},
"userId": "63614c627330d358d7ceff2d",
"name": "SampleName",
"title": "Doctor",
"type": "Lab Consultation",
"description": "Help Me Please",
"timings": "2022-11-07T06:00:00.000Z",
"date": "2022-11-15T00:00:00.000Z",
"status": "pending",
"createdAt": {
"$date": {
"$numberLong": "1667828475559"
}
},
"updatedAt": {
"$date": {
"$numberLong": "1667828475559"
}
},
"__v": 0
}
`
Schema:
`const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const consultationSchema = new mongoose.Schema(
{
createdBy: {
type: mongoose.ObjectId,
ref: "userModel",
},
userId: {
type: String,
required: true,
},
name: {
type: String,
required: true,
},
title: {
type: String,
required: true,
},
type: {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
timings: {
type: String,
required: true,
},
date: {
type: String,
required: true,
},
status: {
type: String,
required: false,
default: "pending",
},
},
{
timestamps: true,
}
);
const consultationModel = mongoose.model("consultations", consultationSchema);
module.exports = consultationModel;
`
Router Get Data:
`router.get("/get-all-dates", authMiddleware, async (req, res) => {
try {
const consultation = await Consultation.find({
date: { $gte: "2022-11-11T00:00:00.000Z" },
});
const dates = consultation.date;
res.status(200).send({
message: "All Dates Fetched Successfully",
success: true,
data: dates,
});
console.log(dates);
} catch (error) {
console.log(error);
res.status(500).send({
message: "Fetching Dates Failed",
success: false,
error,
});
}
});
`
API Call in React:
`const getDateData = async () => {
try {
dispatch(showLoading());
const response = await axios.get("/api/user/get-all-dates", {
headers: {
Authorization: "Bearer " + localStorage.getItem("token"),
},
});
dispatch(hideLoading());
if (response.data.success) {
setUnavailableDates(response.data.data);
console.log(response.data.data);
console.log(setUnavailableDates);
}
} catch (error) {
dispatch(hideLoading());
}
};
useEffect(() => {
getDateData();
}, []);
const disabledDate = (current) => {
// How to pass the dates fecthed from DB here? Direct with setUnavailableDates?
return current && current < moment().endOf("day");
};
`
The output in network is this:
`{
"message": "All Dates Fetched Successfully",
"success": true
}`
The output in console is: undefined

Mongoose ignoring field while trying to save

I am trying to insert a new entry in my collection, the problem is that it ignores both doctorId and patientId while I'am sure they are not undefined. I tried to change the fields types in the definition to strings just to test whether they would be inserted and it still no use.
schema:
const RdvSchema = new mongoose.Schema({
patientId: {
type: mongoose.Schema.Types.ObjectId,
ref: "patients"
},
doctorId: {
type: mongoose.Schema.Types.ObjectId,
ref: "doctors"
},
createdAt: {
type: Date,
default: () => Date.now()
},
updatedAt: {
type: Date,
default: () => Date.now()
},
urgent: {
type: Number,
default: () => false
},
date: Date,
period: String,
description: String
})
the function saving the document:
const createRdv = async (req, res) => {
try {
console.log("patient id: ", req.body.patientId)
let rdv = new Rdv({
"patientId": req.body.patientId,
"doctorId": req.body.doctorId,
"description": req.body.description,
"urgent": req.body.urgent,
"date": req.body.date,
"period": req.body.period
})
await rdv.save(async (err, rdv) => {
if (err) {
console.log(err)
return res.status(500).send(false)
}
try {
await DoctorRepository.addRdv(req.body.doctorId, rdv._id)
await PatientRepository.addRdv(req.body.patientId, rdv._id)
} catch (message) {
console.log(message)
res.status(500).send(false)
}
})
res.status(200).send(true)
} catch (ex) {
res.status(500).send(false)
}
}
The inserted document:
{
"_id": {
"$oid": "6269603d5f0e45e53a470f50"
},
"urgent": 3,
"date": {
"$date": {
"$numberLong": "1653433200000"
}
},
"period": "matin",
"description": "this is a description",
"createdAt": {
"$date": {
"$numberLong": "1651073085661"
}
},
"updatedAt": {
"$date": {
"$numberLong": "1651073085661"
}
},
"__v": 0
}
update: for some reason An old document keeps getting inserted the document has nothing to do with what I was trying to insert. The document is one I had inserted previously through a test using Mocha

Mongoose: res.json returns array with null records when "toObject" is used. Why and how can I solve this?

I'm currently trying to setup an API where I can fetch an array of objects that are assigned to a userId. I have defined the following Mongoose Model:
const userSchema = mongoose.Schema({
name: { type: String, required: true, trim: true },
email: { type: String, required: true, unique: true, trim: true },
password: { type: String, required: true, minlength: 6 },
engagements: [
{ type: mongoose.Types.ObjectId, required: true, ref: "Engagement" },
],
});
And have setup a controller like this:
exports.getEngsByUid = async (req, res, next) => {
const uid = req.params.uid;
let uidWithEngs;
try {
uidWithEngs = await User.findById(uid).populate("engagements");
} catch (err) {
const error = new HttpError("Fetching engagements failed!", 500);
return next(error);
}
if (!uidWithEngs || uidWithEngs.engagements.length === 0) {
return next(new HttpError("Could not find engagements for this user", 500));
}
res.json({
engagements: uidWithEngs.engagements.map((e) => {
e.toObject();
}),
});
};
The problem I am currently facing, is that when I send a GET requests to my defined path, I fetch an array of 'engagements' with null values:
{
"engagements": [
null,
null
]
}
However, if I remove the toObject method (and required .map method) and setup the res as follows:
res.json({
engagements: uidWithEngs.engagements,
});
I do get my array:
{
"engagements": [
{
"_id": "5fb47975eb319d24c098a06d",
"title": "testTitle1",
"client": "testClient1",
"year": 2030,
"owner": "5fb4795deb319d24c098a06b",
"__v": 0
},
{
"_id": "5fb4698aeb319d24c098a06f",
"title": "testTitle2",
"client": "testclient2",
"year": 2040,
"owner": "5fb4795deb319d24c098a06b",
"__v": 0
}
]
}
Why does the use of toObject() return an array with 2 null records and when I don't use the toObject() method, it returns 2 complete records? How can I solve this?
Thank you|
change res.json(...) to basically the short hand for arrow functions and it will work.

Mongoose populate does not populate array

I have struggled with the mongoose.model.populate function for hours now. I have even tried directly copying and pasting several solutions without luck.
I have a User model which is supposed to contain an array of 'Dilemmas' which he/she has created, but I have been unable to populate it.
Here are the models as well as the implementation of populate().
User.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
// Create Schema
const UserSchema = new Schema({
username: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
},
dilemmas: [
{
type: Schema.Types.ObjectId,
ref: "Dilemma"
}
]
});
module.exports = User = mongoose.model("User", UserSchema, "users");
Dilemma.js
const mongoose = require("mongoose");
const slug = require("mongoose-slug-generator");
const Schema = mongoose.Schema;
mongoose.plugin(slug);
// Create Schema
const DilemmaSchema = new Schema({
creator: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
title: {
type: String
},
slug: {
type: String,
slug: "title"
},
red: {
type: String,
required: true
},
blue: {
type: String,
required: true
},
red_votes: {
type: Number,
default: 0,
required: true
},
blue_votes: {
type: Number,
default: 0,
required: true
},
likes: [
{
user: {
type: Schema.Types.ObjectId,
ref: "User"
}
}
],
comments: [
{
user: {
type: Schema.Types.ObjectId,
ref: "User"
},
text: {
type: String,
required: true
},
author: {
type: String
},
avatar: {
type: String
},
date: {
type: Date,
default: Date.now
}
}
],
date: {
type: Date,
default: Date.now
}
});
module.exports = Dilemma = mongoose.model("Dilemma", DilemmaSchema, "dilemmas");
Routes.js
// #route GET api/users/profile
// #desc Gets logged in user's profile
// #access Private
router.get(
"/profile",
passport.authenticate("jwt", { session: false }),
(req, res) => {
User.find({ username: req.user.username })
.populate("dilemmas")
.then(user => {
if (!user) {
errors.nouser = "There is no user";
return res.status(404).json(errors);
}
res.json(user);
})
.catch(err => res.status(400).json(err));
}
);
JSON Response
[
{
"_id": "5b807beef770e7c7e6bf7ce0",
"dilemmas": [],
"username": "Jonas",
"email": "Mohrdevelopment#gmail.com",
"password": "$2a$10$QaqljS9x08YQ9N9EuCBTpO114ZJUFuVxAV80xMzImNi8eW2frPg0C",
"date": "2018-08-24T21:43:10.411Z",
"__v": 0
}
]
JSON Dilemmas response
[
{
"red_votes": 0,
"blue_votes": 0,
"_id": "5b80975f6e47fecba621f295",
"user": "5b807beef770e7c7e6bf7ce0",
"title": "Am i the real author asdsdasd?",
"red": "This is the red dilemma",
"blue": "This is the blue dilemma",
"likes": [],
"comments": [],
"date": "2018-08-24T23:40:15.381Z",
"slug": "am-i-the-real-author-asdsdasd",
"__v": 0
},
{
"red_votes": 0,
"blue_votes": 0,
"_id": "5b808e789bc36bcae8c6c3ad",
"creator": "5b807beef770e7c7e6bf7ce0",
"title": "Am i the real author?",
"red": "This is the red dilemma",
"blue": "This is the blue dilemma",
"likes": [],
"comments": [],
"date": "2018-08-24T23:02:16.565Z",
"slug": "am-i-the-real-author",
"__v": 0
}
]
JSON Users response
{
"_id": {
"$oid": "5b807beef770e7c7e6bf7ce0"
},
"dilemmas": [],
"username": "Jonas",
"email": "Mohrdevelopment#gmail.com",
"password": "$2a$10$QaqljS9x08YQ9N9EuCBTpO114ZJUFuVxAV80xMzImNi8eW2frPg0C",
"date": {
"$date": "2018-08-24T21:43:10.411Z"
},
"__v": 0
}
I just encountered a similar issue myself. Populating a ref worked, but populating an array of refs did not. I was able to get the array populate to work by explicitly specifying the model name in the populate call, e.g.:
User.find({ ... }).populate({
path: 'dilemmas',
model: 'Dilemma',
});
I don't know why this makes a difference, when the name of the referenced model is already specified in the schema.
Have you tried this?
User.find({ username: req.user.username })
.populate("dilemmas")
.exec() // <-- add exec() to perform the search
.then(user => {
...
})
Did you check the documentation here?
https://mongoosejs.com/docs/populate.html#refs-to-children
It shows a similar setup (with Authors and Stories.) It mentions 'pushing' stories to be able to use a find / populate combo.

Categories

Resources