NodeJS + MongoDB: Getting data from collection with findOne () - node.js

I have a collection "companies" with several objects. Every object has "_id" parameter. I'm trying to get this parameter from db:
app.get('/companies/:id',function(req,res){
db.collection("companies",function(err,collection){
console.log(req.params.id);
collection.findOne({_id: req.params.id},function(err, doc) {
if (doc){
console.log(doc._id);
} else {
console.log('no data for this company');
}
});
});
});
So, I request companies/4fcfd7f246e1464d05000001 (4fcfd7f246e1464d05000001 is _id-parma of a object I need) and findOne returns nothing, that' why console.log('no data for this company'); executes.
I'm absolutely sure that I have an object with _id="4fcfd7f246e1464d05000001". What I'm doing wrong? Thanks!
However, I've just noticed that id is not a typical string field. That's what mViewer shows:
"_id": {
"$oid": "4fcfd7f246e1464d05000001"
},
Seems to be strange a bit...

You need to construct the ObjectID and not pass it in as a string. Something like this should work:
var BSON = require('mongodb').BSONPure;
var obj_id = BSON.ObjectID.createFromHexString("4fcfd7f246e1464d05000001");
Then, try using that in your find/findOne.
Edit: As pointed out by Ohad in the comments (thanks Ohad!), you can also use:
new require('mongodb').ObjectID(req.params.id)
Instead of createFromHexString as outlined above.

That's because _id field in mongo isn't of string type (as your req.params.id). As suggested in other answers, you should explicitly convert it.
Try mongoskin, you could use it like node-mongodb-native driver, but with some sugar. For example:
// connect easier
var db = require('mongoskin').mongo.db('localhost:27017/testdb?auto_reconnect');
// collections
var companies = db.collection('companies');
// create object IDs
var oid = db.companies.id(req.params.id);
// some nice functions…
companies.findById();
//… and bindings
db.bind('companies', {
top10: function(callback) {
this.find({}, {limit: 10, sort: [['rating', -1]]).toArray(callback);
}
});
db.companies.top10(printTop10);

You can use findById() which will take care of the id conversion for you.
company = Company.findById(req.params.id, function(err, company) {
//////////
});

In case these didn't work for you, this worked for me for accessing a blog post:
const getSinglePost = async (req, res) => {
let id = req.params.id;
var ObjectId = require('mongodb').ObjectId;
const db = await client.db('CMS');
const data = await db.collection("posts").findOne({ _id: ObjectId(id) })
if (data) {
res.status(200).send(data)
} else res.status(400).send({ message: "no post found" })
}

Related

Mongoose Model.find() methods query string not working

im trying to make simple rest api. I have a collection in mongodb and i connected my db to my app with mongoose pkg. I can access all items without query strings with Operator.find() but it doesn't work with query string ex: Operator.find({name:'Kapkan'}) it returns all of them. Also Operator.findOne({name:'Azami'}) doesn't work either. The query string returns the first element of the collection no matter what.
app.get('/api/operators',async(req,res) => {
let operators;
try{
if(req.query.name){
Operator.find({name:'Kapkan'}).then((data) => {
console.log(data);
});
}
else
operators = await Operator.find();
res.send(operators)
}catch(er){
console.log(er);
}
})
You are not assigning result of query with filter to operators. Unsure if you have {} around else or not but try refactoring the code as shown below:
app.get('/api/operators',async(req,res) => {
const filters = {};
if (req.query.name) {
filters.name = req.query.name;
}
const data = await Operator.find(filters);
console.log(data);
return res.json({ data });
})
I checked my Schema and i realize i forgotten put name field...
const OperatorSchema = new Schema({
_id:Number,
logo:String,
image:String,
unit:String,
*name:String,
side:String,
.
.
.
})

CastError when open link to static file in Jade, Nodejs [duplicate]

When sending a request to /customers/41224d776a326fb40f000001 and a document with _id 41224d776a326fb40f000001 does not exist, doc is null and I'm returning a 404:
Controller.prototype.show = function(id, res) {
this.model.findById(id, function(err, doc) {
if (err) {
throw err;
}
if (!doc) {
res.send(404);
}
return res.send(doc);
});
};
However, when _id does not match what Mongoose expects as "format" (I suppose) for example with GET /customers/foo a strange error is returned:
CastError: Cast to ObjectId failed for value "foo" at path "_id".
So what's this error?
Mongoose's findById method casts the id parameter to the type of the model's _id field so that it can properly query for the matching doc. This is an ObjectId but "foo" is not a valid ObjectId so the cast fails.
This doesn't happen with 41224d776a326fb40f000001 because that string is a valid ObjectId.
One way to resolve this is to add a check prior to your findById call to see if id is a valid ObjectId or not like so:
if (id.match(/^[0-9a-fA-F]{24}$/)) {
// Yes, it's a valid ObjectId, proceed with `findById` call.
}
Use existing functions for checking ObjectID.
var mongoose = require('mongoose');
mongoose.Types.ObjectId.isValid('your id here');
I had to move my routes on top of other routes that are catching the route parameters:
// require express and express router
const express = require("express");
const router = express.Router();
// move this `/post/like` route on top
router.put("/post/like", requireSignin, like);
// keep the route with route parameter `/:postId` below regular routes
router.get("/post/:postId", singlePost);
I have the same issue I add
_id: String .in schema then it start work
This might be a case of routes mismatch if you have two different routes like this
router.route("/order/me") //should come before the route which has been passed with params
router.route("/order/:id")
then you have to be careful putting the route that is using a param after the regular route that worked for me
Are you parsing that string as ObjectId?
Here in my application, what I do is:
ObjectId.fromString( myObjectIdString );
it happens when you pass an invalid id to mongoose. so first check it before proceeding, using mongoose isValid function
import mongoose from "mongoose";
// add this inside your route
if( !mongoose.Types.ObjectId.isValid(id) ) return false;
In my case, I had to add _id: Object into my Schema, and then everything worked fine.
As of Nov 19, 2019
You can use isValidObjectId(id) from mongoose version 5.7.12
https://mongoosejs.com/docs/api/mongoose.html#mongoose_Mongoose-isValidObjectId
if(mongoose.Types.ObjectId.isValid(userId.id)) {
User.findById(userId.id,function (err, doc) {
if(err) {
reject(err);
} else if(doc) {
resolve({success:true,data:doc});
} else {
reject({success:false,data:"no data exist for this id"})
}
});
} else {
reject({success:"false",data:"Please provide correct id"});
}
best is to check validity
You can also use ObjectId.isValid like the following :
if (!ObjectId.isValid(userId)) return Error({ status: 422 })
If above solutions do not work for you.
Check if you are sending a GET request to a POST route.
It was that simple and stupid for me.
All you have to do is change the parameter name "id" to "_id"
//Use following to check if the id is a valid ObjectId?
var valid = mongoose.Types.ObjectId.isValid(req.params.id);
if(valid)
{
//process your code here
} else {
//the id is not a valid ObjectId
}
I was faced with something similar recently and solved it by catching the error to find out if it's a Mongoose ObjectId error.
app.get("/:userId", (req, res, next) => {
try {
// query and other code here
} catch (err) {
if (err.kind === "ObjectId") {
return res.status(404).json({
errors: [
{
msg: "User not found",
status: "404",
},
],
});
}
next(err);
}
});
You could either validate every ID before using it in your queries (which I think is the best practice),
// Assuming you are using Express, this can return 404 automatically.
app.post('/resource/:id([0-9a-f]{24})', function(req, res){
const id = req.params.id;
// ...
});
... or you could monkey patch Mongoose to ignore those casting errors and instead use a string representation to carry on the query. Your query will of course not find anything, but that is probably what you want to have happened anyway.
import { SchemaType } from 'mongoose';
let patched = false;
export const queryObjectIdCastErrorHandler = {
install,
};
/**
* Monkey patches `mongoose.SchemaType.prototype.castForQueryWrapper` to catch
* ObjectId cast errors and return string instead so that the query can continue
* the execution. Since failed casts will now use a string instead of ObjectId
* your queries will not find what they are looking for and may actually find
* something else if you happen to have a document with this id using string
* representation. I think this is more or less how MySQL would behave if you
* queried a document by id and sent a string instead of a number for example.
*/
function install() {
if (patched) {
return;
}
patch();
patched = true;
}
function patch() {
// #ts-ignore using private api.
const original = SchemaType.prototype.castForQueryWrapper;
// #ts-ignore using private api.
SchemaType.prototype.castForQueryWrapper = function () {
try {
return original.apply(this, arguments);
} catch (e) {
if ((e.message as string).startsWith('Cast to ObjectId failed')) {
return arguments[0].val;
}
throw e;
}
};
}
I went with an adaptation of the #gustavohenke solution, implementing cast ObjectId in a try-catch wrapped around the original code to leverage the failure of ObjectId casting as a validation method.
Controller.prototype.show = function(id, res) {
try {
var _id = mongoose.Types.ObjectId.fromString(id);
// the original code stays the same, with _id instead of id:
this.model.findById(_id, function(err, doc) {
if (err) {
throw err;
}
if (!doc) {
res.send(404);
}
return res.send(doc);
});
} catch (err) {
res.json(404, err);
}
};
This is an old question but you can also use express-validator package to check request params
express-validator version 4 (latest):
validator = require('express-validator/check');
app.get('/show/:id', [
validator.param('id').isMongoId().trim()
], function(req, res) {
// validation result
var errors = validator.validationResult(req);
// check if there are errors
if ( !errors.isEmpty() ) {
return res.send('404');
}
// else
model.findById(req.params.id, function(err, doc) {
return res.send(doc);
});
});
express-validator version 3:
var expressValidator = require('express-validator');
app.use(expressValidator(middlewareOptions));
app.get('/show/:id', function(req, res, next) {
req.checkParams('id').isMongoId();
// validation result
req.getValidationResult().then(function(result) {
// check if there are errors
if ( !result.isEmpty() ) {
return res.send('404');
}
// else
model.findById(req.params.id, function(err, doc) {
return res.send(doc);
});
});
});
Always use mongoose.Types.ObjectId('your id')for conditions in your query it will validate the id field before running your query as a result your app will not crash.
I was having problems with this and fixed doing mongoose.ObjectId(id) without Types
ObjectId is composed of following things.
a 4-byte value representing the seconds since the Unix epoch
a 5-byte random value (Machine ID 3 bytes and Processor id 2 bytes)
a 3-byte counter, starting with a random
value.
Correct way to validate if the objectId is valid is by using static method from ObjectId class itself.
mongoose.Types.ObjectId.isValid(sample_object_id)
In my case, similar routes caused this problem.
Router.get("/:id", getUserById);
Router.get("/myBookings",getMyBookings);
In above code, whenever a get request to route "/myBookings" is made, it goes to the first route where req.params.id is equals to "myBookings" which is not a valid ObjectId.
It can be corrected by making path of both routes different.
Something like this
Router.get("/user/:id", getUserById);
Router.get("/myBookings",getMyBookings);
You are having the castError because the next route you called after the id route could not be attached to the id route.
You have to declare the id route as one last route.
The way I fix this problem is by transforming the id into a string
I like it fancy with the backtick:
`${id}`
this should fix the problem with no overhead
UPDATE OCT 2022
it would be best if you now used the :
{id: id} // if you have an id property defined
or
{_id: new ObjectId(id)} // and search for the default mongodb _id
OR you can do this
var ObjectId = require('mongoose').Types.ObjectId;
var objId = new ObjectId( (param.length < 12) ? "123456789012" : param );
as mentioned here Mongoose's find method with $or condition does not work properly
Cast string to ObjectId
import mongoose from "mongoose"; // ES6 or above
const mongoose = require('mongoose'); // ES5 or below
let userid = _id
console.log(mongoose.Types.ObjectId(userid)) //5c516fae4e6a1c1cfce18d77
Detecting and Correcting the ObjectID Error
I stumbled into this problem when trying to delete an item using mongoose and got the same error. After looking over the return string, I found there were some extra spaces inside the returned string which caused the error for me. So, I applied a few of the answers provided here to detect the erroneous id then remove the extra spaces from the string. Here is the code that worked for me to finally resolve the issue.
const mongoose = require("mongoose");
mongoose.set('useFindAndModify', false); //was set due to DeprecationWarning: Mongoose: `findOneAndUpdate()` and `findOneAndDelete()` without the `useFindAndModify`
app.post("/delete", function(req, res){
let checkedItem = req.body.deleteItem;
if (!mongoose.Types.ObjectId.isValid(checkedItem)) {
checkedItem = checkedItem.replace(/\s/g, '');
}
Item.findByIdAndRemove(checkedItem, function(err) {
if (!err) {
console.log("Successfully Deleted " + checkedItem);
res.redirect("/");
}
});
});
This worked for me and I assume if other items start to appear in the return string they can be removed in a similar way.
I hope this helps.
I had the same error, but in a different situation than in the question, but maybe it will be useful to someone.
The problem was adding buckles:
Wrong:
const gamesArray = [myId];
const player = await Player.findByIdAndUpdate(req.player._id, {
gamesId: [gamesArray]
}, { new: true }
Correct:
const gamesArray = [myId];
const player = await Player.findByIdAndUpdate(req.player._id, {
gamesId: gamesArray
}, { new: true }
In my case the parameter id length was 25, So I trimmed first character of parameter id and tried. It worked.
Blockquote
const paramId = req.params.id;
if(paramId.length === 25){
const _id = paramId.substring(1, 25);
}
To change the string object to ObjectId instance fromString() method is not exist anymore. There is a new method createFromHexString().
const _id = mongoose.Types.ObjectId.fromString(id); // old method not available
const _id = mongoose.Types.ObjectId.createFromHexString(id); // new method.
could happen if you are sending less or more then 24 characters string as id

find by _id with Mongoose

I am having trouble with a simple findById with mongoose.
Confirmed the item exists in the DB
db.getCollection('stories').find({_id:'572f16439c0d3ffe0bc084a4'})
With mongoose
Story.findById(topic.storyId, function(err, res) {
logger.info("res", res);
assert.isNotNull(res);
});
won't find it.
I also tried converting to a mongoId, still cannot be found (even though mongoose supposedly does this for you)
var mid = mongoose.Types.ObjectId(storyId);
let story = await Story.findOne({_id: mid}).exec();
I'm actually trying to use this with typescript, hence the await.
I also tried the Story.findById(id) method, still cannot be found.
Is there some gotcha to just finding items by a plain _id field?
does the _id have to be in the Schema? (docs say no)
I can find by other values in the Schema, just _id can't be used...
update: I wrote a short test for this.
describe("StoryConvert", function() {
it("should read a list of topics", async function test() {
let topics = await Topic.find({});
for (let i = 0; i < topics.length; i ++) {
let topic = topics[i];
// topics.forEach( async function(topic) {
let storyId = topic.storyId;
let mid = mongoose.Types.ObjectId(storyId);
let story = await Story.findOne({_id: mid});
// let story = await Story.findById(topic.storyId).exec();
// assert.equal(topic.storyId, story._id);
logger.info("storyId", storyId);
logger.info("mid", mid);
logger.info("story", story);
Story.findOne({_id: storyId}, function(err, res) {
if (err) {
logger.error(err);
} else {
logger.info("no error");
}
logger.info("res1", res);
});
Story.findOne({_id: mid}, function(err, res) {
logger.info("res2", res);
});
Story.findById(mid, function(err, res) {
logger.info("res3", res);
// assert.isNotNull(res);
});
}
});
});
It will return stuff like
Testing storyId 572f16439c0d3ffe0bc084a4
Testing mid 572f16439c0d3ffe0bc084a4
Testing story null
Testing no error
Testing res1 null
Testing res2 null
Testing res3 null
I noticed that topic.storyId is a string
not sure if that would cause any issues mapping to the other table.
I tried also adding some type defs
storyId: {
type: mongoose.Schema.Types.ObjectId,
required: false
}
Because this query finds the doc in the shell:
db.getCollection('stories').find({_id:'572f16439c0d3ffe0bc084a4'})
That means that the type of _id in the document is actually a string, not an ObjectId like Mongoose is expecting.
To find that doc using Mongoose, you'd have to define _id in the schema for Story as:
_id: { type: String }
If your Mongo schema is configured to use Object Id, you query in nodeJS using
models.Foo.findById(id)
where Foo is your model and id is your id.
here's a working example
router.get('/:id', function(req, res, next) {
var id = req.params.id
models.Foo.findById(id)
.lean().exec(function (err, results) {
if (err) return console.error(err)
try {
console.log(results)
} catch (error) {
console.log("errror getting results")
console.log(error)
}
})
})
In Mongo DB your query would be
{_id:ObjectId('5c09fb04ff03a672a26fb23a')}
One solution is to use mongoose.ObjectId()
const Model = require('./model')
const mongoose = require('mongoose')
Model.find({ id: mongoose.ObjectId(userID) })
It works, but it is weird because we are using id instead of _id
This is how we do it now:
const { mongoose } = require("mongoose");
YourModel.find({ _id: mongoose.Types.ObjectId("572f16439c0d3ffe0bc084a4") });
I got into this scenario too. This was how I solved it;
According to the mongoose documentation, you need to tell mongoose to
return the raw js objects, not mongoose documents by passing the lean option and setting it to true. e.g
Adventure.findById(id, 'name', { lean: true }, function (err, doc) {});
in your situation, it would be
Story.findById(topic.storyId, { lean: true }, function(err, res) {
logger.info("res", res);
assert.isNotNull(res);
});
If _id is the default mongodb key, in your model set the type of _id as this:
_id: mongoose.SchemaTypes.ObjectId
Then usind mongoose you can use a normal find:
YourModel.find({"_id": "5f9a86b77676e180c3089c3d"});
models.findById(id)
TRY THIS ONE .
REF LINK : https://www.geeksforgeeks.org/mongoose-findbyid-function/
Try this
Story.findOne({_id:"572b19509dac77951ab91a0b"}, function(err, story){
if (err){
console.log("errr",err);
//return done(err, null);
}else{
console.log(story);
}
});

Find and delete a field in mongodb using mongoose [duplicate]

I'm using the Mongoose Library for accessing MongoDB with node.js
Is there a way to remove a key from a document? i.e. not just set the value to null, but remove it?
User.findOne({}, function(err, user){
//correctly sets the key to null... but it's still present in the document
user.key_to_delete = null;
// doesn't seem to have any effect
delete user.key_to_delete;
user.save();
});
In early versions, you would have needed to drop down the node-mongodb-native driver. Each model has a collection object that contains all the methods that node-mongodb-native offers. So you can do the action in question by this:
User.collection.update({_id: user._id}, {$unset: {field: 1 }});
Since version 2.0 you can do:
User.update({_id: user._id}, {$unset: {field: 1 }}, callback);
And since version 2.4, if you have an instance of a model already you can do:
doc.field = undefined;
doc.save(callback);
You'll want to do this:
User.findOne({}, function(err, user){
user.key_to_delete = undefined;
user.save();
});
I use mongoose and using any of the above functions did me the requirement. The function compiles error free but the field would still remain.
user.set('key_to_delete', undefined, {strict: false} );
did the trick for me.
At mongo syntax to delete some key you need do following:
{ $unset : { field : 1} }
Seems at Mongoose the same.
Edit
Check this example.
Try:
User.findOne({}, function(err, user){
// user.key_to_delete = null; X
`user.key_to_delete = undefined;`
delete user.key_to_delete;
user.save();
});
if you want to remove a key from collection try this method.
db.getCollection('myDatabaseTestCollectionName').update({"FieldToDelete": {$exists: true}}, {$unset:{"FieldToDelete":1}}, false, true);
Could this be a side problem like using
function (user)
instead of
function(err, user)
for the find's callback ? Just trying to help with this as I already had the case.
Mongoose document is NOT a plain javascript object and that's why you can't use delete operator.(Or unset from 'lodash' library).
Your options are to set doc.path = null || undefined or to use Document.toObject() method to turn mongoose doc to plain object and from there use it as usual.
Read more in mongoose api-ref:
http://mongoosejs.com/docs/api.html#document_Document-toObject
Example would look something like this:
User.findById(id, function(err, user) {
if (err) return next(err);
let userObject = user.toObject();
// userObject is plain object
});
the problem with all of these answers is that they work for one field. for example let's say i want delete all fields from my Document if they were an empty string "".
First you should check if field is empty string put it to $unset :
function unsetEmptyFields(updateData) {
const $unset = {};
Object.keys(updatedData).forEach((key) => {
if (!updatedData[key]) {
$unset[key] = 1;
delete updatedData[key];
}
});
updatedData.$unset = $unset;
if (isEmpty(updatedData.$unset)) { delete updatedData.$unset; }
return updatedData;
}
function updateUserModel(data){
const updatedData = UnsetEmptyFiled(data);
const Id = "";
User.findOneAndUpdate(
{ _id: Id },
updatedData, { new: true },
);
}
I believe that, if you desire remove a specific field into a collection, you should do this:
User.remove ({ key_to_delete: req.params.user.key_to_delete});
you can use
delete user._doc.key

Cannot applying find() method with Native MongoDB becaus of ID type

I have a function that is needed to get results.
When I give 1 as _id filter everything is OK.
collectionPersonnel
.find({ '_id' : 1 })
.toArray(function (err, personnel) {
console.log(personnel);
});
If I give filter another way for instance user[0]['personnel_id'] -that is store 1- then I get only [] result;
collectionPersonnel
.find({ '_id' : user[0]['personnel_id'] })
.toArray(function (err, personnel) {
console.log(personnel);
});
And then I've tried another way. But it doesn't work because I used a string(user[0]['personnel_id']) instead of an ObjectID.
var ObjectID = require('mongodb').ObjectID;
var personnelPK_Hex = (user[0]['personnel_id']).toHexString();
var personnelPK = ObjectID.createFromHexString(personnelPK_Hex);
What should I do?
Edit
All of my codes are below;
module.exports = {
show: function(req, res) {
User.native(function(err, collectionUser) {
if(err) {
console.log("There is no exist a User by current_id");
};
collectionUser
.find({'_id' : req.param('id')})
.toArray(function (err, user) {
Personnel.native(function(err, collectionPersonnel) {
if(err) {
// handle error getting mongo collection
console.log("There is no exist a Personel by current _id");
};
if(!collectionPersonnel) {
console.log("There is no exist a Personel by current _id");
};
// var ObjectID = require('mongodb').ObjectID;
// var personnelPK_Hex = (user[0]['personnel_id']).toHexString();
// var personnelPK = ObjectID.createFromHexString(personnelPK_Hex);
collectionPersonnel
.find({ '_id' : user[0].personnel_id })
.toArray(function (err, personnel) {
console.log(personnel);
});
});
});
});
}
};
And console's output is;
[]
Solved
Just like apsillers's said. I had given a numeric _id to collection, incorrectly.
I've fixed _id value and everything is OK.
Thank you all...
user[0]['personnel_id'] might be a string. For Mongo, "1" is different from 1, which is why your literal number 1 worked, but your variable (which holds a string) does not.
Instead, try using a unary plus to convert the string to a number: +user[0]['personnel_id'].
try to use like user[0].personal_id instead of user[0]['personnel_id'] please provide your schema design that would be better to figure out what exactly you are missing.
i tried like this
collectionPersonnel
.find({ '_id' : user[0].personnel_id })
.toArray(function (err, personnel) {
console.log(personnel);
});

Resources