Cast error while executing a mongoose query - node.js

I am executing a query in mongoose where I need to find all the users in my database and sort them and limit the results to 10.
My query route is:(the route is "/user/top")
router.get('/top', middleware.ensureAuthenticated, function (req, res) {
User.find({}).sort({bestScore: 1}).limit(10).exec(function (err, result) {
if(err){
res.json({
status:"error",
data:err
});
}
else {
res.json({
status:"ok",
data: result
})
}
})
});
My User model:
var UserSchema = new mongoose.Schema({
image: String,
displayName: String,
bestScore:Number,
});
The error while i call the url from postman
EDIT 1:
Output of mongodb query:
You can see that my _id is of type ObjectId .

Your data probably contains a document which looks like this:
{
_id: "top",
...
}
Since Mongoose expects that value to be an ObjectId by default you get this error. What you can do is map the _id field explicitly as a string:
var UserSchema = new mongoose.Schema({
_id: String,
image: String,
displayName: String,
bestScore:Number,
});

The issue you had is that your search route is going through findbyId. if you truly wanna search/find all users without using an Id, you need to make sure in your controller, your search comes before your getbyId. same also in your route. I had the same error here and I was able to solve it from my route by putting my search function before getbyId.
Also, when you are using postman, kindly enter the search parameter as seen in my own case study below;
take note the path and my parameter key and value:

Related

Looking for Best Approach For update one in Mongoose

I am still kind of new to Mongoose in general. I am building my blogging app which has backend based on Node and MongoDB, I am using Angular for frontend.
I am creating my Restful API which is supposed to allow user click on a post and update it. However, I don't know for sure whether I am doing it the right way here.
This is the schema for my post:
const mongoose = require('mongoose');
// Schema Is ONly bluePrint
var postSchema = mongoose.Schema({
title: {type: String, required: true },
content: {type: String, required: true},
}, {timestamps: true});
module.exports = mongoose.model("Post", postSchema);
In my angular service, I have this function to help me to send the http request to my backend server, the id for this function comes from backend mongoDB, title and content is from the form on the page
updatePost(id: string, title: string, content: string) {
console.log('start posts.service->updatePost()');
const post: Post = {
id: id,
title: title,
content: content
};
this._http.put(`http://localhost:3000/api/posts/${id}`, post)
.subscribe(res => console.log(res));
}
It appears to me that there are at least couple of ways of approaching this for creating my API
Method 1 ( works but highly doubt if this is good practice):
here I am passing the id retrieved from mongoDB back to server via my service.ts file to avoid the 'modifying immutable field _id' error
app.put("/api/posts/:id", (req,res)=>{
console.log('update api called:', req.params.id);
const post = new Post({
id: req.body.id,
title: req.body.title,
content: req.body.content
});
Post.updateOne({_id: req.params.id}, post).then( result=> {
console.log(result);
res.json({message:"Update successful!"});
});
});
Method 2 I consider this is more robust than method 1 but still I don't think its good practice:
app.put("/api/posts/:id", (req, res)=> {
Post.findOne(
{_id:req.params.id},(err,post)=>{
if(err){
console.log('Post Not found!');
res.json({message:"Error",error:err});
}else{
console.log('Found post:',post);
post.title=req.body.title;
post.content=req.body.content;
post.save((err,p)=>{
if(err){
console.log('Save from update failed!');
res.json({message:"Error",error:err});
}else{
res.json({message:"update success",data:p});
}
})
}
}
);
});
I am open to all opinions in the hope that I can learn something from guru of Mongoose and Restful : )
Justification to Choose findOneAndUpdate() in this scenario in simple words are as follow:
You can use findOneAndUpdate() as it updates document based on the
filter and sort criteria.
While working with mongoose mostly we prefer to use this function as compare to update() as it has a an option {new:
true} and with the help of that we can get updated data.
As your purpose here is to updating a single document so you can use findOneAndUpdate(). On the other hand update() should be
used in case of bulk modification.
As update() Always returns on of document modified it won't return updated documents and while working with such a scenario like
your we always returns updated document data in response so we
should use findOneAndUpdate() here

Mongoose elemMatch returns an empty array

I am currently building a web application and I have stumbled upon a problem I wasn't able to solve for the past 12 hours.
I am working on a basic get method where I am trying to retrieve certain work positions (i.e. cashier, clerk) that an employee is working as (the client-side works perfectly).
The mongoose model for WorkPosition is such (models/work-position.js):
var mongoose = require('mongoose');
var User = require('./user');
var Schema = mongoose.Schema;
var schema = new Schema ({
workplace : {type: String, required: true},
type : {type: String, required: true},
status : {type: String, required: true},
color : {type: String, required: true},
employees: [{type: Schema.Types.ObjectId, ref:'User'}]
});
module.exports = mongoose.model('WorkPosition', schema);
My get method (routes/work-position.js):
var express = require('express');
var router = express.Router();
var jwt = require('jsonwebtoken');
var User = require('../models/user');
var mongoose = require('mongoose');
var WorkPosition = require('../models/work-position');
router.get('/:userId', function(req, res, next) {
const userId = req.params.userId;
WorkPosition.find({employees: {$elemMatch : {$eq: userId}}})
.exec(function(err, workPositions) {
if(err) {
return res.status(500).json({
title: 'an error has occurred',
error: err
});
}
console.log(userId);
console.log(workPositions);
res.status(200).json({
message: 'Success',
obj: workPositions
});
});
});
The problem arises when I try to use the $elemMatch method. The code above, when the WorkPosition.find line is changed to
WorkPosition.find()
without any conditions (
{employees: {$elemMatch : {$eq: userId}}}
) inside, I am successfully able to retrieve the WorkPosition document that I desire.
However, I want to only retrieve the WorkPosition documents where the 'employees' field in WorkPosition matches the 'userId' I have received from req.params. Therefore, I searched through the mongodb/mongoose API (http://mongoosejs.com/docs/api.html#query_Query-elemMatch)
where I found the $elemMatch method.
In the mongodb shell, when I do
db.workpositions.find({"employees": { $elemMatch : {$eq: "596823efbac11d1978ba2ee9"}}})
where "5968...." is the userId, I am successfully able to query the WorkPosition document.
Through this command, I am able to verify that my logic is correct, and using the mongodb native shell command gets me the document I desire.
However, when I try to convert the same logic to the Mongoose API, which is:
WorkPosition.find().elemMatch('employees', {$eq : userId})
I get an empty array, and adding lines
mongoose.set('debug', function (coll, method, query, doc) {
console.log(coll + " " + method + " " + JSON.stringify(query) + " " + JSON.stringify(doc));
});
in /app.js , I am able to see what the mongoose query translates to native mongodb command which is :
workpositions find {"employees":{"$elemMatch":{"$eq":"596823efbac11d1978ba2ee9"}}} {"fields":{}}
. The collection (workpositions), method (find), array to seek (employees) and everything is correctly translated to native mongodb command EXCEPT
"$eq"
. The only difference between the shell command that successfully works and the mongoose command in my code is the additional quotation marks around '$eq'.
shell:
db.workpositions.find({"employees": { $elemMatch : {$eq: "596823efbac11d1978ba2ee9"}}})
mongoose:
db.workpositions.find({"employees": { $elemMatch : {"$eq": "596823efbac11d1978ba2ee9"}}})
I cannot seem to find a way to get rid of these extra quotation marks (which I believe is the cause of the problem). I tried using the native command with mongoose like :
mongoose.connection.db.workpositions.find(.....)
but this also results in an error.
What is the proper way to use elemMatch through mongoose? Can anyone enlighten me how to use it? I tried every way I could think of, and I cannot seem to get past this problem.
I am thinking of changing the WorkPosition.employees field so that it holds actual Users (not only userIds), and I can parse through with additional information that exactly matches the Mongoose elemMatch API. However, I believe this will waste a HUGE amount of database space because each WorkPosition has to carry an array of Users.
To have the correct relationship between Users and WorkPositions, the elemMatch is a requirement in my project. I would greatly appreciate any help!
Please use mongoose.Types.ObjectId around the $eq array operator when comparing mongodb ObjectIds, then you can retrieve the first document that matches the condition with $elemMatch operator.
$elemMatch:{$eq:mongoose.Types.ObjectId(userId)}

Mongo / Express Query Nested _id from query string

Using: node/express/mongodb/mongoose
With the setup listed above, I have created my schema and model and can query as needed. What I'm wondering how to do though is, pass the express request.query object to Model.find() in mongoose to match and query the _id of a nested document. In this instance, the query may look something like:
http://domain.com/api/object._id=57902aeec07ffa2290f179fe
Where object is a nested object that exists elsewhere in the database. I can easily query other fields. _id is the only one giving an issue. It returns an empty array of matches.
Can this be done?
This is an example and not the ACTUAL schema but this gets the point across..
let Category = mongoose.Schema({
name: String
})
let Product = mongoose.Schema({
name: String,
description:String,
category:Category
})
// sample category..
{
_id:ObjectId("1234567890"),
name: 'Sample Category'
}
// sample product
{
_id:ObjectId("0987654321"),
name:'Sample Product',
description:'Sample Product Description',
category: {
_id:ObjectId("1234567890"),
name: 'Sample Category'
}
}
So, what I'm looking for is... if I have the following in express..
app.get('/products',function(req,res,next){
let query = req.query
ProductModel.find(query).exec(function(err,docs){
res.json(docs)
})
})
This would allow me to specify anything I want in the query parameters as a query. So I could..
http://domain.com/api/products?name=String
http://domain.com/api/products?description=String
http://domain.com/api/products?category.name=String
I can query by category.name like this, but I can't do:
http://domain.com/api/products?category._id=1234567890
This returns an empty array
Change your query to http://domain.com/api/object/57902aeec07ffa2290f179fe and try
app.get('/api/object/:_id', function(req, res) {
// req._id is Mongo Document Id
// change MyModel to your model name
MyModel.findOne( {'_id' : req._id }, function(err, doc){
// do smth with this document
console.log(doc);
});
});
or try this one
http://domain.com/api/object?id=57902aeec07ffa2290f179fe
app.get('/api/object', function(req, res) {
var id = req.param('id');
MyModel.findOne( {'_id' : id }, function(err, doc){
console.log(doc);
});
})
First of all increase your skills in getting URL and POST Parameters by this article.
Read official Express 4.x API Documentation
Never mind I feel ridiculous. It works just as I posted above.. after I fixed an error in my schema.

How to update a specific object in a array of objects in Node.js and Mongoose

I am new to node.js coming from java experience. I have a situation that I am trying to wrap my head around. My stack is express.js, mongoose, ejs template. Here is my scenario:
I have a schema:
var UserSchema = mongoose.Schema({
name: {
type: String,
index: true
},
password: {
type: String,
select: false
},
email: {
type: String
},
academic: [{
qualification: String,
institute: String,
from: String,
to: String,
about: String
}]
});
there is a list of academics. I want to update only one academic object in that list. How would I go about this?
router.post('/academic/schools/update', function (req, res) {
});
I pass the values from ejs template into the route and getting the values in the req.body. How would I in node and mongoose query that specific object in the route and then updates its values. I have thought about maybe adding an Id to the academic object to be able to keep track of which to update.
Each academic sub document will have an _id after you save.
There are two ways you can do it. If you pass the id of the user and id of the academic sub-doc id in the url or request body, then you can update like this:
User.findById(userId).then(user => {
let academic = user.academic.id(academicId);
academic.qualification = 'something';
return user.save();
});
If you only pass the id of the academic sub-doc, then you can do it like this:
User.findOne({'academic._id': academicId}).then(user => {
let academic = user.academic.id(academicId);
academic.qualification = 'something';
return user.save();
});
Note that sub document array return from mongoose are mongoosearray instead of the native array data type. So you can manipulate them using .id .push .pop .remove method http://mongoosejs.com/docs/subdocs.html

nodeJS + MongoDB + Mongoose - Unable To Delete 1 Entry

I'm using nodeJS MongoDB/Mongoose to create/update/delete movies inside the database using Postman post/delete methods.
The create function is working fine, and even the remove function is working properly so when I use Postman I get the return: "Movie has been deleted!" like it should.
The only problem is that my function is emptying the entire database of movies instead of just that 1 movie, here is the remove function:
function destroy(req, res, next){
var movieID = req.body
Movie.remove(movieID, function(err,movie){
if(err){
res.status(400).send(err)
} else {
res.send("Movie has been deleted!")
db.close()
}
})
The movie object:
var movieSchema = new mongoose.Schema({
name: String,
yay: Number,
nay: Number,
release_date: Date,
in_theaters: Boolean,
released: Boolean,
buy_link: String,
imdb_link: String,
image_url: String,
description: String,
trailer_link: String
})
I want to delete a movie based on it's "name" so I only have to input the name and it will delete the entire movie.
Have you tried the findOneAndRemove query?
This query is much cleaner compared to finding a model and removing it inside the callback. Beside this I assume it's faster because you basically do 1 query instead of 2 after each other.
If you are passing direct value to Remove method, it will try to match with _id field.
As per your model, _id is ObjectId field which is managed automatically by mongodb.
In case if you enter like this. .remove("movie", callback) which is not a valid ObjectId.
Mongoose is discarding this invalid condition and executing Movie.remove({}); which is deleting all your records.
So it is better to validate whether the input is valid ObjectId or not before directly passing to Movie.remove();
I also recommend to use like this: Movie.remove({_id: movieId}, callback).
And for movie name :
Movie.remove({name: movieName}, callback);
Update:
You can take from Postman
var movieName = req.body.movieName;
Movie.remove({name: movieName}, function(err, updateObj){
});
Can you try this?
var movieName = req.body.name;
Movie.find('name': movieName, function(err, movie) {
if (err) res.send({error: err});
Movie.remove(function(err, movie){
if (err) res.send({error: err});
res.json({message: "Movie is removed", movie: movie});
});
});

Resources