Mongo DB / Mongoose Conditional Inclusion Search - node.js

I have a simple feature that may involve a complicated query. I want to allow users to only be shown to members they've already given approval to find them in my site.
The trouble is I have no idea how to add a conditional and then query within the queried users.
A user calls find.
If the searched users have "Approval Only" selected.
Check to see if the searching user is in their approved list.
I am using NodeJS with Mongoose for my finds.
Please help.
Schema below:
const ProfileSchema = new mongoose.Schema({
userDOBs: {
type: [Date],
required: true
},
gender: {
type: String,
required: true
},
active: {
type: Boolean,
default: true
},
discoverySettings: {
approvedOnly: {
type: Boolean,
default: false
}
},
blockedProfileIDs: {
type: [mongoose.Schema.Types.ObjectId],
ref: "Profile"
},
approvedIDs: [
{
type: [mongoose.Schema.Types.ObjectId],
ref: "Profile"
}
]
});
Query Below:
const profiles = await Profile.find({
$and: [
{
_id: {
$ne: req.user.profileID,
$nin: doNotDisplayList
},
"loc.loc": {
$nearSphere: [long, lat],
$maxDistance: maxDistance
},
userDOBs: {
$lt: moment().subtract(filter.searchParams.ageRange[0], "years"),
$gt: moment().subtract(filter.searchParams.ageRange[1], "years")
},
active: true,
"discoverySettings.visible": true,
blockedProfiles: {
$ne: req.user.profileID
}
}
]
})
.sort({
updatedAt: -1
})
.limit(limit)
.skip(skip);

Related

Mongoose search query with regex not returning expected results with multiple conditions and population

export const searchPost = async (req: any, res: Response) => {
try {
const searchQuery = req.params.query;
const page = req.query.page || 1;
const limit = req.query.limit || 10;
const skip = (page - 1) * limit;
const posts = await Post.find({
$or:
[
{ content: { $regex: searchQuery, $options: 'i' } },
{ location: { $regex: searchQuery, $options: 'i' } },
{ 'user.fullName': { $regex: searchQuery, $options: 'i' } },
{ 'user.username': { $regex: searchQuery, $options: 'i' } },
{ 'group.name': { $regex: searchQuery, $options: 'i' } }
]
})
.populate('user')
.populate('group')
.skip(skip)
.limit(limit)
.sort({ createdAt: -1 });
res.json(posts);
} catch (err) {
console.log(err);
return res.status(500).json({ message: 'Something went wrong!' });
}
};
Post Modal
const PostSchema = new Schema(
{
content:
{
type: String,
required: true
},
location:
{
type: String
},
image:
{
type: String
},
user:
{
type: Schema.Types.ObjectId,
ref: 'User',
required: true
},
group:
{
type: Schema.Types.ObjectId,
ref: 'Group'
},
comments:
[
{
type: Schema.Types.ObjectId,
ref: 'Comment'
}
],
likesCount:
{
type: Number,
default: 0
},
likesUsers:
[
{
type: Schema.Types.ObjectId,
ref: 'User'
}
]
},
{ timestamps: true }
);
const Post = mongoose.model('Post', PostSchema);
export default Post;
User Model
const UserSchema = new Schema(
{
fullName:
{
type: String,
required: true,
index: true
},
username:
{
type: String,
required: true,
index: true
},
email:
{
type: String,
required: true,
unique: true
},
password:
{
type: String,
required: true
},
profileImg:
{
type: String,
default:
'https://res.cloudinary.com/dyfm31f1n/image/upload/v1675059905/fit-fiesta/placeholders/blank-profile-picture-gdb207bae8_1280_zymz7e.png'
},
coverImg:
{
type: String,
default:
'https://res.cloudinary.com/dyfm31f1n/image/upload/v1675059731/fit-fiesta/placeholders/bg_qr4vtm.jpg'
},
location:
{
type: String
},
weight:
{
type: Number
},
height:
{
type: Number
},
targetWeight:
{
type: Number
},
groups:
[
{
type: Schema.Types.ObjectId,
ref: 'Group'
}
],
events:
[
{
type: Schema.Types.ObjectId,
ref: 'Event'
}
],
posts:
[
{
type: Schema.Types.ObjectId,
ref: 'Post'
}
],
connections:
[
{
type: Schema.Types.ObjectId,
ref: 'User'
}
],
pendingConnections:
[
{
type: Schema.Types.ObjectId,
ref: 'User'
}
]
},
{ timestamps: true }
);
The Post model has a reference to a User model and a Group model. The method populates these references with their corresponding data using the populate method. The result of the query is sorted in descending order of creation time and sent as a response to the client.
Here in searchPost API its not considering user relation fields such as user.username and user.fullName or group.name while finding with regex
The find, skip, limit, and sort are performed by the mongodb database on the server side. The result is then sent back to mongoose where the populate is performed by submitting additional queries.
In the 'post' document in the database, the 'user' and 'group' fields contains an ObjectId, not an object, so the fields `user.fullName', 'user.username', and 'group.name' don't exist, and therefore don't match.
In order to filter these fields on the database server, you would need to use aggregate with separate $lookup stages to retrieve the user and group documents in order for the server to consider those fields.

updateMany and elemMatch in with nested schemas in mongoose (Node.js)

I'm trying to query a MongoDB database via mongoose to updateMany the fields of my database. I suppose that the first request is correct because mongoose doesn't fire any error, but for the nested schemas, I'm getting the following error.
My goal is to delete the occurences of the userTag in friends and remove the friendRequestsSent when userTarget equals userTag, friendRequestsReceived when userRequest equals userTag and notification when data equals userTag.
Here are the schemas of my Model
const NotificationSchema = new Schema({
title: String,
type: Number,
icon: String,
data: String,
createdAt: { type: Date, default: Date.now },
})
const FriendRequestSchema = new Schema({
userRequest: { type: String, required: true },
userTarget: { type: String, required: true },
createdAt: { type: Date, default: Date.now },
})
const UserSchema = new Schema({
tag: { type: String, required: true, unique: true },
friendRequestsSent: { type: [FriendRequestSchema] },
friendRequestsReceived: { type: [FriendRequestSchema] },
friends: { type: [String] },
notifications: { type: [NotificationSchema] },
})
The request
const updateResponse = await User.updateMany(
{
friends: { $elemMatch: { $eq: userTag } },
friendRequestsSent: {
userTarget: {
$elemMatch: { $eq: userTag },
},
},
friendRequestsReceived: {
userRequest: {
$elemMatch: { $eq: userTag },
},
},
notifications: {
data: {
$elemMatch: { $eq: userTag },
},
},
},
{
$pull: {
friends: userTag,
friendRequestsSent: { userTarget: userTag },
friendRequestsReceived: { userRequest: userTag },
notifications: { data: userTag },
},
}
)
The error
Error while deleting the user account: Cast to String failed for value "{ '$elemMatch': { '$eq': '0eQzaAwpt' } }" at path "userRequest" for model "User"
The userRequest field in friendRequestsReceived is type String, not array so $elemMatch will not work. Also, you don't need to use $elemMatch because you specify only a single condition in the $elemMatch expression as it says in the docs:
If you specify only a single condition in the $elemMatch expression, you do not need to use $elemMatch.
In your case, you just need to do something like (details here):
await User.updateMany({
friends: userTag,
"friendRequestsSent.userTarget" : userTag,
"friendRequestsReceived.userRequest": userTag,
"notifications.data": userTag
}...

how to select a property depending another property - mongodb

I'm using mongoose. I want to select users property depending on another property at this here type.
for example when my type is private I want to select users.
Conversation.find({
users: {
$elemMatch: {
user: _id
}
}
},{
title: 1,
type: 1,
users:1 // when `type` is `private` I want to this field to be one.
});
my Schema:
const ConversationSchema = new Schema({
type: {type: String, enum: ['private', 'group'], default: 'private'},
creator: {type: Schema.Types.ObjectId, ref: 'User', index: true, required: true}, // creator
// for group,
title: String,
picture: String,
description: String,
users: [
{
user: { type: Schema.Types.ObjectId, index: true, reuqired: true, unique: true },
role: { type: String, enum: ['admin', 'member'], default: 'member' },
mute: { type: Boolean, default: false },
type: {type: String, enum: ['private', 'group'], default: 'private'},
}
],
}, { timestamps: true });
You can conditionally exclude fields by using REMOVE in aggregation. In your case, it should be:
Conversation.aggregate([
{$match: {"users.user": id}},
{
$project: {
title: 1,
type: 1,
users: {
$cond: {
if: { $eq: [ "$type", "private" ] },
then: "$users",
else: "$$REMOVE"
}
}
}
}
])
Side note: If you specify only a single condition in the $elemMatch expression, you do not need to use $elemMatch.
You can use aggregate like this, it will select all users if you want to users detail then will have to use populate.
db.getCollection("Conversation").aggregate([
{
$unwind: "$users"
},
{
$match: {
"type": 'private'
}
}
]);

Mongoose aggreagate with group not working

In my Node.JS API, it is possible to order and get menus. The structure of an ordered menu looks like the following (the main schema is the orderMenuSchema; menuItemSchema is for the subdocument-array with ordered items):
var menuItemSchema = mongoose.Schema({
itemId: {
type: mongoose.Schema.Types.ObjectId,
required: true
},
prepared: {
type: Boolean,
default: false
},
finished: {
type: Boolean,
default: false
},
timestamp: {
type: Date,
default: Date()
}
}, {_id: false})
var orderMenuSchema = mongoose.Schema({
orderId: {
type: mongoose.Schema.Types.ObjectId,
required: true
},
menuId: {
type: mongoose.Schema.Types.ObjectId,
required: true
},
items: {
type: [menuItemSchema],
required: true,
validate: menuItemsCheck
},
finished: {
type: Boolean,
default: false
},
timestamp: {
type: Date,
default: Date()
}
})
Example Data:
{
"_id":"5d2333a1841a0e4ef05873d0",
"finished":false,
"timestamp":"2019-07-08T12:14:04.000Z",
"menuId":"5d189ffdbe02ef0b00b22370",
"items":[
{
"prepared":false,
"finished":false,
"timestamp":"2019-07-08T12:14:04.000Z",
"itemId":"5d189ffdbe02ef0b00b2236d"
},
{
"prepared":false,
"finished":false,
"timestamp":"2019-07-08T12:14:04.000Z",
"itemId":"5d189ffdbe02ef0b00b2236e"
},
{
"prepared":false,
"finished":false,
"timestamp":"2019-07-08T12:14:04.000Z",
"itemId":"5d189ffdbe02ef0b00b2236f"
}
],
"orderId":"5d2333a1841a0e4ef05873c3",
"__v":0
}
Whether an item is prepared or not is stored in the prepared field of the menuItem.
Each menu has multiple items to choose from, and the user is able to have only some items - that's why the orderMenuSchema has an array of subdocuments called "items" in which only the ordered items are stored.
Now I would like to get all unprepared menus, group them by the menuID
and then group them by the itemID - everything with a Mongoose
aggregation.
So, I think I need two groupings: The first one by the menuId, the second one by the itemId.
Furthermore, I would like to know how many of each item are unprepared - so after grouping by the menuId, I need to get a count of all unprepared items
Expected Output:
I thought of something like this:
{
"result":[
{
"menuID":"tastefulMenu123",
"items":[
{
"itemId":"noodlesoop123",
"unpreparedCount":13
},
{
"itemId":"tastyBurger123",
"unpreparedCount":2
},
{
"itemId":"icecoldIce123",
"unpreparedCount":20
}
]
}
]
}
There will be an array of subdocuments, one subdocument for each menuId. Each subdocument than has an array of items in which the itemID as well as the unpreparedCount are stored.
What I already tried (not working):
OrderMenu.aggregate([
{$unwind: "$items"},
{ $project: { prepared: 1, itemId: 1} },
{ $match: {
prepared: false,
timestamp: {
$gte: today,
$lt: tomorrow
}
}},
{ $group: {
_id: {menuId: '$menuId', itemId: '$itemId'},
count: { $sum: 1 }
}}
]).then(result => {
console.log(result)
return Promise.resolve(result)
}).catch(error => {
console.log(error)
return Promise.reject(500)
})
Any help would be appreciated!

Displaying friends list using node.js and MongoDB

I am new to a node.js and I am trying to use this application https://github.com/knoldus/Node.js_UserLogin_Template
However, I cannot see friends list. I do not know what is the problem with ?
Could you help me with issue ?
Thanks
//Your user schema is looks like that
const userSchema = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
//unique: true,
required: true
},
password: {
type: String,
required: true
},
friends: {
type: Array // here you can put your friends collections _id like this [ObjectId("id1"), ObjectId("id2")]
}
});
const FriendSchema = new Schema({
user_from: {
type: Schema.Types.ObjectId,
ref: "users",
required: true
},
user_to: {
type: Schema.Types.ObjectId,
ref: "users",
required: true
},
is_accepted: {
type: Boolean,
default: false
},
date: {
type: Date,
default: Date.now
}
});
//and your query will look like that --
FriendModel.aggregate([
{
$lookup: {
from: "users", // users collection
localField: "_id", // friends collection id
foreignField: "friends", // friends field in you users collection document
as: "myfriends" //any alias name you can use
}
},
{
$match: { // matching condition for current user's friends from friends collection including current user
$or: [
{ user_to: mongoose.Types.ObjectId(req.user.id) }, // req.user.id referrers to logged in user object id
{ user_from: mongoose.Types.ObjectId(req.user.id) },
]
},
$match: { is_accepted: true} // that condition is for is user accepted friend request or not.
}
,
{ // filtering logged in user from the friend list
$project: {
myfriends: { // myfriends is alias name that you used in $loopup part
$filter: {
input: "$myfriends",
as: "item",
cond: { $ne: [ "$$item._id", mongoose.Types.ObjectId(req.user.id) ] }
}
}
}
}
])

Resources