How do I use MongoDB data with D3 and Node? - node.js

Using Node, Express, Mongoose, and MongoDB, I'm trying to send a GET request to MongoDB and then use the data as an object for a D3 graph. I have the code for the graph in a separate js file. How do I send the data to that file?
Here are the basic Schemas I'm using:
var hikeSessionSchema = new mongoose.Schema({
hike_name: {type: String, required: true},
hike_date: {type: String, required: true},
mileage: {type: Number, required: true},
duration: {type: String, required: true},
elevation_gain: {type: Number, required: true},
city: {type: String, required: true},
location: {type: String, required: true},
notes: String
})
var hikerSchema = new mongoose.Schema({
username: {type: String, required: true},
log: [hikeSessionSchema]
})
var HikeSession = mongoose.model('HikeSession', hikeSessionSchema)
var Hiker = mongoose.model('Hiker', hikerSchema)
So I want to find the user by ID, then get an array of the user's HikeSessions. And I want to move that data to a js doc to make a d3 graph (I've already made the d3 app on CodePen but connected it to Google Sheets before).
I think the GET request should be something like this, but I don't know what to do with the result. The path will probably change:
app.get('/:id', (req, res) => {
const id = req.params.id;
Hiker.findById(id)
.then(result => {
//What goes here?
})
.catch(err => {
console.log(err);
})
}
After I get the data into the d3 doc, I'm not sure what challenges I'll run into with rendering it.

Step 1:
Define and export your d3_generator(input) function in d3.js:
const d3_generator = (input) => {
/// (Your logic)
}
module.exports = { d3_generator };
Step 2
Import d3_generator function from d3.js in your controller and use it after you got result from MongoDB:
const { d3_generator } = require('./d3');
...
app.get('/:id', (req, res) => {
const id = req.params.id;
Hiker.findById(id)
.then(result => {
let generated_d3 = d3_generator(result.log);
})
.catch(err => {
console.log(err);
})
}

Related

empty result when getting data from mongodb with mongoose and node js

I'm trying to fetch all my documents from a collection in my MongoDb DB but I can't do it. I have absolutely no error but it just returns an empty array in my console.
Model :
import mongoose from "mongoose";
const websiteSchema = mongoose.Schema({
id: {type: String},
name: {type: String, required: true},
url: {type: String, required: true},
note: {type: Number, required: true}
});
export default mongoose.model("Website", websiteSchema);
File in which I want to use the data :
import express from 'express';
import Website from '../models/website.js';
const routerWebsiteList = express.Router();
routerWebsiteList.get('/website-list', async function(req, res, next) {
try {
const websitesToCrawl = await Website.find({});
console.log(websitesToCrawl);
} catch (error) {
res.status(500).json({message : "An error occured"});
}
})
export default routerWebsiteList;
All I get in my console is [].
My data in my database seems good too :
Accully everything looking fine maybe you can use
require for import files
const name = require('name')
and for the export file :
module.exports = routerWebsiteList
after that be sure you connected your database correct like this
async function database() {
await mongoose
.connect(process.env.DATABASE_URL)
.then(() => {
console.log("Database Coonected");
})
.catch((err) => {
console.log(err);
});
}
database()
Last detail is you don't need to put id on mongoose schema just remove it and finally change your model like this
const mongoose = require("mongoose");
const websiteSchema = mongoose.Schema({
name: {type: String, required: true},
url: {type: String, required: true},
note: {type: Number, required: true}
});
const Website = mongoose.model("Website", websiteSchema);
module.exports = Website
I hope that will be helpful. If you will be stuck somewhere feel free to type here again.
I finally found what was the problem. The mongoose Schema was looking in a wrong collection named "websites". All I had to do was to specified the collection name in my model as following : ("websitesToCrawl" is the collection name)
export default mongoose.model("Website", websiteSchema, "websitesToCrawl");

How to use MongoDB Subdocument _id with EJS

I'm trying to get a subdocument from MongoDB using Mongoose, then pass the data to an EJS doc but I'm getting an Uncaught SyntaxError: Invalid or unexpected token because of the _id object. Ideally I would like the _id to be a string, but using toString() or toHexString() doesn't work.
Here are my basic Schemas:
var hikeSessionSchema = new mongoose.Schema({
hike_name: {type: String, required: true},
hike_date: {type: String, required: true},
mileage: {type: Number, required: true}
})
var hikerSchema = new mongoose.Schema({
username: {type: String, required: true},
log: [hikeSessionSchema]
})
var HikeSession = mongoose.model('HikeSession', hikeSessionSchema)
var Hiker = mongoose.model('Hiker', hikerSchema)
And here's my GET request:
app.set('view engine', 'ejs');
...
app.get('/users/:id/:hike', (req, res) => {
var id = req.params.id;
var hikeId = req.params.hike;
Hiker.findById(id, (err, hiker) => {
const hike = hiker.log.id(hikeId)
console.log(hike)
res.render('editHike', { data: hike })
})
When I console.log(hike) I get:
{
_id: 6125629447cec024c067c1da,
hike_name: 'Hike Name',
hike_date: '2021-08-04',
mileage: 10
}
I've tried saving the _id to a variable as a string, then replacing the _id with the variable, but it goes back to an object. I've also tried delete hike._id but that doesn't work either.
I need to be able to be able to reference the _id in the EJS doc for later use. Maybe there's a way to get the HikeSession object without the _id and then save the _id separately? How can I make this work? Thanks
I came up with a workaround by duplicating the object without the _id field, making a variable for turning _id into a string, then adding that variable to the new object.
app.get('/users/:id/:hike', (req, res) => {
var id = req.params.id;
var hikeId = req.params.hike;
Hiker.findById(id, (err, hiker) => {
var hikeData = hiker.log.id(hikeId)
var hike = (({ hike_name, hike_date, mileage }) => ({ hike_name, hike_date, mileage }))(hikeData);
var idStr = hikeData._id.toString()
hike.id = idStr;
res.render('editHike', { data: hike })
})
Then importing in the EJS file like this:
var data = <%- JSON.stringify(data) %>;
There's probably a better way to do it, but this worked.

How to bind array of values to nodeJs model?

I am developing RESTful API.
Model
var productsSchema = new Schema({
productName:{type: String, required: true},
productDescription: {type: String, required: true},
produtThumbnail:{type: String},
productStock: [
{
size: {type: Number, required: false},
price: {type: Number, required: false}
}],
productTags:[{
tag: {type: String, required: false}
}]
});
POST Method
router.post('/api/new-product',upload.any(),function(req, res, next){
console.log(req.body);
if(req.files){
req.files.forEach(function(file){
var filename = (new Date()).valueOf() + '-' + file.originalname;
fs.rename(file.path,'public/images/'+ filename, function(err){
if (err) throw err;
console.log('file Uploaded');
//Save to mongoose
var product = new Products({
productName: req.body.productName,
productDescription: req.body.productDescription,
size: req.body.productStock.size,
productThumbnail: filename
});
product.save(function(err, result){
if(err){}
res.json(result);
});
});
});
}
}) <br/>
Problem:
I am able to bind object data to model, but I don't know how to bind array-data to model.
For example
var product = new Products({
productName: req.body.productName,
productDescription: req.body.productDescription,
size: req.body.productStock.size,// This line doesn't work
productThumbnail: filename
});
size:req.body.productStock doesn't work
So, How can I bind array-data to model, and then save it to mongodb?
Please help..
Just change it to type array
var productsSchema = new Schema({
merchantId: {type: String, required: false},
produtThumbnai:{type: String},
productStock: {type: Array},
productTags:{type: Array}
});
It Works!!
Maybe it's just because of a typo, because your Schema looks OK. Change this:
var product = new Products({
// ...
size: req.body.productStock.size,
// ...
});
To this:
var product = new Products({
// ...
productStock: req.body.productStock,
// ...
});
For this to work you should be passing your productStock as an array of objects from your POST request (!)
Otherwise it's a bit difficult to offer you more help, since we are unaware of what req.body actually is.
EDIT
To check if the above works (and assuming you are passing productStock as a whole array of objects from Postman/Curl/whatever), do the following and paste the result here:
product.productStock.foreach( (product, idx) => console.log(`(${idx}):${product.id}, `) )

Mongoose pre-save hook fires, but does not persist data

I am encountering a problem where my Mongoose pre.save() hook is firing, but the attribute does not get saved to the database. I have been searching for a long time without finding an answer.I found this thread, and the behaviour I am experiencing is very similiar, but OP's problem is related to the context of this, whereas I seem to have a different problem.
Here is my models.js:
'use strict';
const mongoose = require("mongoose");
const slugify = require("slugify");
let Schema = mongoose.Schema;
let BlogPostSchema = new Schema({
title: {
type: String,
required: true
},
createdAt: {type: Date, default: Date.now},
updatedAt: {type: Date, default: Date.now},
author: String,
post: {
type: String,
required: true
}
});
BlogPostSchema.pre('save', function(next) {
this.slug = slugify(this.title);
console.log(this.slug);
next();
});
// Passed to templates to generate url with slug.
BlogPostSchema.virtual("url").get(function() {
console.log(this.slug);
console.log(this.id);
return this.slug + "/" + this.id;
});
BlogPostSchema.set("toObject", {getters: true});
let BlogPost = mongoose.model("BlogPost", BlogPostSchema);
module.exports.BlogPost = BlogPost;
And here is the relevant lines in the router file index.js:
const express = require('express');
const router = express.Router();
const BlogPost = require("../models").BlogPost;
// Route for accepting new blog post
router.post("/new-blog-post", (req, res, next) => {
let blogPost = new BlogPost(req.body);
blogPost.save((err, blogPost) => {
if(err) return next(err);
res.status(201);
res.json(blogPost);
});
});
I am able to save the blog post to the database, and my console.log's correctly prints out the slug to the console. However, the this.slug in the pre-save hook does not get persisted in the database.
Can anybody see what the problem is here? Thank you so much in advance.
Mongoose will act according to the schema you defined.
Currently, your schema does not contain s filed named slug.
You should add a slug field to your schema.
Changing your current schema to something like this should work:
let BlogPostSchema = new Schema({
slug: String,
title: {
type: String,
required: true
},
createdAt: {type: Date, default: Date.now},
updatedAt: {type: Date, default: Date.now},
author: String,
post: {
type: String,
required: true
}
});

Retrieve Array in Subdocument MongoDB

I have a Users model structure somewhat like this:
const userSchema = new mongoose.Schema({
email: { type: String, unique: true },
password: String,
todosDo: [models.Do.schema],
}
And the child "Do" schema somewhat like this (in a different file):
const doSchema = new mongoose.Schema({
name: {type: String, default : ''},
user: {type: mongoose.Schema.ObjectId, ref: 'User'},
createdAt: {type : Date, default : Date.now}
});
And I'm trying to figure out how to retrieve the todosDo array for the signed in user. This is what I've got so far:
// Get all "Do" todos from DB
// Experimenting to find todos from certain user
User.findById(req.user.id, function(err, user){
if(err){
console.log(err);
} else {
doTodos = user.todosDo, // this obviously doesn't work, just an idea of what I was going for
console.log(doTodos);
finished();
}
});
Am I referencing the child/parent wrong or am I just not retrieving the array right? Any help is greatly appreciated!
As far I guess you may want to edit as raw js objects so you need to use lean() function. without using lean() function user is mongoose object so you can't modify it.
can try this one:
User.findById(req.user.id)
.lean()
.exec(function (err, user) {
if(err){
console.log(err);
return res.status(400).send({msg:'Error occurred'});
}
if(!user) {
return res.status(400).send({msg:'User Not found'});
}
doTodos = user.todosDo;
console.log(user.todosDo); // check original todos
console.log(doTodos);
return res.status(200).send({doTodos : doTodos }); // return doTodos
});
and to refer child schema in parent schema from different model you can access a Model's schema via its schema property.
say in doSchema.js file
const doSchema = new mongoose.Schema({
name: {type: String, default : ''},
user: {type: mongoose.Schema.ObjectId, ref: 'User'},
createdAt: {type : Date, default : Date.now}
});
module.exports = mongoose.model( 'DoSchema', doSchema );
in user.js file
var DoModel = require('./doSchema');// exact path
const userSchema = new mongoose.Schema({
email: { type: String, unique: true },
password: String,
todosDo: [DoModel.schema],
}
Thanks for your help everybody! My problem was that I needed to push all the newly created todos in the post route to todosDo, so then I could retrieve them at the get route. Everything's working now!

Resources