Mongoose use user input to find results in DB with express.js? - node.js

I'm new to mongoose/mongo and express so I'm not quiet sure how to go about this. So basically I'm trying to use checkboxes to sort of "filter" results in my database.
Here's an example of something in my db:
{ "location" : "PHARMACY",
"region" : "SOCAL",
"system" : "WITS",
"contact" : "SMITH",
"membership" : "TIER1" }
So far I've only figured out how to hard code it like this, where I do Environment.find({region: "SOCAL"}) and it spits out all the db entries that contain SOCAL as a region:
var express = require("express"),
app = express(),
mongoose = require("mongoose"),
bodyParser = require("body-parser");
mongoose.connect("mongodb://localhost/epims", {
useNewUrlParser: true,
useUnifiedTopology: true
});
app.use(bodyParser.urlencoded({ extended: true }));
app.set("view engine", "ejs");
//schema setup
var environmentSchema = new mongoose.Schema({
name: String,
region: String,
system: String,
contact: String,
membership: String,
});
var Environment = mongoose.model("Environment", environmentSchema);
app.get("/environments/show", function(req, res) {
//find environment with selected parameters
Environment.find({region: "SOCAL"}, function(err, foundEnvironment) {
if (err) {
console.log(err);
} else {
//render show template with that env
res.render("show", { environment: foundEnvironment });
}
});
});
How do I make it so this search through the database changes based on what selections are made?
Here is my select form if anybody needs to see that as well
<form action="/environments" method="GET">
<legend>Locations</legend>
<input type="checkbox" name="location" value="PHARMACY">
<label for="PHARMACY">PHARMACY</label>
<input type="checkbox" name="location" value="OFFICE">
<label for="OFFICE">OFFICE</label>
<input type="checkbox" name="location" value="STORE">
<label for="STORE">STORE</label>
<legend>Regions</legend>
<input type="checkbox" name="region" value="SOCAL">
<label for="SOCAL">SOCAL</label>
<input type="checkbox" name="region" value="NORCAL">
<label for="NORCAL">NORCAL</label>
<input type="checkbox" name="region" value="WA">
<label for="WA">WA</label>
...
Submit
</form>

You can pass the search parameters via the search query:
/environments/show?location=OFFICE&location=STORE&region=SOCAL&region=NORCAL
The GET request produce this database query:
{
region: { '$in': [ 'SOCAL', 'NORCAL' ] },
location: { '$in': [ 'OFFICE', 'STORE' ] }
}
app.get("/environments/show", function (req, res) {
let region = [req.query.region || []].flat();
let location = [req.query.location || []].flat();
let query = {
region: {
"$in": region
},
location: {
"$in": location
}
};
Environment.find(query, function (err, foundEnvironment) {
if (err) {
console.log(err);
} else {
//render show template with that env
res.render("show", { environment: foundEnvironment });
}
});
});
<form action="/environments/show" method="GET">
<legend>Locations</legend>
<input type="checkbox" name="location" value="PHARMACY">
<label for="PHARMACY">PHARMACY</label>
<input type="checkbox" name="location" value="OFFICE">
<label for="OFFICE">OFFICE</label>
<input type="checkbox" name="location" value="STORE">
<label for="STORE">STORE</label>
<legend>Regions</legend>
<input type="checkbox" name="region" value="SOCAL">
<label for="SOCAL">SOCAL</label>
<input type="checkbox" name="region" value="NORCAL">
<label for="NORCAL">NORCAL</label>
<input type="checkbox" name="region" value="WA">
<label for="WA">WA</label>
...
<button type="submit">Submit</button>
</form>

Related

Array not passing in express to mongo

I'm trying to pass an array to MongoDB using express post request.
app.post('/createGroup', (req, res) => {
const { title, description, tenants } = req.body;
const group = {
title: title,
description: description,
tenants: tenants,
};
console.log(tenants);
const newGroup = new Group(group);
newGroup.save();
res.redirect('/'); });
Everything works just fine till here. if I log console.log(tenants) returns and array of ids
[ 'd8ef412c-7947-4500-8e7f-73157e30961e', 'a515dfa8-a272-4d95-87d2-a1a4f22175b5' ]
But the tenants are not passed to MongoDB.
Instead, I get
_id: 623639bde8f1bdfc6eab462e
title: "Nitro"
description: "Its a Group"
tenants: Array
created_at: 2022-03-19T20:14:53.268+00:00
__v: 0
Here is my Schema,
const GroupSchema = new mongoose.Schema({
title: String,
description: String,
tenants: [{type: String}],
created_at: {
type: Date,
default: Date.now
}
});
The HTML form is here.
<form class="create-group" action="/createGroup" method="POST">
<div class="form-group">
<label for="title">Group Title</label>
<input type="text" class="form-control" id="title" name="title" placeholder="Group Title / Name">
</div>
<div class="form-group">
<label for="description">Group Description</label>
<textarea class="form-control" id="description" name="description" rows="3"
placeholder="Group Description"></textarea>
</div>
<div>
<fieldset>
<legend>
Tenants
</legend>
<% tenants.forEach(function(tenant) { %>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="<%= tenant.tenantId %>" name="tenants[]">
<label class="form-check-label" for="<%= tenant.tenantId %>">
<%= tenant.tenantName %>
</label>
</div>
<% }) %>
</fieldset>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Create Group</button>
</div>
</form>
I believe the issue is in your schema, you should be defining tenants array property like this
tenants: [String]
or you can do
tenants: { type: [String] }

Update data in mongodb with input valors

I have three input to obtain three different values. Im using express.js , node.js, mongodb and ejs templates.
<form action="/save-profile/<%= user.id %>/<%= user.name %>/<%= user.lastname %>/<%= user.description %>" method="POST">
<div class="input-group mb-3">
<span class="input-group-text" id="basic-addon1">Name</span><%= user.username %>
<input type="text" class="form-control" placeholder="'John'" aria-label="Username" name="username">
<span class="input-group-text">lastName</span><%= user.lastname %>
<input type="text" class="form-control" placeholder="" aria-label="Server" name="lastname">
</div>
<div class="input-group">
<span class="input-group-text">Description:</span>
<textarea class="form-control" aria-label="With textarea" placeholder="" name="description"><%= user.description %></textarea>
</div>
</p><br>
<button class="btn btn-primary mb-10 btn-lg">Save</button>
</div>
</div>
In js file:
router.post('/save-profile', async (req, res) => {
const profile_id = await User.findById({ _id: req.body.id })
const updatedName = await User.findOneAndUpdate({ username: req.body.username})
const updatedlastname = await User.findOneAndUpdate({ apellido: req.body.lastname })
const updatedDescription = await User.findOneAndUpdate({ description: req.body.description })
console.log(profile_id,updatedName,updatedApellido,updatedDescription)
res.redirect('/profile')})
I tried to do it with a get but it didn't work
Firstly, action attribute in the form tag accepts the path where you are handling the form data. You only need to pass the user.id, there's no need to pass the other fields for this use-case.
<form action="/save-profile/<%= user.id %>" method="POST">
...
</form>
Secondly, in your route handler, the database record can be updated using only a single findOneAndUpdate call. You don't need to call it multiple times for every field if you're only going to update a single record.
The path written in action attribute will appear as /save-profile/1, for instance, in your route handler. Value preceding /save-profile/ i.e. 1 can be accessed by modifying the path in route handler as /save-profile/:id and in the callback you can get it by req.params.id
Finally you have this.
router.post('/save-profile/:id', async (req, res) => {
const response = await User.findOneAndUpdate(
{ _id: req.params.id },
{
username: req.body.username,
apellido: req.body.lastname,
description: req.body.description
},
{ new: true }
)
// Do something with response
res.redirect('/profile')
})

How to combine schema model with file upload in Node.js and MongoDB?

Good day everyone, I am building a web application to store employee information in a local database. I have written the necessary code so that the client can input the required information such as Name, email, phone number etc. via a form and save it to MongoDB. Now I would like to add a file upload feature so that documents can be uploaded alongside employee information, such as insurance policies, passport copies etc. I am struggling to find a way to implement this as a lot methods online show implementations of CRUD and file uploads separately. Is there anyone experienced who would be able to suggest an implementation approach? I'm new to Node dev and not familiar with the vast selection of available frameworks. So far I have the following set up:
Frameworks used: Express, Mongoose, Express-Handlebars (view engine), Bootstrap
employee.Model:
const mongoose = require('mongoose');
const mongoURI = 'mongodb://localhost:27017/testDB'
const conn = mongoose.createConnection(mongoURI, {useNewUrlParser: true});
Schema = mongoose.Schema;
var employeeSchema = new Schema({
fullName: {
type: String,
required: true
},
email: {
type: String
},
mobile: {
type: String
},
city: {
type: String
}
});
const Employee = conn.model('Employee', employeeSchema);
module.exports = Employee;
Employee Controller:
const mongoose = require ('mongoose');
const express = require ('express');
var router = express.Router();
const Employee = require('../models/employee.model');
router.post('/', (req, res) => {
insertRecord(req, res);
});
function insertRecord (req, res) {
var employee = new Employee();
employee.fullName = req.body.fullName;
employee.email = req.body.email;
employee.mobile = req.body.mobile;
employee.city = req.body.city;
employee.save((err, doc) =>{
if (!err)
res.redirect('employee/list');
else {
console.log('Error during record insertion: ' + err);
}
});
}
Handlebars View:
<h3>{{viewTitle}}</h3>
<form action="/employee" method="POST" autocomplete="off">
<div class="form-group">
<label>Full Name</label>
<input type="text" class="form-control" name="fullName"
placeholder="Full Name">
</div>
<div class="form-group">
<label>Email</label>
<input type="text" class="form-control" name="email"
placeholder="Email">
</div>
<div class="form-row">
<div class="form-group col-md-6">
<label>Mobile</label>
<input type="text" class="form-control" name="mobile"
placeholder="Mobile">
</div>
<div class="form-group col-md-6">
<label>City</label>
<input type="text" class="form-control" name="city"
placeholder="City">
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-info"><i class="fa fa-
database"></i> Submit</button>
</div>
</form>
Uploaded files are inputs just like others, they just need the specific enctype="multipart/form-data" attribute on the HTML side, and a proper handling on the server.
And easy-to-grasp demonstration using Multer (based on their own examples actually):
const upload = require('multer')({dest: 'uploads/'}); // npm i multer
const fileUploadMiddleware = upload.fields({
{name: 'resume', maxCount: 1},
{name: 'insurance', maxCount: 4}
});
router.post('/', fileUploadMiddleware, (req, res) => {
insertRecord(req, res);
});
function insertRecord(req, res) {
...
console.log(req.files.resume[0].mimetype); // this is resume file type
console.log(req.files.insurance[0].path); // this is first insurance file path
console.log(req.body.fullName); // this still works
}

Firebase auth.$createUserWithEmailAndPassword complaining about email format

On firebase, using vue.js, when registering and trying to add more info about the user to the database I am running into a really frustrating error when the app tries to write the newUser email to Firebase. The app simply breaks when I the email is part of an object.
I am using the createUserWithEmailAndPassword(this.email, this.password) method to authorise and register a user (on the user list) and then calling a function to register the same user on the database.
this is the error i am getting:
As you can see the console is outputting a string joaoalvesmarrucho#gmail.com while complaining it's not a string. :/
I have converted the email to a string with no success.
HTML TEMPLATE:
<form v-on:submit.prevent>
<div class="control">
<input class="input" v-model="newUser.name" type="text" placeholder="Your Display Name">
</div>
<div class="control">
<input class="input" v-model="newUser.email" id="email" type="email" placeholder="joew#bloggs.com" >
</div>
<div class="control">
<input id="password" v-model="newUser.password" class="input" type="password" placeholder="Password">
</div>
<div class="control">
<input id="confirm_password" class="input" type="password" placeholder="Retype your password" v-on:keyup="checkRetypePassword">
</div><span id='message'></span>
<button type="submit" class="button is-primary" v-on:click="signUp">Register</button><span> or </span><button type="submit" class="button is-primary" v-on:click="googleSignUp">Register with Google</button>
</form>
SCRIPT:
export default {
data: function() {
return {
users: usersRef,
// username: this.$store.getters.getUser.displayName,
newUser: {
name: '',
email: '' ,
password: '',
uid: '',
}
};
},
methods: {
signUp: function() {
var jsonEmail = JSON.stringify(this.newUser.email);
var email = jsonEmail.replace(/\"/g, "");
console.log(email);
firebaseApp.auth().createUserWithEmailAndPassword(this.email, this.password).then(function(user) {
logUser(user); // callback push user to database
console.log(email);
}, function(error) {
alert(error.message + error.code);
});
function logUser(user) {
usersRef.push(this.newUser);
this.name = '';
this.email = ''; // ERROR: "email" must be a valid string
this.uid = firebaseApp.auth().currentUser.uid;
}
Could you please give me a hint?
Got it working simply by getting the value from the email field. Hope this is useful to somebody in the future:
HTML
<form v-on:submit.prevent>
<div class="control">
<input class="input" v-model="user.name" type="text" placeholder="Your Display Name">
</div>
<div class="control">
<input class="input" v-model="user.email" id="email" type="email" placeholder="joew#bloggs.com" >
</div>
<div class="control">
<input id="password" v-model="user.password" class="input" type="password" placeholder="Password">
</div>
<div class="control">
<input id="confirm_password" class="input" type="password" placeholder="Retype your password" v-on:keyup="checkRetypePassword">
</div><span id='message'></span>
<button type="submit" class="button is-primary" v-on:click="signUp">Register</button><span> or </span><button type="submit" class="button is-primary" v-on:click="googleSignUp">Register with Google</button>
</form>
SCRIPT:
export default {
data: function() {
return {
users: usersRef,
user: {
name: '',
email: '',
},
users: [{
name: 'John Doe',
email: 'john.doe#gmail.com',
uid: "should be actual User UID",
}],
};
},
methods: {
signUp: function() {
firebaseApp.auth().createUserWithEmailAndPassword(email.value, password.value).then(function(user) {
}, function(error) {
alert(error.message + error.code);
});

Sequelize ExpressJS Using Id for Post Method

I have a form that is being used to edit and update the record of a specific Id and I'm able to access the Id within my GET method for my route with req.params.annotationId, but when I try to use the POST version of getting a parameter with req.body.annotationId I get a value returned of NULL. I also tried to use req.params.annotationId and it returned the :annotationId placeholder for the route. Is this because the field is not present in the form? which would make sense because body-parser looks for values present in the fields?
This is the resulting query from the POST method:
Executing (default): SELECT `annotation_id` AS `annotationId`, `annotation_date` AS `annotationDate`,`user_id` AS `userId`, `createdAt`, `updatedAt`, `userUserId` FROM `annotation` AS `annotation` WHERE `annotation`.`user_id` = 1 AND `annotation`.`annotation_id` = NULL LIMIT 1;
Here is my route:
appRoutes.route('/edit/:annotationId')
.get(function(req, res){
console.log('This is the url path ' + req.originalUrl);
console.log(req.params.annotationId);
models.Annotation.find({
where: {
userId: req.user.user_id,
annotationId: req.params.annotationId
},attributes: ['annotationId', 'annotationDate']
}).then(function(annotation){
res.render('pages/annotation-edit.hbs',{
annotation: annotation,
user: req.user,
editMode: req.originalUrl
});
})
})
.post(function(req, res){
console.log("POST method triggered");
console.log(req.params.annotationId);
models.Annotation.find({
where: {
userId: req.user.user_id,
annotationId: req.body.annotationId
}
}).then(function(annotation){
if (annotation) {
console.log("Annotation exists");
annotation.update({
annotationDate: req.body.annotationDate,
userId: req.user.user_id
}).success(function() {
console.log("Annotation Updated");
});
}
})
});
Here is my annotation model:
module.exports = function(sequelize, DataTypes) {
var Annotation = sequelize.define('annotation', {
annotationId: {
type: DataTypes.INTEGER,
field: 'annotation_id',
autoIncrement: true,
primaryKey: true
},
annotationDate: {
type: DataTypes.DATE,
field: 'annotation_date'
},
userId: {
type: DataTypes.STRING,
field: 'user_id'
}
},
{
freezeTableName: true,
},
classMethods: {
associate: function(db) {
Annotation.belongsTo(db.User)
}
}
});
return Annotation;
}
Here is the form for the POST request:
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="annotation-form">
<form action="/app/edit/:annotationId" method="post">
<div class="annotation-form-header">
<img class="user-image" src="http://placehold.it/80x80" alt="Generic placeholder image">
<label for="annotation-date">Annotation Date:</label>
<input type="date" name="annotationDate" id="annotation-form-date" value="{{annotation.annotationDate}}">
</div>
<button type="submit" id="create-annotation-button">Update Annotation</button>
</form>
req.body.annotationId with get annotationID from data in form like :
<form action="/app/edit" method="post">
<input name="annotationId" type="hidden" value="121313">
<div class="annotation-form-header">
<img class="user-image" src="http://placehold.it/80x80" alt="Generic placeholder image">
<label for="annotation-date">Annotation Date:</label>
<input type="date" name="annotationDate" id="annotation-form-date" value="{{annotation.annotationDate}}">
</div>
<button type="submit" id="create-annotation-button">Update Annotation</button>
</form>
```
req.params.annotationId get annotationID from URL : /edit/4465465
<form action="/app/edit/:annotationId" method="post"> <- invalid URL
The form should use the handlebars object to pass in the current Id selected like so,
<form action="/app/edit/{{annotation.annotationId}}" method="post">
<input name="annotationId" type="hidden" value="121313">
<div class="annotation-form-header">
<img class="user-image" src="http://placehold.it/80x80" alt="Generic placeholder image">
<label for="annotation-date">Annotation Date:</label>
<input type="date" name="annotationDate" id="annotation-form-date" value="{{annotation.annotationDate}}">
</div>
<button type="submit" id="create-annotation-button">Update Annotation</button>
</form>
The route should then be change from .find to .update:
.post(function(req, res){
console.log("POST method triggered");
console.log(req.params.annotationId);
models.Annotation.update({
annotationId: req.body.annotationId,
annotationDate: req.body.annotationDate,
},{where:{
userId: req.user.user_id,
annotationId: req.body.annotationId
}}).then(function(){
console.log("Annotation was Updated");
res.redirect('/app');
});
});

Resources