Query embedded document in mongoDB - mongodb

I'm trying to query a Group document that has Users sub documents.
But I only want the subdocuments that belong to the user,
but I'm also getting data of other users.
Look at the query result, UserStatus field, only one of them belongs to the user.
The query result -
[
{
"_id": "5b1bcc12d5cdbf2cf78f2133",
"name": "First group ever",
"users": [
{
userId: 1,
"userStatus": "I"
},
{
userId: 2,
"userStatus": "I"
}
]
}
]
Group.js -
const GroupSchema = mongoose.Schema({
name: {
type: String,
required: true
},
users: [{
userId: {
type: mongoose.SchemaTypes.ObjectId,
ref: 'users',
required: true
},
userStatus: {
type: String,
required: true
}
}]
})
Query -
Group.find({
"users": {
$elemMatch: {
userId: req.params.id,
$or: [{
userStatus: "I"
}, {
userStatus: "A"
}]
}
}
}, "_id name users.userStatus",
function (err, groups) {
if (err)
res.send(err)
res.json(groups);
});

Related

Grouping documents in mongoose

I am building a chat app using nodejs and mongoose. I need to retrieve chats for a user(logged in user) such that the retrieved data/chats will contain the latest message and the name of the user the logged in user had a chat with. I am not able to come app with a way to aggregate the data since the sender can also be a recipient in a chat. My message schema is shown below.
const messageSchema = new mongoose.Schema(
{
content: { type: String, required: true },
fromUser: { type: mongoose.Schema.Types.ObjectId, ref: "User" },
toUser: { type: mongoose.Schema.Types.ObjectId, ref: "User" },
users: Array,
messageRead: { type: Boolean, default: false },
},
{
timestamps: true,
}
);
The purpose of this is to have a list of chats render on the front end which when clicked will open an inbox containing messages between the two users.
You can just use the $or operator. This way it will return all elements where the user is the sender or recipient:
.aggregate([
{
$match: {
$or: [
{
fromUser: ObjectId("<UserID>")
},
{
toUser: ObjectId("<UserID>")
}
]
}
},
{ $sort: { createdDate: -1 } },
{ $limit: 1 },
])
Edit after feedback via chat
I grouped the documents by using the users array and basically just output all fields as in the initial document:
.aggregate([
{
$match: {
users: ObjectId("<UserID>")
}
},
{
$group: {
_id: "$users",
out: {
$top: {
output: {
_id: "$_id",
content: "$content",
fromUser: "$fromUser",
toUser: "$toUser",
users: "$users",
messageRead: "$messageRead",
createdAt: "$createdAt"
},
sortBy: {
createdAt: -1
}
}
}
}
},
{
"$replaceRoot": {
"newRoot": "$out"
}
}
])

How to query to get all documents referenced to another collection based on one field as username

In my Node API and MongoDB, I'm trying to make an endpoint to get all the posts associated with one username of a profile. In my Profile schema, I have a reference to the Post Schema and in my Post schema, I have a reference to the Username from Profile schema.
My issue I don't know how to get all the posts for that username. I did similarly but with embedded for the Experience schema but I'm not sure how to do that for the referenced collections.
Post model:
const { Connect } = require("../db");
const reactionSchema = {
likedBy: {
type: String,
unique: true,
sparse: true
}
};
const postSchema = {
text: {
type: String,
required: true,
unique: true,
sparse: false
},
username: {
type: Connect.Schema.Types.String,
ref: "Profile"
},
image: {
type: String,
default: "https://via.placeholder.com/150",
required: false
},
createdAt: {
type: Date,
default: Date.now,
required: false
},
updatedAt: {
type: Date,
default: Date.now,
required: false
},
reactions: [reactionSchema],
comments: {
type: Connect.Schema.Types.ObjectId,
ref: "Comment",
required: false
}
};
const collectionName = "post";
const postSchemaModel = Connect.Schema(postSchema);
const Post = Connect.model(collectionName, postSchemaModel);
module.exports = Post;
Profile model:
// Here defining profile model
// Embedded we have the Experience as []
const { Connect } = require("../db");
const { isEmail } = require("validator");
const postSchema = {
type: Connect.Schema.Types.ObjectId,
ref: "Post"
};
const experienceSchema = {
role: {
type: String,
required: true
},
company: {
type: String,
required: true
},
startDate: {
type: Date,
required: true
},
endDate: {
type: Date,
required: false
},
description: {
type: String,
required: false
},
area: {
type: String,
required: true
},
createdAt: {
type: Date,
default: Date.now,
required: false
},
updatedAt: {
type: Date,
default: Date.now,
required: false
},
username: {
type: String,
required: false
},
image: {
type: String,
required: false,
default: "https://via.placeholder.com/150"
}
};
const profileSchema = {
firstname: {
type: String,
required: true
},
surname: {
type: String,
required: true
},
email: {
type: String,
trim: true,
lowercase: true,
unique: true,
required: [true, "Email is required"],
validate: {
validator: string => isEmail(string),
message: "Provided email is invalid"
}
},
bio: {
type: String,
required: true
},
title: {
type: String,
required: true
},
area: {
type: String,
required: true
},
imageUrl: {
type: String,
required: false,
default: "https://via.placeholder.com/150"
},
username: {
type: String,
required: true,
unique: true
},
experience: [experienceSchema],
posts: [postSchema],
createdAt: {
type: Date,
default: Date.now,
required: false
},
updatedAt: {
type: Date,
default: Date.now,
required: false
}
};
const collectionName = "profile";
const profileSchemaModel = Connect.Schema(profileSchema);
const Profile = Connect.model(collectionName, profileSchemaModel);
module.exports = Profile;
I was able to make this for the embedded experience but not sure that correct for referenced collection:
const profileWithExperiences = await Student.aggregate([
{ $match: { username: res.username.username } },
{
$unwind: "$experience"
},
{
$match: {
"experience._id": new ObjectID(req.params.experienceId)
}
},
{
$project: {
username: 1,
experience: 1,
_id: 0
}
}
]);
I would like to see an example for referenced collections as it is confusing me how should I do that
[EDIT]
JSON for Profiles and Posts
{
"_id": ObjectId("5e2c98fc3d785252ce5b5693"),
"imageUrl": "https://i.pravatar.cc/300",
"firstname": "Jakos",
"surname": "Lemi",
"email": "lemi#email.com",
"bio": "My bio bio",
"title": "Senior IT developer",
"area": "Copenhagen",
"username": "Jakos",
"experience": [
{
"image": "https://via.placeholder.com/150",
"_id": ObjectId("5e3975f95fbeec9095ff3d2f"),
"role": "Developer",
"company": "Google",
"startDate": ISODate("2018-11-09T23:00:00.000Z"),
"endDate": ISODate("2019-01-05T23:00:00.000Z"),
"area": "Copenhagen",
"createdAt": ISODate("2020-02-04T13:47:37.167Z"),
"updatedAt": ISODate("2020-02-04T13:47:37.167Z")
},
{
"image": "https://via.placeholder.com/150",
"_id": ObjectId("5e3978bf5e399698e20c56d4"),
"role": "Developer",
"company": "IBM",
"startDate": ISODate("2018-11-09T23:00:00.000Z"),
"endDate": ISODate("2019-01-05T23:00:00.000Z"),
"area": "Copenhagen",
"createdAt": ISODate("2020-02-04T13:59:27.412Z"),
"updatedAt": ISODate("2020-02-04T13:59:27.412Z")
}
],
"createdAt": ISODate("2020-01-25T19:37:32.727Z"),
"updatedAt": ISODate("2020-02-04T23:14:37.122Z"),
"__v": NumberInt("0")
}
Post
{
"_id": ObjectId("5e3beb639e072afedd19dcef"),
"username": ObjectId("5e2c98fc3d785252ce5b5693"),
"image": "https://via.placeholder.com/150",
"text": "My awesome post",
"createdAt": ISODate("2020-02-06T10:33:07.22Z"),
"updatedAt": ISODate("2020-02-06T10:33:07.22Z"),
"reactions": [ ],
"__v": NumberInt("0")
}
Output expected:
{
"username": "Jakos",
"postsCount": [1],
"posts": [
{
"_id": ObjectId("5e3beb639e072afedd19dcef"),
"image": "https://via.placeholder.com/150",
"text": "My awesome post",
"createdAt": ISODate("2020-02-06T10:33:07.22Z"),
"updatedAt": ISODate("2020-02-06T10:33:07.22Z"),
"reactions": [ ],
"__v": NumberInt("0")
}
]
}
I want to see all the posts related to that username
You can use the $lookup aggregation to join collections.
db.profiles.aggregate([
{
$match: {
username: "Jakos"
}
},
{
$lookup: {
from: "posts", //the name of the posts collection, change this if it is different
localField: "_id",
foreignField: "username",
as: "posts"
}
},
{
$project: {
username: 1,
posts: 1,
postsCount: {
$size: "$posts"
},
_id: 0
}
}
])
Playground
For mongoose it should be like this in your app:
const profileWithExperiences = await Student.aggregate([
{ $match: { username: res.username.username } },
{
$unwind: "$experience"
},
{
$lookup: {
from: "posts", //the name of the posts collection, change this if it is different
localField: "_id",
foreignField: "username",
as: "posts"
}
},
{
$project: {
username: 1,
posts: 1,
postsCount: {
$size: "$posts"
},
_id: 0
}
}
]);
Try to get like this i am getting data from two table with the use of node and mongodb.
var param = {};
param['user_id']=id;
var search={ user_id:param.user_id,status: [ 'Pending', 'Accepted' ] };
var output={};
output = await JobRequest.find(search);
if(output !=0)
{
var JSon=await JobRequest.aggregate([
{$match: { user_id: {$gte:id}} },
{
"$project": {
"employee_id": {
"$toObjectId": "$employee_id"
},
"service_id": {
"$toObjectId": "$service_id"
},
"createdDate": {
"$toString": "$createdDate"
},
"chat_status": {
"$toString": "$chat_status"
},
"status": {
"$toString": "$status"
},
"delivery_address": {
"$toString": "$delivery_address"
},
"delivery_lat": {
"$toString": "$delivery_lat"
},
"delivery_lang": {
"$toString": "$delivery_lang"
},
}
},
{
$lookup:
{
from: "employees",
localField: "employee_id",
foreignField: "_id",
as: "employee_details"
}
},
{
$lookup:
{
from: "services",
localField: "service_id",
foreignField: "_id",
as: "service_details"
}
}
]);

Populate nested ObjectId

My profile document is saved in my DB as the following:
{
"_id": {
"$oid": "5c3aa1de18ad682fb1444138"
},
"user": {
"$oid": "5c318f56e3224b15e0fdbd06"
},
"company": {
"info": {
"$oid": "5c318f56e3224b15e0fdbd07"
},
"position": "Stemuli"
},
"hometown": "Dallas",
"date": {
"$date": "2019-01-13T02:26:38.185Z"
},
"__v": 0
}
I am trying to populate the field company.info within the newly saved Profile.
new Profile(profileFields).save().then(profile => {
Profile.populate(profile, { path: "company.info" }, (err, p) => {
console.log(err);
console.log(p);
});
});
I get the following returned for the company field
company: {id: "5c318f56e3224b15e0fdbd07", position: "Stemuli"}
And the document that this id is linked to is the following:
{
"_id": {
"$oid": "5c318f56e3224b15e0fdbd07"
},
"employees": [
{
"$oid": "5c318f56e3224b15e0fdbd06"
}
],
"name": "Company",
"__v": 14
}
How can I get it to populate the actual company document instead of just the id?
Profile Schema
// Create Schema
const ProfileSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: "users"
},
company: {
id: { type: Schema.Types.ObjectId, ref: "industrypartners" },
position: { type: String }
},
work_experience: Array,
hometown: { type: String },
date: {
type: Date,
default: Date.now
}
});
module.exports = Profile = mongoose.model("profile", ProfileSchema);
Industry Partner Schema
// Create Schema
const IndustryPartnerSchema = new Schema({
name: {
type: String,
required: true
},
schools: [{ type: Schema.Types.ObjectId, ref: "schools" }]
});
module.exports = IndustryPartner = mongoose.model(
"industrypartner",
IndustryPartnerSchema
);
Try with this:
new Profile(profileFields).save().then(profile => {
Profile.populate(Profile, {
path: "company.info",
select: {
_id: 1,
employees: 1,
name: 1,
}
}, (err, p) => {
console.log(err);
console.log(p);
});
});

mongoose find if array of references of a document contains an IdObject

im using nodejs express and mongodb
this is part of my schema:
var UserSchema = mongoose.Schema({
friends: [{
type: mongoose.Schema.Types.ObjectId,
ref: "User",
unique: true
}],
sentRequests: [{
type: mongoose.Schema.Types.ObjectId,
ref: "User",
unique: true
}],
receivedRequests: [{
type: mongoose.Schema.Types.ObjectId,
ref: "User",
unique: true
}]
}
var User = mongoose.model('User', UserSchema);
i want to query if a user with a specefic id is in any array ** (friends, sentRequests or receivedRequests) **of another user :
i used this approach but I have to do this for 3 arrays and it's slow:
UserSchema.statics.relStatus = function(User1ID, User2ID) {
var User = this;
User.find({
_id: User1ID,
friends: {
_id: User2ID
}
}).then((err, res) => {
if (res) {
return ' they are friends';
}
});
You can try with following aggregation query (if you are using mongo 3.4 or higher):
User.aggregate([
{ $match: { _id: User1ID } },
{
$facet: {
friends: [{ $match: { friends: User2ID } }],
sentRequests: [{ $match: { sentRequests: User2ID } }],
receivedRequests: [{ $match: { receivedRequests: User2ID } }]
}
},
{
$project: {
relationship: {
$switch: {
branches: [
{ case: { $anyElementTrue: [ "$friends" ] }, then: "friends" },
{ case: { $anyElementTrue: [ "$sentRequests" ] }, then: "sentRequests" },
{ case: { $anyElementTrue: [ "$receivedRequests" ] }, then: "receivedRequests" },
],
default: false
}
}
}
}
])
Result will be object in following format {relationship: "<RELATIONSHIP_TYPE>"}
or {relationship: false} if they have no relationship.
First stage of the aggregation pipeline will match single document by _id (which is indexed by default), so you shouldnt have any performance issues.
In order to query for multiple field you can user '$or'.
User.find({
_id: User1ID,
{
$or: [
{friends: $in:[User2ID]},
{sentRequests: $in:[User2ID]},
{receivedRequests: $in:[User2ID]}
]
}
}).then((err, res) => {
if (res) {
return ' they are friends';
}
});
$or will return a user which have User2ID in either 'friends' or 'sentRequests' or 'receivedRequests'.
Read more about '$or' and '$in'
Hope this helps.

Check follow status in aggregate mongo / mongoose

I have this schema for users where followers/followed is array and the reference the same schema
var userSchema = new Schema({
username: { type: String, unique: true, trim: true, required: true },
password: { type: String, required: true },
followers: [{ type: Schema.Types.ObjectId, ref: "users" }],
followed: [{ type: Schema.Types.ObjectId, ref: "users" }],
registered: { type: Date, default: Date.now },
admin: { type: Number, default: 0 }
});
What I am looking for to return the follow status, if the _id is contains in followed array give me for example follow_status: 1
[
{
$match: { username: new RegExp(username, "i") }
},
{
$unwind: "$followers"
},
{
$lookup: {
from: "users",
localField: "followers",
foreignField: "_id",
as: "info"
}
},
{
$unwind: "$info"
},
{
$project: {
info: {
_id: 1,
username: 1,
avatar: { $ifNull: ["$avatar", ""] },
fullname: { $ifNull: ["$fullname", ""] }
}
}
},
{
$replaceRoot: { newRoot: "$info" }
},
{
$limit: 1000
}
]
Current pipeliens result
[
{
"_id": "5a906653f52e66c9c7a23cb6",
"username": "User1"
},
{
"_id": "5a908eb564a726cf8ec7e0a3",
"username": "User2"
}
]