Mongoose find with or operator failing - node.js

I have an input with autocomplete and I want to find user by _id, name or email.
This query is working fine but it search just using name or email:
var matchRegexp = new RegExp(_.escapeRegExp(req.query.match), 'i');
User.find(
{$or: [{email: matchRegexp}, {name: matchRegexp}]},
{email: 1, name: 1, _id: 0}
).limit(limitResults).exec(
function(error, users) {
if (error) {
return res.status(400).json({
"message": "Query failed",
});
} else {
return res.json({
"results": users
});
}
}
);
If I try to add _id at the $or it fails searching by name or email but it works with _id:
{$or: [{email: matchRegexp}, {name: matchRegexp}, {_id: req.query.match}]}
How can I let it work using all 3 options?
EDIT:
This is the error:
CastError: Cast to ObjectId failed for value "ayeye" at path "_id" for
model "User"
it is not considering at all name and email...

Because your match string is not a valid ObjectId you are getting this error.
So here you can first check the validation for valid ObjectId and then make a criteria accordingly.
const ObjectId = require('mongoose').Types.ObjectId
const criteria = { $or: [{ email: matchRegexp },{ name: matchRegexp }] }
If (ObjectId.isValid(matchRegexp)) {
criteria.$or.push({ _id: matchRegexp })
}
User.find(criteria, { email: 1, name: 1, _id: 0 }).limit(limitResults).exec()

Related

how to get specified info from a collection

i have this schema
const todoSchema = new mongoose.Schema({
name: String,
details: String
})
const userSchema = new mongoose.Schema({
name: String,
todo: [todoSchema]
})
i want a rout that gets a specified todo (based on name) for a specified user
this code
await User.findOne({ 'todo._id': req.body.todo_id, _id: req.body.id })
its get all the information of the user not a Single ToDo
such as if i got this
"user": {
"todo": [
{
"_id": "60fc3bd454b38c19a0afd09a",
"name": "hi",
"details": "hello"
} , {
"_id": "60fc3bd454b38c19a0afd09b",
"name": "bye",
"details": "goodbye"
}
]
}
how can i get the todo[1] details
how should i fix it
$ positional operator is what you want.
await User.findOne(
{ 'todo._id': req.body.todo_id,
_id: req.body.id },{
'todo.$': 1 }
})

MongoDB findOne with multiple query returns wrong results

I am trying to query mongodb documents by passing two fields as arguments in a findOne api. Of the two fields passed to the the query statement only can be true at a time. the code is shown below.
//login user with phone or email
userSchema.statics.loginUser = async (userData) => {
const user = await User.findOne({
$or: [{ email: userData.email }, { phone: userData.phone }],
});
if (!user) {
throw new Error("Wrong username or password one");
}
const isMatch = await bcrypt.compare(userData.password, user.password);
if (!isMatch) {
throw new Error("Wrong username or password");
}
return user;
};
But i have noticed that even if i pass a non existent email of phone, the query always returns an existing document. What am i doing wrong in that query?
I think this is happening because userData.email or userData.phone is null and you have a document in your User collection that also has a null email or phone.
When I add the following three records to my database:
> db.User.insertMany([
... {email: "user1#example.com", phone: "555-1234"},
... {email: "user2#example.com", phone: null},
... {email: null, phone: "555-4321"}])
And then perform the following query using the email address of the first user:
> db.User.findOne({$or: [{ email: "user1#example.com" }, { phone: null }]})
I get back the expected record:
{
"_id" : ObjectId("600724ae7d009a501642a783"),
"email" : "user1#example.com",
"phone" : "555-1234"
}
But if I use the this query with an unknown email address:
> db.User.findOne({$or: [{ email: "user3#example.com" }, { phone: null }]})
Then I get the first document with a null phone because there's no match for the email
{
"_id" : ObjectId("600724ae7d009a501642a784"),
"email" : "user2#example.com",
"phone" : null
}
If I change my query to use an $and clause in each $or clause, I now get null when I use a value that doesn't exist and a null phone
> db.User.findOne({$or: [
... { $and: [{ email: { $ne: null } }, { email: "user3#example.com" }] },
... { $and: [{ phone: { $ne: null } }, { phone: null }] }
... ]})
null
And I can still find a record if it exists and is not null, for example this query:
> db.User.findOne({$or: [
... { $and: [{ email: { $ne: null } }, { email: null }] },
... { $and: [{ phone: { $ne: null } }, { phone: "555-1234" }] }
... ]})
Returns this one matching record:
{
"_id" : ObjectId("600724ae7d009a501642a783"),
"email" : "user1#example.com",
"phone" : "555-1234"
}
I also thought of this solution. I transformed the incoming data and removed the undefined data as shown below
let username = {};
username["email"] = userData.email;
username["phone"] = userData.phone;
Object.keys(username).forEach((key) =>
username[key] === undefined ? delete username[key] : {}
);
const user = await User.findOne(username);

Id not getting removed from nested array

Hello I am trying to remove nested array object id from document but it is not getting removed. Although I am getting message "Deleted"
I have a response where the structure is :-
{
"admins": {
"users": [
"5d0364048db4957100f33fea" //<===want to delete this relational id
],
"email": "1cf1eede89#himail.online",
"password": "$2a$10$vHyGxX9P.t0/ybKcmIzkc.ZCX18oHaVnvTgJIWA2gTNzJ3TCdXS4a",
"_id": "5d0339d5b4b28b6ddff06802",
"companyName": "GH",
"__v": 0
}
I want to delete users _id from the array.
I tried this but it is not getting removed.
router.delete('/userDelete/:uId', checkAuth , (req, res, next) =>{
if(req.userData.role2 === 'admin') {
Admin.findOneAndUpdate({ _id: req.params.userId },{ $pull: { 'admins.users': req.params.uId}})
.exec()
.then(result => {
res.status(200).send(["Deleted"]);
})
.catch(err =>{
if (err.code == 500)
res.status(500).send(["Didn't get deleted"]);
else
return next(err);
});
Controller is like this :-
var admin = new Admin();
admin.companyName = req.body.companyName;
admin.admins = {
email : req.body.email,
password: req.body.password,
users : []
};
I am stuck here , what changes I must do in my route ?
EDIT:- DB is like this
The problem is here Admin.findOneAndUpdate({ _id: req.params.userId }
req.params.userId is undefined, because it does not exist in your path. req.params object holds only one property, uId.
So your query does not find any data.
req.params.userId would have value if your method route had a form like this router.delete('/userDelete/:userId/:uId).
So you could add userId in the url of your delete request and access it through req.params object. The new url should be like this
/userDelete/userId/uId
(e.g.)
userDelete/5d0339d5b4b28b6ddff06802/5d0364048db4957100f33fea
try to hard code your id here
Admin.findOneAndUpdate({ _id: req.params.userId },{ $pull: { 'admins.users': "5d0364048db4957100f33fea"}})
If it is working then do
req.params.uId.toString()
Try marking the '_id' as false in the schema itself for the array. For eg, in your schema, mark '_id' as false like below:
admin: {
type: Object,
users: [{
_id: false
}]
}
.
.
//rest of the schema

Mongodb join query from nodejs

I am new to MongoDB and trying to join two query and store result in a single model. I want to fetch client name from another collection while fetching client task.
Model:-
const mongoose = require('mongoose');
var ObjectId = mongoose.Schema.Types.ObjectId;
const ClientTaskSchema = new mongoose.Schema({
clientId: {
type: Number
},
taskId: {
type: Number
},
clientTaskId: {
type: Number
},
active: {
type: Boolean
}
});
module.exports = mongoose.model('ClientTask', ClientTaskSchema);
Controller:-
module.exports.getClientByTask = function(req, res) {
var query = url.parse(req.url,true).query;
ClientTask.find({taskId: query.taskId}, function(err, clientTask) {
if (err) throw err;
if (!clientTask) {
res.status(200).json({ success: false, message: 'Somthing went wrong. Please contact admin.'});
}
else {
res.status(200).json({ success: true, message: 'Successfull', data: clientTask});
}
});
};
One option is to pass clientId as a reference:
clientId: {
type: mongoose.Schema.Types.ObjectId, ref: 'Client / or whatever your model'
}
Then you'll be able to use Mongoose's populate method http://mongoosejs.com/docs/populate.html
ClientTask
.find({ taskId: query.taskId })
.populate('clientId', { name: 1 }).exec(function (err, clientTask) {
if (!clientTask) {
res.status(404).json({ message: 'Client task not found' })
}
// your logic
});
You can fetch aggregated data with mongodb aggregate. To Calculates aggregate values for the data in a collection:
$lookup Performs a left outer join to an unsharded collection in the same database to filter in documents from the “joined” collection for processing. To each input document, the $lookup stage adds a new array field whose elements are the matching documents from the “joined” collection. The $lookup stage passes these reshaped documents to the next stage.
In your case:
Model.ClientTask.aggregate([
{
$lookup:
{
from: "client",
localField: "_id",
foreignField: "clientId",
as: "clientData"
},
},
{
$project: {
"name": clientData.name, // This name will be client name from client collections
"taskId": 1,
"clientTaskId": 1,
"active": 1
}
}
],
function (err, response) {
console.log(err, response)
});

How to load document with a custom _id by Mongoose?

Here is my schema definition:
var DocSchema = new mongoose.Schema({
_id: {
name: String,
path: String
},
label: String,
...
});
mongoose.model('Doc', DocSchema, 'doc_parse_utf8');
var Doc = mongoose.model('Doc');
And the documents have been inserted to mongodb by other program. Then I tried to query the document:
Doc.findOne({_id:{name:name,path:path}}, function(err, doc){
if (err && err_handler) {
err_handler(err);
} else if(callback) {
callback(doc);
}
});
But, a cast error will be reported:
{ message: 'Cast to ObjectId failed for value "[object Object]" at path "_id"',
name: 'CastError',
type: 'ObjectId',
value: { name: 'mobile', path: 'etc/' },
path: '_id' }
I have searched this problem on mongoose's document, google and statckoverflow.com, however, there's no any solution for me. Please help, thanks.
All you need to do is override the _id type by setting it to Mixed.
var UserSchema = new Schema({
_id: Schema.Types.Mixed,
name: String
});
This causes Mongoose to essentially ignore the details of the object.
Now, when you use find, it will work (nearly as expected).
I'd warn you that you'll need to be certain that the order of properties on the _id object you're using must be provided in the exact same order or the _ids will not be considered to be identical.
When I tried this for example:
var User = mongoose.model('User', UserSchema);
var testId = { name: 'wiredprairie', group: 'abc'};
var u = new User({_id: testId , name: 'aaron'});
u.save(function(err, results) {
User.find().where("_id", testId)
.exec(function(err, users) {
console.log(users.length);
});
});
The console output was 0.
I noticed that the actual data in MongoDB was stored differently than I thought it had been saved:
{
"_id" : {
"group" : "abc",
"name" : "wiredprairie"
},
"name" : "aaron",
"__v" : 0
}
As you can see, it's not name then group as I'd coded. (It was alphabetical, which made sense in retrospect).
So, instead, I did this:
var User = mongoose.model('User', UserSchema);
var testId = { name: 'wiredprairie', group: 'abc'};
var u = new User({_id: testId , name: 'aaron'});
u.save(function(err, results) {
User.find().where("_id", { group: 'abc', name: 'wiredprairie'})
.exec(function(err, users) {
console.log(users.length);
});
});
Then, the console output was 1.
I think you should re-design your schema. If the database is already on service, and can not change it now, you can temporary use this to solve the problem:
mongoose.connection.on('open', function () {
mongoose.connection.db.collection('doc_parse_utf8').find({
_id: {
name: 'mobile',
path: 'etc/'
}
}).toArray(function(err, docs) {
console.log(err || docs)
})
})
As I know if you choose different order of fields in object find method will not work because
_id: {
name: 'mobile',
path: 'etc/'
}
and
_id: {
path: 'etc/',
name: 'mobile'
}
are different keys.

Resources