Match query in Mongoose for arrays does not work - node.js

Below you can find my code where I try to exclude from result all users who have authorized user in any of following lists: friends, sentFriendRequests and receiveFriendRequests. Unfortunately the code is not working (other conditions work perfect e.g. filter by id, fullName and email), but I can not figure out why. Thanks in advance for any kind of help.
User.findOne({_id: req.user.id})
.exec(function (err, user) {
if (err) {
logger.error('Friend 500 ' + err)
return res.status(500).json({
code: config.errorCode.status500.code,
message: config.errorCode.status500.message
})
}
console.log('User id: ' + user._id)
User.aggregate([
{
$project: {
fullName: {
$concat: ['$firstName', ' ', '$lastName']
},
email: 1,
avatarPath: 1,
mutualFriends: {
$size: {
$setIntersection: [user.friends, '$friends']
}
}
}
},
{
$match: {
$and: [
{
_id: { $ne: user._id }
},
{
$or: [
{
fullName: { $regex: req.query.keyWord, $options: 'i'}
},
{
email: { $regex: req.query.keyWord, $options: 'i'}
}
]
},
{
friends: { $ne: user._id }
},
{
receivedFriendRequests: { $ne: user._id }
},
{
sentFriendRequests: { $ne: user._id }
}
]
}
},
{ $sort: { mutualFriends: -1} }
])
.exec(function (err, result) {
if (err) {
logger.error('Friend 500 ' + err)
return res.status(500).json({
code: config.errorCode.status500.code,
message: config.errorCode.status500.message
})
}
return res.json({
data: result
})
})
})
Schema Definition
var UserSchema = new Schema({
firstName: {
type: String
},
frstNameNormalized: {
type: String
},
lastName: {
type: String
},
lastNameNormalized: {
type: String
},
email: {
type: String,
// unique: true,
// required: true,
lowercase: true,
// match: [/^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/, 'Please enter a valid email address']
},
birthDate: {
type: Date
},
facebookID: {
type: String
},
twitterID: {
type: String
},
password: {
type: String
},
nickname: {
type: String,
default: ''
},
description: {
type: String,
default: ''
},
avatarPath: {
type: String,
default: ''
},
friends: [{ type: Schema.Types.ObjectId, ref: 'User' }],
receivedFriendRequests: [{ type: Schema.Types.ObjectId, ref: 'User' }],
sentFriendRequests: [{ type: Schema.Types.ObjectId, ref: 'User' }],
sharedFriendCheckIns: [{ type: Schema.Types.ObjectId, ref: 'User' }],
resetPasswordToken: String,
resetPasswordExpires: Date,
emailConfirmToken: String,
emailConfirmExpires: Date,
emailConfirmed: {
type: Boolean,
default: false
}
})
....
var User = module.exports = mongoose.model('User', UserSchema)
A full model description can be found on the following link. MongoDB version is v3.2.8

In case that someone needs it, I have resolved this problem. Since I am a beginner the solution is trivial and probably not optimal but it does what it should to.
FriendController.route('/find')
.get(passport.authenticate('jwt', { session: false }), function (req, res) {
// get users who are not firends of authorized user nor he/she send to them friend request or received it
var parts = (req.query.keyWord).split(' ')
var firstName = ''
var lastName = ''
if (parts.length > 1) {
firstName = (req.query.keyWord).split(' ').slice(0, -1).join(' ')
lastName = (req.query.keyWord).split(' ').slice(-1).join(' ')
} else {
firstName = req.query.keyWord
lastName = req.query.keyWord
}
User.findOne({_id: req.user.id})
.exec(function (err, user) {
if (err) {
logger.error('Friend 500 ' + err)
return res.status(500).json({
code: config.errorCode.status500.code,
message: config.errorCode.status500.message
})
}
User.find({
_id: { $ne: user.id },
friends: { $ne: user.id },
receivedFriendRequests: { $ne: user.id },
sentFriendRequests: { $ne: user.id },
$or: [
{
firstName: { $regex: firstName, $options: 'i'}
},
{
lastName: { $regex: lastName, $options: 'i'}
},
{
email: req.query.keyWord
}
]
})
.select('_id')
.exec(function (err, potentialFriends) {
if (err) {
logger.error('Friend 500 ' + err)
return res.status(500).json({
code: config.errorCode.status500.code,
message: config.errorCode.status500.message
})
}
var ids = potentialFriends.map(function (potentialFriend) {
return potentialFriend._id
})
User.aggregate([
{
$project: {
firstName: 1,
lastName: 1,
email: 1,
avatarPath: 1,
mutualFriends: {
$size: {
$setIntersection: [user.friends, '$friends']
}
}
}
},
{
$match: {
_id: { $in: ids }
}
},
{ $sort: { mutualFriends: -1} }
])
.exec(function (err, result) {
if (err) {
logger.error('Friend 500 ' + err)
return res.status(500).json({
code: config.errorCode.status500.code,
message: config.errorCode.status500.message
})
}
return res.json({
data: result
})
})
})
})
})

Related

I want to pass array object inside array subdocument in mongoose

Here is my Schema
I am trying to add replies array inside answers array. If someone answers a question and if someone wants to reply on the given answer
const mongoose = require("mongoose");
const { ObjectId } = mongoose.Schema;
const questionSchema = new mongoose.Schema(
{
postedBy: {
type: ObjectId,
required: true,
ref: "User",
},
question: {
type: String,
required: true,
},
photo: {
data: String,
required: false,
},
answers: [
{
userId: { type: ObjectId, ref: "User" },
answerType: {
data: String,
required: false,
},
answer: String,
replies: [
{
userId: { type: ObjectId, ref: "User" },
reply: String,
replyType: {
data: String,
required: false,
},
},
],
},
],
questionType: {
data: String,
required: false,
},
createdAt: {
type: Date,
required: true,
default: Date.now,
},
},
{ timeStamps: true }
);
module.exports = mongoose.model("Question", questionSchema);
Here is my Controller method
exports.postReply = (req, res) => {
const reply = req.body.reply || "";
const userId = req.user._id || "";
const answerId = req.body.answerId || "";
Question.findByIdAndUpdate(
{ _id: answerId },
({ $push: { answers: { answer: { replies: { reply, userId } } } } },
{ new: true }),
(err, newReply) => {
if (err) {
res.status(400).json({
error: errorHandler(err),
});
} else {
res.json({
msg: "Reply posted successfully",
newReply,
});
}
}
);
};
I feel I am going wrong on the findOneAndUpdate method. I am getting no error on the console but newReply comes null. Any help will be appreciated.
I would suggest you using the $addToSet instead of the $push operator as you are adding a document to the array. (see: https://docs.mongodb.com/manual/reference/operator/update/addToSet/).
If you want to add more than one document to the array, refer also to the $each operator together with $addToSet.
So your coding can look similiar to this (note: the variable 'yourDocument' is the document you want to add):
Question.findByIdAndUpdate(
{ _id: answerId },
{ $addToSet: { answers: yourDocument } },
{ new: true },
(err, newReply) => {
if (err) {
res.status(400).json({
error: errorHandler(err),
});
} else {
res.json({
msg: "Reply posted successfully",
newReply,
});
}
}
);
};
The problem is clearly the parentesis around
({ $push: { answers: { answer: { replies: { reply, userId } } } } }, { new: true })
Doing this console.log( ({a:1}, {b:2}) ); will log {b: 2} which means you are doing this
Question.findByIdAndUpdate( { _id: answerId }, { new: true }, (err, newReply) => {
So remove the parentesis and you should be good
Question.findByIdAndUpdate(
{ _id: answerId },
{ $push: { answers: { answer: { replies: { reply, userId } } } } },
{ new: true },
(err, newReply) => {
if (err) {
res.status(400).json({
error: errorHandler(err),
});
} else {
res.json({
msg: "Reply posted successfully",
newReply,
});
}
}
);

sequelize includes not returning data

I am trying to get data mapped with empid from 2 tables viz-skillsrepo and certifications and render it to frontend,I am getting all data from certifications table,but i need data only of the empid which i send in request
tried using includes method
app.get('/updateprofile/:id', function (req, res) {
db.skillsrepo.find({
where: { employeeId: req.params.id },
include: [
{
model: db.certifications
},
{
model: db.attachments
},
{
model: db.project
}
]
}).then(result => {
if (result != null) {
res.render('updateprofile', {
user: result,
eid: req.params.id,
});
console.log("**********", result)
}
})
});
This is the Schema:
var skillsrepo = exports.skillsrepo = connection.define('skillsrepo', {
firstname: {
type: Sequelize.STRING(23)
},
lastname: {
type: Sequelize.STRING(23)
},
highQual: {
type: Sequelize.STRING(23)
},
fivekeystrenghts: {
type: Sequelize.TEXT
},
domain: {
type: Sequelize.STRING(23)
},
technicalskills: {
type: Sequelize.STRING(23)
},
typesoftesting: {
type: Sequelize.STRING(23)
},
employeeId: {
type: Sequelize.INTEGER(11),
references: {
model: 'employeemastertablee',
key: 'id'
}
}
});
skillsrepo.hasMany(certifications, {
foreignKey: "employeeId"
});
certifications.belongsTo(skillsrepo, {
foreignKey: "employeeId"
});

Sequelize: OR between parent where clause and child where clause

I have 2 models:
const User = sequelize.define('User', {
email: {
type: DataTypes.STRING,
},
password: {
type: DataTypes.STRING,
},
});
User.associate = (models) => {
User.hasOne(models.Profile, {
foreignKey: {
name: 'user_id',
},
});
};
const Profile = sequelize.define('Profile', {
name: {
type: DataTypes.STRING,
},
avatar: {
type: DataTypes.STRING,
},
}, {
tableName: 'profiles',
freezeTableName: true,
timestamps: false,
});
Profile.associate = (models) => {
Profile.belongsTo(models.User, {
foreignKey: {
name: 'user_id',
},
});
};
I would like to get all users where the email address OR the name matches a certain condition. Something like:
User
.all({
where: {
email: {
$like: filter
},
},
include: [{
model: Profile,
where: {
name: {
$like: filter
},
},
}],
})
.then(users => res.status(200).send(users))
.catch(error => {
return res.sendStatus(500);
});
but it returns all users where user.email AND profile.name matches the condition. I would like to have OR between the 2 where clause.
Is it possible?
Note:
I'm using Sequelize 4.0.0.
Update:
In case of anybody else struggles with this, the solution is:
User
.all({
where: {
$or: {
email: {
$like: filter
},
'$Profile.name$': {
$like: filter
}
}
},
include: [{
model: Profile,
}],
})
.then(users => res.status(200).send(users))
.catch(error => {
return res.sendStatus(500);
});
In case if anyone else is looking for this, here is how I managed to solve it:
User
.all({
where: {
$or: {
email: {
$like: filter
},
'$Profile.name$': {
$like: filter
}
}
},
include: [{
model: Profile,
}],
})
.then(users => res.status(200).send(users))
.catch(error => {
return res.sendStatus(500);
});
Thanks #Ninja Coding for confirming the solution.

Failed to update single property by mongoose

Frist I have read and try the solution in the post of mongoose-and-partial-select-update.
However when I try to use the traditional style, query would work.
My schema:
var userSchema = mongoose.Schema({
local: {
email: {
type: String,
index: {
unique: true,
dropDups: true
}
},
password: String,
displayName: String,
avatar: {
type: String,
default: "./img/user.png"
},
role: {
type: String,
default: "student"
},
ask_history: [
{
question_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'questionAnswer'
},
favorite: Boolean,
ask_time: Date
}
],
interest: [String]
}
})
Working Update function:
User.findById(id, function(err, User) {
if (err) {
throw done(err);
}
if (!User) {
return;
}
User.local.role = "admin";
User.save(function(err, updatedUser) {
if (err) {
throw err
} else {
//good
}
})
});
However if I do this:
User.update({_id : id},
{$set{
local:{role:"admin"}
}
},function(...){...}
});
Code above will overwrite user into:
{
_id : "...",
local: {
role : "admin"
}
}
I read that $ will make the update only changing property, where I did wrong?
The positional operator $ works with array of subdocuments.
In your case you have a single sub-document, so the following should work:
User.update({_id : id},
{ $set
{
"local.role": "admin"
}
}, function(...){...}
});

Issues With Mongoose $push

I really just need a second set of eyes here. I am using the Mongoose npm to create a new entry in my MongoDB. Then I am using that new entry in a few functions in the Async npm.
The issue that I am having is that I am getting the first three console logs, "hitter", "create", and "req.body.campaign_id" but nothing past that. I think it has to do with my $push in the first findByIdAndUpdate. Please see my code and schema below.
Code! See async parallel "campaign" function
Bid.create(req.body, function(err, bid){
console.log('create')
async.parallel({
campaign: function(done) {
console.log(req.body.campaign_id)
Camapaign.findByIdAndUpdate(req.body.campaign_id, {
$push: { bids: bid._id }
}, {
safe: true,
upsert: true
}, function(err, campaign){
console.log('camp', 2)
if(err) {
console.log(err)
done(err)
} else {
done(null, campaign)
}
});
},
user: function(done) {
console.log('user', 1)
User.findByIdAndUpdate(req.body.user_id, {
$push: {'bids': bid._id }
}, {
safe: true,
upsert: true
}, function(err, bid){
console.log('user', 2)
if(err) {
done(err)
} else {
done(null, bid)
}
});
}
}, function(err, response){
console.log('response')
if(err) {
console.log(err)
} else {
res.status(200).send(response);
}
});
})
Campaign Schema
var campaignSchema = new mongoose.Schema({
title:String,
imgUrl:[String],
shortDesc: { type: String, set: shortenDesc },
longDesc:String,
duration: Number,
price: Number,
desired_price: Number,
bids: [{ type: mongoose.Schema.Types.ObjectId, ref: 'bidSchema' }],
owner_id: { type: mongoose.Schema.Types.ObjectId, ref: 'userSchema' }
});
User Schema
var schema = new mongoose.Schema({
name: String,
email: {
type: String
},
password: {
type: String
},
salt: {
type: String
},
twitter: {
id: String,
username: String,
token: String,
tokenSecret: String
},
facebook: {
id: String
},
google: {
id: String
},
campaigns: [campaignSchema],
bids: [{type: mongoose.Schema.Types.ObjectId, ref: 'bidSchema'}]
});
Please let me know if you need to see anything else. All help is appreciated.
Thanks!
You are doing Camapaign.findByIdAndUpdate are you sure Camapaign isn't mispelled there? Shouldn't it be Campaign?

Resources