find by _id with Mongoose - node.js

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);
}
});

Related

How to save the result in variable of findOne() Mongoose using nodejs

I am trying to save the result of findOne(), however, I do not have any idea how to save this result in a variable.
function createSubscription (req, res, next) {
let product_id = "P-5JM98005MT260873LLT44E2Y" ;
let doc = null;
product.findOne({"plans.id": product_id}, { "plans.$": 1
}).sort({create_time: -1}).exec(function(err, docs) {
if(err) {
} else {
if(docs != null) {
this.doc = docs;
}
}
});
console.log(doc);
let result = null;
if (doc.create != null) {
result = processDoc(doc);
}
}
function processDoc(doc) {
//do something
return resul;
}
function processResult(result) {
//do something
}
Below, I copy the product schema
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const ProductSchema = new Schema({
id: {
type: String,
trim: true,
required: true,
},
name: {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
create_time: {
type: Date,
required: true,
}
});
module.exports = mongoose.model('Product', ProductSchema);
The doc is always null and does not receive the value.
In general terms, I would like to get the response product.findOne to use another function, calling by createSubscription()
To save the result of findOne() is as easy as this:
var doc = product.findOne();
The problem you're having is that you are calling processDoc() before findOne() is finished. Look into asynchronous javascript. You can fix this by using async/await like this:
async function createSubscription (req, res, next) {
var doc = await product.findOne();
processDoc(doc);
}
The reason is you are calling a callback function, which means function is asynchronous. So there is no guarantee that your query will execute and the values will be set before it reaches to,
if (doc.create != null) {
result = processDoc(doc);
}
To resolve this you may use the Async/Await Syntax:
const createSubscription = async function (params) {
try { return await User.findOne(params)
} catch(err) { console.log(err) }
}
const doc = createSubscription({"plans.id": product_id})
Since you want to query something in database, that means you already created one and saved some data in it.
However before saving data in the database you should be creating your model which should be created under models folder in Product.js(model names should start capital letter in convention) . Then you have to IMPORT it in your above page to access it. You want to query by product.id but which product's id?
Since you have req in your function, that means you are posting something. If you set up the proper settings in app.js you should be able to access to req.body which is the information that being posted by the client side.
app.js //setting for parsing(reading or accessing) req.body
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
Now we can reach req.body.id
this should be your code:
function createSubscription (req, res, next) {
const product=Product.findOne({plan.id:req.body.id})
.
.
}
If you noticed I did not put this plan.id in quotes.Because findOne() method belongs the Product model and that model belongs to the package that you are using. (In mongoose you query without quotes, in mongodb client you query in quotes)
findOne() is an asynchronous operation means result will not come to you immediately. So you should always keep it in try/catch block.
function createSubscription (req, res, next) {
try{
const product=Product.findOne({plan.id:req.body.id})
}
catch(error){console.log(error.message)} //every error object has message property
.
.
}
Lastly since you are querying only one object you do not need to sort it.

How can I retrieve documents' properties from a pre hook?

I posted this question yesterday because I didn't know how to solve my problem.
Change variable value in document after some time passes?
I was told I need to use a pre hook. I tried to do it, but "this" would refer to the query, not to the document. So I couldn't retrieve the documents to check if the 4 weeks passed. (check the question, you will get it)
Because I don't know how to make this .pre('find') to use variables from each of my document (so it checks if the 4 weeks passed) I was thinking about looping through all of them and checking if 4 weeks passed.
router.get('/judet/:id([0-9]{2})', middleware.access2, function(req, res)
{
var title = "Dashboard";
Somer.find({}, function(err, someri)
{
if(err)
{
console.log(err);
}
else
{
res.render("dashboard", {title: title, id:req.params.id, someri:someri});
}
});
}); ///get route
var someriSchema = new mongoose.Schema({
nume: {type: String, required: true},
dateOfIntroduction: {type:Date, default: Date.now, get: formatareData},
});
someriSchema.pre('find', function(next) {
console.log(this.dateOfIntroduction); <- this will return undefined, because this refers to the query, actually
next();
});///schema and the pre hook. I thought I could use it like this, and inside the body of the pre hook I can check for the date
Here's what I am talking about:
router.get('/judet/:id([0-9]{2})', middleware.access2, function(req, res)
{
var title = "Dashboard | Best DAVNIC73";
Somer.find({}, function(err, someri)
{
if(err)
{
console.log(err);
}
else
{
someri.forEach(function(somer)
{
///check if 4 weeks passed and then update the deactivate variable
})
res.render("dashboard", {title: title, id:req.params.id, someri:someri});
}
});
});
but I think this will be very bad performance-wise if I will get many entries in my DBs and I don't think this is the best way to do this.
So, if I was told correctly and I should use a pre hook for obtaining what I've said, how can I make it refer to the document?
Ok, I think I understood your requirements. this is what you could do:
/*
this will always set a documents `statusFlag` to false, if the
`dateOfIntroduction` was before Date.now()
*/
const mongoose = require('mongoose')
someriSchema.pre('find', function(next) {
mongoose.models.Somer.update(
{ datofIntroduction: { $lte: new Date() }},
{ statusFlag : false})
.exec()
.then((err, result) => {
// handle err and result
next();
});
});
The only problem I see, is that you are firing this request on every find.
in query middleware, mongoose doesn't necessarily have a reference to
the document being updated, so this refers to the query object rather
than the document being updated.
Taken straight from the documentation of mongoose
I pointed you yesterday to their documentation; but here is a more concrete answer.
someriSchema.post('find', function(res) {
// res will have all documents that were found
if (res.length > 0) {
res.forEach(function(someri){
// Do your logic of checking if 4 weeks have passed then do the following
someri.deactivated = true
someri.save()
})
}
})
What this basically do is for every found schema you would update their properties accordingly, your res can have only 1 object if you only queried 1 object. your second solution would be to do the cron
EDIT: This is what you would do to solve the async issue
const async = require('async')
someriSchema.post('find', function(res) {
async.forEach(res, function(someri, callback) {
// Do your logic of checking if 4 weeks have passed
// then do the following - or even better check if Date.now()
// is equal to expiryDate if created in the model as suggested
// by `BenSow`
// Then ONLY if the expiry is true do the following
someri.deactivated = true
someri.save(function (err) {
err ? callback(err) : callback(null)
})
}, function(err){
err ? console.log(err) : console.log('Loop Completed')
})
})

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);
});

NodeJS + MongoDB: Getting data from collection with findOne ()

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" })
}

mongodb/mongoose findMany - find all documents with IDs listed in array

I have an array of _ids and I want to get all docs accordingly, what's the best way to do it ?
Something like ...
// doesn't work ... of course ...
model.find({
'_id' : [
'4ed3ede8844f0f351100000c',
'4ed3f117a844e0471100000d',
'4ed3f18132f50c491100000e'
]
}, function(err, docs){
console.log(docs);
});
The array might contain hundreds of _ids.
The find function in mongoose is a full query to mongoDB. This means you can use the handy mongoDB $in clause, which works just like the SQL version of the same.
model.find({
'_id': { $in: [
mongoose.Types.ObjectId('4ed3ede8844f0f351100000c'),
mongoose.Types.ObjectId('4ed3f117a844e0471100000d'),
mongoose.Types.ObjectId('4ed3f18132f50c491100000e')
]}
}, function(err, docs){
console.log(docs);
});
This method will work well even for arrays containing tens of thousands of ids. (See Efficiently determine the owner of a record)
I would recommend that anybody working with mongoDB read through the Advanced Queries section of the excellent Official mongoDB Docs
Ids is the array of object ids:
const ids = [
'4ed3ede8844f0f351100000c',
'4ed3f117a844e0471100000d',
'4ed3f18132f50c491100000e',
];
Using Mongoose with callback:
Model.find().where('_id').in(ids).exec((err, records) => {});
Using Mongoose with async function:
const records = await Model.find().where('_id').in(ids).exec();
Or more concise:
const records = await Model.find({ '_id': { $in: ids } });
Don't forget to change Model with your actual model.
Combining Daniel's and snnsnn's answers:
let ids = ['id1', 'id2', 'id3'];
let data = await MyModel.find({
'_id': {
$in: ids
}
});
Simple and clean code. It works and tested against:
"mongodb": "^3.6.0",
"mongoose": "^5.10.0",
Use this format of querying
let arr = _categories.map(ele => new mongoose.Types.ObjectId(ele.id));
Item.find({ vendorId: mongoose.Types.ObjectId(_vendorId) , status:'Active'})
.where('category')
.in(arr)
.exec();
This code works for me just fine as of mongoDB v4.2 and mongoose 5.9.9:
const Ids = ['id1','id2','id3']
const results = await Model.find({ _id: Ids})
and the Ids can be of type ObjectId or String
Both node.js and MongoChef force me to convert to ObjectId. This is what I use to grab a list of users from the DB and fetch a few properties. Mind the type conversion on line 8.
// this will complement the list with userName and userPhotoUrl
// based on userId field in each item
augmentUserInfo = function(list, callback) {
var userIds = [];
var users = []; // shortcut to find them faster afterwards
for (l in list) { // first build the search array
var o = list[l];
if (o.userId) {
userIds.push(new mongoose.Types.ObjectId(o.userId)); // for Mongo query
users[o.userId] = o; // to find the user quickly afterwards
}
}
db.collection("users").find({
_id: {
$in: userIds
}
}).each(function(err, user) {
if (err) {
callback(err, list);
} else {
if (user && user._id) {
users[user._id].userName = user.fName;
users[user._id].userPhotoUrl = user.userPhotoUrl;
} else { // end of list
callback(null, list);
}
}
});
}
if you are using the async-await syntax you can use
const allPerformanceIds = ["id1", "id2", "id3"];
const findPerformances = await Performance.find({
_id: {
$in: allPerformanceIds
}
});
I tried like below and it works for me.
var array_ids = [1, 2, 6, 9]; // your array of ids
model.find({
'_id': {
$in: array_ids
}
}).toArray(function(err, data) {
if (err) {
logger.winston.error(err);
} else {
console.log("data", data);
}
});
I am using this query to find the files in mongo GridFs. I wanted to get the by its Ids.
For me this solution is working: Ids type of ObjectId.
gfs.files
.find({ _id: mongoose.Types.ObjectId('618d1c8176b8df2f99f23ccb') })
.toArray((err, files) => {
if (!files || files.length === 0) {
return res.json('no file exist');
}
return res.json(files);
next();
});
This is not working: Id type of string
gfs.files
.find({ _id: '618d1c8176b8df2f99f23ccb' })
.toArray((err, files) => {
if (!files || files.length === 0) {
return res.json('no file exist');
}
return res.json(files);
next();
});

Resources