How to perform findOne query in mongoose - node.js

i have mongoose schema named administrator
var administratorSchema = new mongoose.Schema({
username : String,
password : String,
active : Boolean,
level : String
});
When i try this query,i can get the result
mongoose.connect('mongodb://'+dbServer+'/'+dbName, function(connectionError) {
var administratorModel = mongoose.model('administrators',administratorSchema);
administratorModel.findOne({_id,111155dffxv}function(err, resad){
console.log('into mongoose findone');
});
});
====> Console output : 'into mongoose findone'
The problem is : when i try to change the criteria from _id to "username", mongoose dosen't work and findOne dosen't execute:
mongoose.connect('mongodb://'+dbServer+'/'+dbName, function(connectionError) {
var administratorModel = mongoose.model('administrators',administratorSchema);
administratorModel.findOne({'username','mohamed'}function(err, resad){
console.log('into mongoose findone');
});
});
====> Console output : ''
Thanks.

Your query object isn't valid (use a colon instead of a comma) and you're missing a comma between the findOne parameters. Your call should look like this instead:
administratorModel.findOne({'username': 'mohamed'}, function(err, resad){
console.log('into mongoose findone');
});
You should also be checking the err parameter of your callbacks to see if things are working.
Not sure why it was reaching the callback with your _id criteria version as that one has the same issues.

Related

Cast error while executing a mongoose query

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:

Mongoose findById returns null even with valid id

I have already seen the discussion about the following question with a similar title
mongoose 'findById' returns null with valid id
But my problem is not the database name since all my other connections with the same database in fact the queries on the same collection are working fine.
I am using mongoose 4.13.6, node js 6.11 and mongo 3.4.
It is a post request .
var query=req.body;
I am sending the search parameters as
var findFruit =
{
_id:query._id
}
When I print my findFruit I get :
_id:'5a1cf77920701c1f0aafb85e'
The controller function for this is :
Fruit.findById(findFruit._id,function(err,fruit){
if( _.isNull(err) ){
var response = genRes.generateResponse(true,"found successfully");
callback(response);
}
else{
var response = genRes.generateResponse(false,"there occured some error : "+err);
callback(response);
}
})
I even tried find
Fruit.find(findFruit,function(err,fruit){
if( _.isNull(err) ){
var response = genRes.generateResponse(true,"found successfully");
callback(response);
}
else{
var response = genRes.generateResponse(false,"there occured some error : "+err);
callback(response);
}
})
The collection for sure has the entry under this id .
I went through this git issue as well https://github.com/Automattic/mongoose/issues/3079
Unfortunately I cannot downgrade mongoose as it might affect multiple other working functions.
Edit :
I tried creating ObjectId like :
var mongoose = require('mongoose');
var ObjectID = require('mongodb').ObjectID;
var objectId = new ObjectID();
// Convert the object id to a hex string
var originalHex = objectId.toHexString();
// Create a new ObjectID using the createFromHexString function
var newObjectId = new ObjectID.createFromHexString(query._id);
The model file :
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;
var FruitSchema = new Schema({
name : {type : String, unique : true},
color : {type : String}
});
module.exports = mongoose.model('Fruit', FruitSchema);
All my findById("id") calls returned null.
When looking at the collection with Compass I realized that all my _id elements were Strings. Note that the entire collection was imported.
I created a new element in Compass and the _id was created as ObjectId! and when I called findById("id") with that element's id it worked!
My conclusion is that there is obviously a bug with import. I have not found a way to convert the string _id fields to ObjectId's in the actual collection.
All my findById("id") calls returned null, when _id elements are Strings.
In the first place:
Check your mongodb database, if _id is stored as String, findById(id) can not find since it identifies ObjectId. If you've used import database by using mongoimport command and including _id in JSON:
Solution 1:
modify your JSON and for each document, change _id for instance:
_id: "5a68fde3f09ad7646ddec17e" to the following and run mongoimport again:
"_id": { "$oid": "5a68fde3f09ad7646ddec17e" }
Solution 2:
delete _id in the JSON file, drop collection and import again. Mongo will auto-create _id.
After any of solutions above, findById("id") will work.
Secondly:
Specifically in such cases where your _id elements are string, might be a better idea to use mongodb package: npm i mongodb
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
MongoClient.connect(url, function (err, db) {
if (err) throw err;
var dbo = db.db("your_db_name");
dbo.collection("fruits_collection")
.find({_id:'5a1cf77920701c1f0aafb85e'})
//.find({_id:'5a1cf77920701c1f0aafb85e'}, { projection: { _id: 0, name: 1, color: 1} }) // to select specific columns desired
.toArray(function (err, result) {
if (err) throw err;
console.log(result);
db.close();
});
});
The above simple code, assumed you manage error handling yourself, either through try-catch, or
sending 404 as status code, or redirect to error page template, depending on whether the code is embedded in the Express route handler or not.
Hope this helped .. :)
Still trying to figure out why findById didn't work for me but the following piece of code did it
var mongoose = require('mongoose');
var newObjectId=new mongoose.Types.ObjectId(query._id);
var params={
'_id':newObjectId
}
Fruit.find(params).exec(function (err,fruit) {
if( _.isNull(err) ){
var response = genRes.generateResponse(true,"found successfully");
callback(response);
}
else{
var response = genRes.generateResponse(false,"there occured some error : "+err);
callback(response);
}
})
#Shoom. Yes, worked for me, thanks. findById() expects ObjectID, not a String.
I did not have a constraint to create documents with a specific id, so I imported with no _id. The db had newly-assigned _id as ObjectID.
findById(id), (and updateOne({ _id: id }, ...), started working as expected.

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

findAndModify or findOneAndUpdate - "is not a function"

First of all, which one is best?
findAndModify or findOneAndUpdate or findByIdAndUpdate?
In my case I got a table like this:
seqkeys
{
"_id" : "invoice",
"seq" : NumberInt(1)
},
{
"_id" : "receipt",
"seq" : NumberInt(1)
}
And I want to find the seq number for invoice, add 1 and save. (and then use that number to save the real invoice record in the invoice table)
But I cant get this to work, I constantly get
TypeError: seqkeysTable.findAndModify is not a function
So here is my model:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var seqkeysSchema = new Schema({
_id: String, // Table Name
seq: Number // Sequence Number
});
module.exports = mongoose.model('seqkeysModel', seqkeysSchema,'seqkeys');
And here is my findkey nodejs function....
var seqkeysModel = require('../../models/seqkeysModel');
var seqkeysTable = mongoose.model('seqkeysModel');
var findKey = function(tableName) {
return new Promise((resolve, reject) => {
seqkeysTable.findAndModify(
{ "_id": tableName },
{ $inc: {"seq":1} },
{ new: true }
,function (err, data) {
if (err) {
reject(new Error('findKey ERROR : ' + err));
return;
} else {
resolve(data.seq);
};
});
})};
Mongoose has findOneAndUpdate (equivalent of mongoBD findAndModify. MongoDB also introduced findOneAndUpdate in version >= 3.2) and findByIdAndUpdate. That's why findAndModify won't work with Mongoose.
As to which one is better, in findOneAndUpdate you can pass query object containing multiple fields while findByIdAndUpdate only takes id as the first parameter.
findAndModify is a function of MongoDB, That's why that doesn't work with Mongoose.
Both findOneAndUpdate and findByIdAndUpdate works well with Mongoose, perhaps if you wish to use _id to search in the database then go for findByIdAndUpdate else if you are using a different field to search in the database then use findOneAndUpdate.
However, if you try to use findAndModify in MongoDB, you might get the following warning :
DeprecationWarning: collection.findAndModify is deprecated. Use findOneAndUpdate, findOneAndReplace or findOneAndDelete instead.
To eliminate this use the following in your code :
mongoose.set('useFindAndModify', false);
Just in case, difference between findByIdAndUpdate & findOneAndUpdate :
findByIdAndUpdate(id, ...) is equivalent to findOneAndUpdate({ _id: id
}, ...)

Why the callback in mongoose code getting document as null?

I have following code:
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test')
var Schema = mongoose.Schema;
var schema = new Schema({name : String, inventory: {}});
var Character = mongoose.model('Character',schema);
var Frodo = new Character({name: 'Frodo',inventory:{ringOfPower: 1}});
Frodo.save();
Character.findOne({name : 'Frodo'},function(error, character){
console.log(character);
});
I am running it as node optionminimize.js and null is getting logged to the console.It got persisted in mongodb alright, I opened a mongodb shell and ran
db.characters.findOne({name:'Frodo'})
and get following result:
{
"_id" : ObjectId("582fc3bab386f00ed0e7fede"),
"name" : "Frodo",
"inventory" : {
"ringOfPower" : 1
},
"__v" : 0
}
Why the document parameter in the callback is receiving null?
Frodo.save() is a async call so it returns a Promise, and you search by Charachter.findOne immediately the line after which happens before the Frodo.save() promise is resolved.
So, the search must be executed after the character is saved successfully, otherwise it returns null as you mentioned, so your code need to be like this:
Frodo.save()
.then(function() {
Character.findOne({name : 'Frodo'}, function(error, character) {
console.log(character);
});
})

Resources