Do mongoose query on Dropdown changed - javascript

First of I want to say that I am new to JS and mongoose/MongoDB.
I made a page where I show some data I have stored in mongoDB and that is working fine. Now I made a dropdown menu with some options and I want to update one field based on the value I select in the dropdown menu.
Basically my question is: how do update one field in the mongoDB when the onchanged of the Dropdown menu is triggered?
Here is my Schema I want to update:
/src/models/itemmodel.js
const mongoose = require('mongoose');
const AutoIncrement = require('mongoose-sequence')(mongoose);
console.log('/src/models/itemmodel.js');
// Create a Schema for items
const itemSchema = new mongoose.Schema({
title: { type: String, default: '' },
description: { type: String, default: '' },
createdBy: { type: String, default: '' },
masco: { type: String, default: '' },
projectName: { type: String, default: '' },
projectNumber: { type: Number, default: '' },
createdDate: { type: String, default: '' },
lastUpdate: { type: String, default: '' },
status: { type: String, default: 'New' },
responsible: { type: String, default: 'Not defined' },
affectedSystem: { type: String, default: 'Not defined' },
seqId: Number,
itemId: { type: String, default: '' },
});
module.exports = mongoose.model('Item', itemSchema);
And this is how i have setup the Dropdownmenu:
<% var id = ''; %>
<select name="" id="statusDropdown" class="dropbtn" onchange="getSelectedValue(this);">
<% var states = [ 'New', 'Assigned', 'In progess']; %>
<% var selectedId = items._id; %>
<% for ( var i = 0; i < states.length; i++ ) { %>
<% var selected = ( id == i ) ? "selected" : ""; %>
<option value='{"state": "<%=states[i]%>", "id": "<%=items._id%>"}' > <%=states[i] %> </option>
<% } %>
</select>
I am calling the function getSelectedValue(this) here and i have this function in the same file in the . I am receiving the data i want, the _id and the onchanged value.
<script>
var ItemModel = require("../src/models/itemmodel.js");
function getSelectedValue(selectObject) {
var statusOfItem = JSON.parse(selectObject.value);
console.log(statusOfItem.state);
console.log(statusOfItem.id);
var e = document.getElementById("statusDropdown");
var newStatus = e.options[e.selectedIndex].value;
console.log(statusOfItem);
ItemModel.findOneAndUpdate({ _id: statusOfItem.id }, { $set: { status: statusOfItem.state } },
{new: true})
.then((updatedItem) => {
console.log(updatedItem);
})
.catch((err) => {
console.error('Error when updating with new status ' + err);
});
}
</script>
But my issue is, that i get the error message:
Uncaught ReferenceError: ItemModel is not defined
at getSelectedValue (SR790123-7:213)
at HTMLSelectElement.onchange (SR790123-7:112)
So I am not able to access the mongoose model I have created and also exported.
And I looked for this issue and found some information that it is not possible to use mongoose when working with browser/client-side JavaScript. I tried to implement browserify but then I am getting the error:
GET http://127.0.0.1:3000/src/queries.js net::ERR_ABORTED 404 (Not Found)
when adding the path in src () and that file exists and is in the folder.
Thank you in advance and I am grateful for every help I get.
Best regards,
Christian

Related

Mongoose - CastError Cast to string failed for value "Object"

I have Mongoose CastError issue. I made a nodeJs API. At the specific route, it returns data appended with some other data. I saw many fixes available here but my scenario is different.
Here is my model and the problem occurs at fields property.
const deviceSchema = new Schema({
device_id: { type: String, required: true },
user_id: { type: Schema.Types.ObjectId, ref: 'User', require: true },
location_latitude: { type: String, default: '0' },
location_longitude: { type: String, default: '0' },
fields: [{ type: String }],
field_id: { type: Schema.Types.ObjectId, ref: 'Field', required: true },
timestamp: {
type: Date,
default: Date.now,
},
});
and my controller is
exports.getAllDevices = async (req, res) => {
try {
let devices = await Device.find({})
.sort({
timestamp: 'desc',
})
.populate('user_id', ['name']);
// Let us get the last value of each field
for (let i = 0; i < devices.length; i++) {
for (let j = 0; j < devices[i].fields.length; j++) {
if (devices[i].fields[j] !== null && devices[i].fields[j] !== '') {
await influx
.query(
`select last(${devices[i].fields[j]}), ${devices[i].fields[j]} from mqtt_consumer where topic = '${devices[i].device_id}'`
)
.then((results) => {
************** Problem occurs here **************
if (results.length > 0) {
devices[i].fields[j] = {
name: devices[i].fields[j],
last: results[0].last,
};
} else {
devices[i].fields[j] = {
name: devices[i].fields[j],
last: 0,
};
}
************** Problem occurs here **************
});
}
}
}
// Return the results
res.status(200).json({
status: 'Success',
length: devices.length,
data: devices,
});
} catch (err) {
console.log(err);
res.status(500).json({
error: err,
});
}
};
It actually gets data from InfluxDB and appends it to fields property which was fetched from MongoDB as mentioned in my model. But it refused to append and CastError occurs.
After addition, it will look like this
I can't resolve this error after trying so many fixes. I don't know where I'm wrong. Please suggest to me some solution for this.
I can see you are not using devices variable as Mongoose Document. devices is an array of Documents.
I would like to suggest you to use lean() function to convert from Document to plain JavaScript object like
let devices = await Device.find({})
.sort({
timestamp: 'desc',
})
.populate('user_id', ['name'])
.lean();

defining 2 types under 1 variable in mongodb

I'm trying to create a Schema that looks like this:
const exampleSchema = mongoose.Schema({
topic: {
type: String,
required: true,
},
words: {
type: String || Array: {
type: String,
required: true,
}
required: true,
}
});
I've read about custom types in mongodb but don't understand what the documentation is showing me. Could anyone help me out?
Creating custom schema in mongoose,
class StringOrArray extends mongoose.SchemaType {
constructor(key, options) {
super(key, options, 'StringOrArray');
}
cast(val) {
// please change your logic as per your requirement
if (typeof val !== 'string' && !Array.isArray(val)) {
throw new Error('StringOrArray: ' + val + ' must be a String or Array');
}
return val;
}
}
// Don't forget to add `Int8` to the type registry
mongoose.Schema.Types.StringOrArray = StringOrArray;
Use StringOrArray in your schema,
const exampleSchema = mongoose.Schema({
topic: {
type: String,
required: true,
},
words: {
type: StringOrArray,
required: true
}
});

How to update existing object with additional data

The project is created with nodejs and mongoose. What I am trying to do is to update the existing model with addition data (which is a comment, in that case).
This is the model and its methods:
const bugSchema = new Schema({
title: {
type: String,
required: true
},
description: {
type: String,
required: true
},
date: {
type: String,
required: true
},
time: {
type: String,
required: true
},
assignedTo: {
type: String,
required: true
},
assignedBy: {
type: String,
required: true
},
status: {
type: String,
required: true
},
priority: {
type: String,
required: true
},
comments: {
comment:[
{
user:{
type: String,
required: true
},
content: {
type: String,
required: true
}
}
]
}
});
bugSchema.methods.addComment = function(comment){
const username = comment.user;
const content = comment.content;
console.log(comment);
const updatedComments = [...this.comments];
updatedComments.push({
user : username,
content: content
});
this.comments = updatedComments;
return this.save();
};
The controller, which is passing the information from the form:
exports.postComment = (req,res,next) =>{
const bugId = req.body.bugID;
const name = req.session.user.fullName;
const content = req.body.content;
const prod = {name, content};
Bug.findById(bugId).then(bug =>{
return bug.addComment(prod);
})
.then(result =>{
console.log(result);
});
};
I am getting a following error:
(node:3508) UnhandledPromiseRejectionWarning: TypeError: this.comments is not iterable
(node:3508) UnhandledPromiseRejectionWarning: TypeError: this.comments is not iterable
The error indicate you're trying to iterable a type of data which does NOT has that capability.
You can check that printing the type:
console.log(typeof this.comments)
Or even, priting the whole object:
console.log(this.comments)
as you can see, in both cases you're getting an object, not a list (how you spect)
So you can do 2 things:
1- Iterable a list
this.comments is an object but into that object you have the list you want, so just use the list instead.
bugSchema.methods.addComment = function(comment){
const username = comment.user;
const content = comment.content;
console.log(comment);
//const updatedComments = [...this.comments];
const updatedComments = [...this.comments.comment];
updatedComments.push({
user : username,
content: content
});
this.comments = updatedComments;
return this.save();
};
Or you can modify your schema making the comments a list instead of an object
2- comments as list in schema
Define the comments attribute as a list
const bugSchema = new Schema({
title: {
type: String,
required: true
},
description: {
type: String,
required: true
},
...
...,
comments:[
{
user:{
type: String,
required: true
},
content: {
type: String,
required: true
}
}
]
});
And then, try to iterable it as how you been doing
bugSchema.methods.addComment = function(comment){
const username = comment.user;
const content = comment.content;
console.log(comment);
const updatedComments = [...this.comments];
updatedComments.push({
user : username,
content: content
});
this.comments = updatedComments;
return this.save();
};
I am not sure but comments is an object and not an array so you can't push using [...this.comments] and I think it is the comment you want to push?
const updatedComments = [...this.comment];
updatedComments.push({
user : username,
content: content
});
this.comment = updatedComments;
From your schema comments is not an array. you are trying to spread an object into an array. const updatedComments = [...this.comments]; also push works on array.
try to modify your schema definitions by declaring the commentSchema outside the bugSchema.
const commentSchema = new Schema({
user:{
type: String,
required: true
},
content: {
type: String,
required: true
}
})
const bugSchema = new Schema({
comments: {
type: [commentSchema]
}
})
Bug.findByIdAndUpdate(bugId, {$push: {comments: newComment}})
Don't use findByIdAndUpdate Mongoose method, you better use save
it is written here https://mongoosejs.com/docs/tutorials/findoneandupdate.html
The findOneAndUpdate() function in Mongoose has a wide variety of use cases. You should use save() to update documents where possible, but there are some cases where you need to use findOneAndUpdate(). In this tutorial, you'll see how to use findOneAndUpdate(), and learn when you need to use it.
Below a router example
router.put('/items', (req, res) => {
if (!req.body._id || !req.body.title) {
return res.status(501).send({ message: 'Missing parameters, or incorrect parameters' });
}
return itemModel.findOne({ _id: req.body._id }, (err, item) => {
if (err) {
return res.status(500).send({
message: err
});
}
item.title = req.body.title; // <------------- You rewrite what was before stored on title attribute
return item.save((err, item) => { // <------------- You save it, this is not gonna create a new one, except if it doesn't exist already
if (err) {
return res.status(400).send({
message: 'Failed to update item'
});
} else {
return res.status(200).send({
message: 'Item update succesfully',
data: item
});
}
});
});
});

My static method is not running

Here is my recipe.js file...
const express = require('express');
const mongoose = require('mongoose');
const User = require('../model/user');
require('mongoose-currency').loadType(mongoose);
const Currency = mongoose.Types.Currency;
const Schema = mongoose.Schema;
const reviewSchema = require('../model/review');
let recipeSchema = new Schema({
name: {
type: String,
required: true
},
description: {
type: String,
},
steps: {
type: String,
required: true,
},
ingredients: {
type: Array,
default: ['1', '2', '3', '4']
},
category: {
type: String,
required: true,
index: true
},
postedBy: {
type: String,
required: true,
},
reviewsOfRecipe: [reviewSchema],
numberOfRatings: {
type: Number,
default: 0
},
totalAddedRatings: {
type: Number,
default: 0
},
reviewAverage: {
type: Number,
default: 0
},
postersCreationDate: {
type: Number,
index: true
},
likedBy: {
type: Array
},
reviewedBy: {
type: Array
}
});
// Here is my static method
recipeSchema.methods.calculateAverage = function(){
let recipe = this;
if (recipe.numberOfRatings === 0 && recipe.totalAddedRatings === 0){
recipe.reviewAverage = 0;
}
else {
recipe.reviewAverage = recipe.totalAddedRatings / recipe.numberOfRatings
}
};
let Recipe = mongoose.model('Recipe', recipeSchema);
module.exports = Recipe;
In my router file, every time a user submits a review for a recipe, the fields numberOfRatings and totalAddedRatings get incremented. And after they get incremented, my static method calculateAverage should run and update the document.
Here is what it looks like in my code:
Recipe.findOneAndUpdate({_id: recipe._id, postersCreationDate: recipe.postersCreationDate},{$inc: {numberOfRatings: 1, totalAddedRatings: reviewScore}}, {returnNewDocument: true}).then((recipe) => {
recipe.calculateAverage();
});
However, every time a user submits a review, although numberOfRatings and numberOfRatings get incremented accordingly, reviewAverage does not.
I am thinking about setting reviewAverage as a virtual field instead; but I am worried that doing so would make it harder and inefficient to sort the recipes by the highest and lowest review averages.
first some things about your findOneAndUpdate can change. Id should be sufficient to find by and 'returnNewDocument' is not an option. In mongoose it's just 'new'
Recipe.findOneAndUpdate({_id: recipe._id},{$inc: {numberOfRatings: 1, totalAddedRatings: reviewScore}}, {new: true}).then((recipe) => {
recipe.calculateAverage();
});
The problem is that you aren't saving the average to the recipe.
recipeSchema.methods.calculateAverage = function(callback) {
let recipe = this;
if (recipe.numberOfRatings === 0 && recipe.totalAddedRatings === 0) {
recipe.reviewAverage = 0;
}
else {
recipe.reviewAverage = recipe.totalAddedRatings / recipe.numberOfRatings
}
recipe.save(callback);
};

search in array filed of mongodb by mongoos in query

I have a collection Events in my Mongodb and it has an array filed called for_who.
This field is for checking if a user id is in this array, so this user can see this Event. I want to get the Events that for_who field contains user_id.
This is my current query:
Events.find(
{ for_who: { "$in" : [user_id]} }
).lean().exec(function(err , obj) { ... });
my schema:
var eventSchema = new mongoose.Schema({
id : { type: Number , required: true , unique: true } ,
title : { type: String , required: true } ,
created_at : { type: String , required: true } ,
for_who : { type: Array }
});
var Events = mongoose.model('Events', eventSchema);
Events.find().then((events) => {
var userData = events.find((v) => {
return v.for_who === user_id
})
if(userData){
//user was found
}
})
i think this cloud work

Categories

Resources