How to find all voted user against each debate in mongoose - node.js

I have a debate collection, which holds all the debates and another collection holds the votes against each debate. So I wanted to retrieve all the debates with a new user flag (isVoted) if I found any user in the vote collection against each debate.
Vote model:
var voteSchema = new Schema({
user: { type: Schema.Types.ObjectId, required: true, ref: 'User' }, // One who votes
debate: { type: Schema.Types.ObjectId, required: true, ref: 'Debate' }
}, { timestamps: true });
Debate Model:
var debateSchema = new Schema({
category: { type: Schema.Types.ObjectId, required: true, ref: 'Category' },
question: { type: String, required: true },
Votes: { type: Number, default: 0 },
}, { timestamps: true });
Query
DebateData.aggregate([
{
$match: query
},
{
$sort : { createdAt : -1 }
},
{
$lookup: {
from: "votes", // must be the PHYSICAL name of the collection
localField: "_id",
foreignField: "debate",
as: "votes"
}
},
{
$addFields: {
'isVoted': {
$cond: { if: { $eq: [ '$votes.user', ObjectId(req.query.userId) ] }, then: 'true', else: 'false' }
}
}
},
{
$project: {
'_id': 1,
'question': 1,
'isVoted': 1,
'createdAt': 1
}
},
]).then(result => {
res.status(200).json({ success: true, status: 200, message: 'Debate videos', data: result});
}).catch(err => {
res.status(500).json({ success: false, status: 500, message: err.message })
});
Expected output:
{
"data": [
{
"_id": "60e81f8299a4809658290d80",
"votes": 10,
"category": [
{
"name": "Hockey"
}
],
"question": "What is football?",
"isVoted": true,
"createdAt": "2021-07-09T10:05:54.498Z"
},
{
"_id": "60e438f1194949add0cc2074",
"votes": 12,
"category": [
{
"name": "Cricket"
}
],
"question": "What is football?",
"isVoted": false,
"createdAt": "2021-07-06T11:05:21.654Z"
}
]
}
Current output:
{
"data": [
{
"_id": "60e81f8299a4809658290d80",
"votes": 10,
"category": [
{
"name": "Hockey"
}
],
"question": "What is football?",
"createdAt": "2021-07-09T10:05:54.498Z"
},
{
"_id": "60e438f1194949add0cc2074",
"votes": 12,
"category": [
{
"name": "Cricket"
}
],
"question": "What is football?",
"createdAt": "2021-07-06T11:05:21.654Z"
}
]
}
Vote Data:
{
data: [
{
"user": "69881f8299a480965829ytr267",
"debate": "60e81f8299a4809658290d80"
}
]
}

Related

how to find avg of referenced schema field in mongo db

I have schema designed like this
const MerchantSchema = new mongoose.Schema({
mobile: {
type: Number,
required: true,
unique: true
},
user_type:{
type:String,
enum:['merchant'],
required: true
},
isVerified:{
type:Boolean,
default:()=>false
},
token:String,
location:{
type:{
type:String,
enum:['Point'],
default:()=>'Point'
},
coordinates:{
type:[Number],
required:true
}
},
images:[String],
reviews:[{
type:mongoose.Schema.Types.ObjectId,
ref:'reviews'
}],
});
MerchantSchema
.method("toJSON", function() {
const { __v, _id, ...object } = this.toObject();
object.id = _id;
return object;
});
MerchantSchema.index({location:'2dsphere'});
module.exports = mongoose.model('merchants',MerchantSchema);
review schema
const mongoose = require('mongoose');
const ReviewSchema = new mongoose.Schema({
rating:{
type:Number,
required:true,
default:0
},
message: {
type: String,
required: true
},
owner_id:{
type:mongoose.Schema.Types.ObjectId,
ref:'merchants'
},
posted_by:{
type:mongoose.Schema.Types.ObjectId,
ref:'users',},
});
ReviewSchema
.method("toJSON", function() {
const { __v, _id, ...object } = this.toObject();
object.id = _id;
return object;
});
module.exports = mongoose.model('reviews', ReviewSchema);
i want to get result based on geoLocation and with some condition
and i want additional field of avgRating in the result set.
i am trying with following query
try {
const merchants = await Merchant.aggregate([
// match merchant lat lng in radius
{
$geoNear: {
near: {
type: "Point",
coordinates: [
parseFloat(req.query.long),
parseFloat(req.query.lat)
]
},
distanceField: "distance",
maxDistance: Number(req.query.distance),
spherical: true
},
},
{ $match: { isActive: { $eq: true } } },
// {
// $unwind: {
// path: "$reviews"
// }
// },
{
$addFields: {
reviewCount: {
$size: "$reviews"
},
avgRating: {
$avg: "$reviews.rating",
}
}
},
// uncoment this to get all data except the reviews
{
$project: {
reviews: 0,
}
},
// uncomment this to get all the reviews withth slected fields
// if selected fields are not required then comment let section
// and pipeline section and uncomment local fiels and foreign fields
// {
// $lookup: {
// from: "reviews",
// let: { owner_id: "$_id" },
// pipeline: [
// {$match: { $expr: { $eq: [ "$owner_id", "$$owner_id" ] } } },
// { $project: { message: 1, posted_by: 1, _id: 0,rating:1 } }
// ],
// as: "reviews",
// }
// },
]);
return (merchants.length>0)?
apiResponse.successResponseWithData(res,"Success",merchants) :
apiResponse.ErrorResponse(res,{message:"No merchants found"});
} catch (error) {
return apiResponse.ErrorResponse(res, error.message || "Some error occurred while getting merchants.");
}
output i am getting is
{
"status": 1,
"message": "Success",
"data": [
{
"_id": "6223400356a5f3404b57a273",
"mobile": 9999999999,
"user_type": "merchant",
"location": {
"type": "Point",
"coordinates": [
25.75679373274295,
86.02907103277855
]
},
"images": [],
"isVerified": true,
"__v": 0,
"reviewCount": 2,
"facilityCount": 2,
"avgRating": null
}
]
}
expecting output
{
"status": 1,
"message": "Success",
"data": [
{
"_id": "6223400356a5f3404b57a273",
"mobile": 9999999999,
"user_type": "merchant",
"location": {
"type": "Point",
"coordinates": [
25.75679373274295,
86.02907103277855
]
},
"images": [],
"isVerified": true,
"__v": 0,
"reviewCount": 2,
"facilityCount": 2,
"avgRating": 3.0
}
]
}
what are the possibilities of getting the expecting outputs.
any suggestion would be highly appreciated
Your code works fine with me. I tried it on my own database. Below is the example.
db.Merchant.aggregate([
{
$geoNear: {
near: {
type: "Point",
coordinates: [
25.75679373274295,
86.02907103277855
]
},
distanceField: "distance",
maxDistance: 10,
spherical: true
}
},
{
$match: {
isActive: {
$eq: true
}
}
},
{
$lookup: {
from: "reviews",
let: {
owner_id: "$_id"
},
pipeline: [
{
$match: {
$expr: {
$eq: [
"$owner_id",
"$$owner_id"
]
}
}
},
{
$project: {
message: 1,
posted_by: 1,
_id: 0,
rating: 1
}
}
],
as: "reviews"
}
},
{
$addFields: {
reviewCount: {
$size: "$reviews"
},
avgRating: {
$avg: "$reviews.rating"
}
}
},
{
$project: {
reviews: 0
}
}
])
mongoplayground

Sequelize limit order issue

Currently am having a problem with a simple query.
Sequelize query:
db[TABLE_NAMES.BOOKING].findAll({
order: [
[ db[TABLE_NAMES.USER_BOOKING_RELATION], db[TABLE_NAMES.BOOKING_STATUS], sequelize.literal('status = \'Attention\''), 'desc'],
['created_at', 'desc']
],
offset,
limit: max,
attributes: ['id', 'created_at'],
where: { school_id: schoolId },
include: [
{
attributes: ['id'],
model: db[TABLE_NAMES.USER_BOOKING_RELATION],
include: [
{
required: true,
attributes: ['status'],
model: db[TABLE_NAMES.BOOKING_STATUS],
where: { status: { [Sequelize.Op.in]: ['Attention', 'Pending', 'Invited'] } }
},
]
}
]
});
Models
const booking = sequelize.define(TABLE_NAMES.BOOKING, {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
unique: true
},
created_at: {
type: DataTypes.DATE,
defaultValue: sequelize.literal('NOW()')
},
updated_at: {
type: DataTypes.DATE,
defaultValue: sequelize.literal('NOW()')
},
deleted_at: {
type: DataTypes.DATE
}
});
const user_booking_relation = sequelize.define(TABLE_NAMES.USER_BOOKING_RELATION, {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
unique: true
},
user_id: {
type: DataTypes.UUID,
allowNull: false
},
booking_id: {
type: DataTypes.UUID,
allowNull: false
},
booking_status_id: {
type: DataTypes.UUID,
allowNull: false,
},
created_at: {
type: DataTypes.DATE,
defaultValue: sequelize.literal('NOW()')
},
updated_at: {
type: DataTypes.DATE,
defaultValue: sequelize.literal('NOW()')
},
deleted_at: {
type: DataTypes.DATE
},
});
const booking_status = sequelize.define(TABLE_NAMES.BOOKING_STATUS, {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
unique: true
},
status: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
created_at: {
type: DataTypes.DATE,
defaultValue: sequelize.literal('NOW()')
},
updated_at: {
type: DataTypes.DATE,
defaultValue: sequelize.literal('NOW()')
},
deleted_at: {
type: DataTypes.DATE
}
});
booking.hasMany(user_booking_relation, { foreignKey: 'booking_id', sourceKey: 'id' });
user_booking_relation.belongsTo(booking, { foreignKey: 'booking_id', targetKey: 'id' });
booking_status.hasMany(user_booking_relation, { foreignKey: 'booking_status_id', sourceKey: 'id' });
user_booking_relation.belongsTo(booking_status, { foreignKey: 'booking_status_id', targetKey: 'id' });
Generated sql
SELECT `booking`.*,
`user_booking_relations`.`id` AS `user_booking_relations.id`,
`user_booking_relations->booking_status`.`id` AS `user_booking_relations.booking_status.id`,
`user_booking_relations->booking_status`.`status` AS `user_booking_relations.booking_status.status`
FROM (SELECT `booking`.`id`, `booking`.`created_at`
FROM `booking` AS `booking`
WHERE (`booking`.`deleted_at` IS NULL AND `booking`.`school_id` = 'a97b42e5-c864-4a4a-870b-737dd9700124')
AND (SELECT `user_booking_relations`.`booking_id`
FROM `user_booking_relation` AS `user_booking_relations`
INNER JOIN `booking_status` AS `booking_status`
ON `user_booking_relations`.`booking_status_id` = `booking_status`.`id` AND
(`booking_status`.`deleted_at` IS NULL AND
`booking_status`.`status` IN ('Invited', 'Pending', 'Attention'))
WHERE ((`user_booking_relations`.`deleted_at` IS NULL) AND
`user_booking_relations`.`booking_id` = `booking`.`id`)
LIMIT 1) IS NOT NULL
ORDER BY `booking`.`created_at` DESC
LIMIT 0, 10) AS `booking`
LEFT OUTER JOIN ( `user_booking_relation` AS `user_booking_relations` INNER JOIN `booking_status` AS `user_booking_relations->booking_status` ON
`user_booking_relations`.`booking_status_id` = `user_booking_relations->booking_status`.`id` AND
(`user_booking_relations->booking_status`.`deleted_at` IS NULL AND
`user_booking_relations->booking_status`.`status` IN ('Invited', 'Pending', 'Attention')) )
ON `booking`.`id` = `user_booking_relations`.`booking_id` AND
(`user_booking_relations`.`deleted_at` IS NULL)
ORDER BY `user_booking_relations->booking_status`.status = 'Attention' DESC, `booking`.`created_at` DESC;
Result
// first page
// first page
[
{
"id": "4c74c307-3f7c-40c6-ba26-6d0e9f510bcc",
"created_at": "2020-05-30T20:15:07.000Z",
"user_booking_relations": [
{
"id": "7cb183c7-77cf-4fc8-9c98-eb2b8abf8d39",
"booking_status": {
"status": "Attention"
}
}
]
},
{
"id": "1e8c9250-61b5-4610-b913-bd7aee866d5d",
"created_at": "2020-06-01T14:48:00.000Z",
"user_booking_relations": [
{
"id": "0a9ba1a0-0929-4979-ba15-12c4903fd8a5",
"booking_status": {
"status": "Invited"
}
},
]
},
{
"id": "a1624f59-ebaa-4bc7-95b8-d0e96c1ec917",
"created_at": "2020-06-01T08:45:12.000Z",
"user_booking_relations": [
{
"id": "fdbc677b-2035-44d2-8d9a-ab304e5624ee",
"booking_status": {
"status": "Pending"
}
}
]
}
]
// second page
[
{
"id": "d18abf5c-c986-4c2c-a08d-02e1488745d8",
"created_at": "2020-05-30T20:14:10.000Z",
"user_booking_relations": [
{
"id": "585c0674-13cf-45ac-91bc-087b345a7b31",
"booking_status": {
"status": "Attention"
}
}
]
},
{
"id": "692ccef9-12b4-4aed-955b-11ce65512469",
"created_at": "2020-05-30T20:12:45.000Z",
"user_booking_relations": [
{
"id": "5c3d214d-833a-482d-aeb5-272af750f3bb",
"booking_status": {
"status": "Attention"
}
}
]
},
{
"id": "1a5a56a9-4a6e-4548-a4d8-b388e6a9ac02",
"created_at": "2020-05-30T20:09:43.000Z",
"user_booking_relations": [
{
"id": "cea240f4-2529-44ae-a82d-f53d2dbbd0fc",
"booking_status": {
"status": "Attention"
}
}
]
}
]
Expected result
// first page
[
{
"id": "4c74c307-3f7c-40c6-ba26-6d0e9f510bcc",
"created_at": "2020-05-30T20:15:07.000Z",
"user_booking_relations": [
{
"id": "7cb183c7-77cf-4fc8-9c98-eb2b8abf8d39",
"booking_status": {
"status": "Attention"
}
}
]
},
{
"id": "d18abf5c-c986-4c2c-a08d-02e1488745d8",
"created_at": "2020-05-30T20:14:10.000Z",
"user_booking_relations": [
{
"id": "585c0674-13cf-45ac-91bc-087b345a7b31",
"booking_status": {
"status": "Attention"
}
}
]
},
{
"id": "692ccef9-12b4-4aed-955b-11ce65512469",
"created_at": "2020-05-30T20:12:45.000Z",
"user_booking_relations": [
{
"id": "5c3d214d-833a-482d-aeb5-272af750f3bb",
"booking_status": {
"status": "Attention"
}
}
]
}
]
// second page
[
{
"id": "1a5a56a9-4a6e-4548-a4d8-b388e6a9ac02",
"created_at": "2020-05-30T20:09:43.000Z",
"user_booking_relations": [
{
"id": "cea240f4-2529-44ae-a82d-f53d2dbbd0fc",
"booking_status": {
"status": "Attention"
}
}
]
},
{
"id": "1e8c9250-61b5-4610-b913-bd7aee866d5d",
"created_at": "2020-06-01T14:48:00.000Z",
"user_booking_relations": [
{
"id": "0a9ba1a0-0929-4979-ba15-12c4903fd8a5",
"booking_status": {
"status": "Invited"
}
},
]
},
{
"id": "a1624f59-ebaa-4bc7-95b8-d0e96c1ec917",
"created_at": "2020-06-01T08:45:12.000Z",
"user_booking_relations": [
{
"id": "fdbc677b-2035-44d2-8d9a-ab304e5624ee",
"booking_status": {
"status": "Pending"
}
}
]
}
]
As we can see from the generated SQL, bookings with any of those statuses are initially selected and then sorted. I need Attention bookings to go first. How can I get around this or fix it? Any thoughts will be much appreciated.
Thanks,
Alex.

I need to aggregate two collections based on userIds but couldn't manage it

I want to aggregate the collections (Review and Account) below but couldn't manage it properly so I needed to ask you guys.
Current Review Collection is written below
{
lawyerId: { type: mongoose.Schema.Types.ObjectId },
reviews: [
{
userId: { type: mongoose.Schema.Types.ObjectId, unique: true },
message: { type: String },
rate: { type: Number },
createdAt: { type: Date, default: Date.now },
},
],
}
If you recommend Review Collection can be refactored like this
{
lawyerId: { type: mongoose.Schema.Types.ObjectId },
userId: { type: mongoose.Schema.Types.ObjectId },
message: { type: String },
rate: { type: Number },
createdAt: { type: Date, default: Date.now },
}
Account Collection
{
_id: { type: mongoose.Schema.Types.ObjectId}
email: { type: String, unique: true },
firstName: { type: String },
lastName: { type: String },
},
The expected result of fetching reviews
{
averageRate: 3.2,
reviews: [
{
firstName: 'Jack',
lastName: 'Harden',
message: 'I dont like it',
rate: 2,
createdAt: '2020-01-01T14:58:23.330+00:00'
},
{
firstName: 'Takeshi',
lastName: 'San',
message: 'Thats nice',
rate: 5,
createdAt: '2020-03-02T10:45:10.120+00:00'
}
],
}
You should be able to achieve this using an aggregation.
You can view a live demo here, which allows you to run this query.
The Query:
// Assuming we are searching for an lawyerId of 3
db.review.aggregate([
{
$match: {
lawyerId: 3
}
},
{
$lookup: {
from: "account",
localField: "userId",
foreignField: "_id",
as: "user"
}
},
{
$unwind: "$user"
},
{
$group: {
_id: "$lawyerId",
averageRate: {
$avg: "$rate"
},
reviews: {
$push: {
createdAt: "$createdAt",
firstName: "$user.firstName",
lastName: "$user.lastName",
message: "$message",
rate: "$rate"
}
}
}
},
{ // *******************************************
$project: { // *******************************************
_id: 0, // If you comment out/remove all of these lines
averageRate: 1, // then the return also contains the 'lawyerId',
reviews: 1 // as '_id', which I would find useful...
} // *******************************************
} // *******************************************
])
The Results:
The query from above, using the data set from above, produces the following results:
[
{
"averageRate": 3.25,
"reviews": [
{
"createdAt": ISODate("2015-02-28T00:00:00Z"),
"firstName": "First",
"lastName": "Uno",
"message": "Message meh",
"rate": 3
},
{
"createdAt": ISODate("2015-02-28T00:00:00Z"),
"firstName": "Second",
"lastName": "Dos",
"message": "Message blah",
"rate": 4
},
{
"createdAt": ISODate("2015-02-28T00:00:00Z"),
"firstName": "First",
"lastName": "Uno",
"message": "Message foo",
"rate": 4
},
{
"createdAt": ISODate("2015-02-28T00:00:00Z"),
"firstName": "Third",
"lastName": "Tres",
"message": "Message bar",
"rate": 2
}
]
}
]
The Dataset:
In Mongo Playground, you can build out databases with multiple collections, this explains the data structure:
db={ // <---- Database 'db'
"account": [ // <---- Collection 'account'
{
_id: 21,
email: "first.uno#gmail.com",
firstName: "First",
lastName: "Uno"
},
{
_id: 22,
email: "second.dos#yahoo.com",
firstName: "Second",
lastName: "Dos"
},
{
_id: 23,
email: "third.tres#hotmail.com",
firstName: "Third",
lastName: "Tres"
}
],
"review": [ // <---- Collection 'review'
{
lawyerId: 3,
userId: 21,
message: "Message meh",
rate: 3,
createdAt: ISODate("2015-02-28T00:00:00Z")
},
{
lawyerId: 3,
userId: 22,
message: "Message blah",
rate: 4,
createdAt: ISODate("2015-02-28T00:00:00Z")
},
{
lawyerId: 3,
userId: 21,
message: "Message foo",
rate: 4,
createdAt: ISODate("2015-02-28T00:00:00Z")
},
{
lawyerId: 3,
userId: 23,
message: "Message bar",
rate: 2,
createdAt: ISODate("2015-02-28T00:00:00Z")
}
]
}
You can try this pipeline to get all reviews from review collection:
db.reviews.aggregate([
{
$lookup: {
from: "accounts",
localField: "userId",
foreignField: "_id",
as: "user"
}
},
{
$unwind: "$user"
},
{
$addFields: {
"firstName": "$user.firstName",
"lastName": "$user.lastName"
}
},
{
$group: {
"_id": null,
"average_rate": {
$avg: "$rate"
},
"reviews": {
$push: "$$ROOT"
}
}
},
{
$unset: [
"_id",
"reviews._id",
"reviews.user",
"reviews.userId",
"reviews.lawyerId"
]
}
])
Results:
[
{
"average_rate": 3.5,
"reviews": [
{
"createdAt": "Some Review Date",
"firstName": "Jack",
"lastName": "Harden",
"message": "I dont like it",
"rate": 2
},
{
"createdAt": "Some Review Date",
"firstName": "Takeshi",
"lastName": "San",
"message": "That's nice",
"rate": 5
}
]
}
]
Demo here

aggregate nested array of objects using mongoose

I have the following model and I want to query a specific user on _id field and populate the inbox.messages array with the necessary data that matches the corresponding _id field in the users model and more importantly i also want to group each message by the 'from' field and return that result
const UserSchema = new Schema({
username: {
type: String,
required: true,
},
blockedUsers: {
users: [
{
userId: {type: Schema.Types.ObjectId, ref: 'User', required: true },
}
]
},
favorites: {
users: [
{
userId: {type: Schema.Types.ObjectId, ref: 'User', required: true },
}
]
},
profileViews: {
views: [
{
userId: {type: Schema.Types.ObjectId, ref: 'User', required: true },
date: {type: Date}
}
]
},
inbox: {
messages: [
{
messageId: {type: Schema.Types.ObjectId},
from: {type: Schema.Types.ObjectId, ref: 'User', required: true },
content: {type: String, required: true},
date: {type: Date}
}
]
},
images: {
"imagePaths": [
{
imageId: {type: Schema.Types.ObjectId},
path: { type: String, required: true},
date: {type: Date}
}
],
}
})
what I have so far
let incomingId = '5e29fd75fdfd5320d0e42bc4';
let myUser = await User.aggregate([
{ $match: {"_id": mongoose.Types.ObjectId(incomingId) }},
{ $lookup: { }}
])
Not sure exactly what to put in the $lookup field or if this is even correct.
As a sample I would like the documents to look like:
[
{
"from": "5e240f7480a24e07d832c7bd",
"username":"hable0",
"images": {
imagePaths: [
'images/2020-09-24-Z_34234342_12.jpg'
],
},
"inbox": {
"messages": [
{
"messageId": "5e2a110a21c64d63f451e39e",
"content": "Message content",
"date": "2020-01-23T21:32:58.126Z"
},
{
"messageId": "5e2a111321c64d63f451e3a0",
"content": "Message content",
"date": "2020-01-23T21:33:07.378Z"
},
{
"messageId": "5e2a112321c64d63f451e3a2",
"content": "Message content",
"date": "2020-01-23T21:33:23.036Z"
}
]
}
}
]
You could try the following pipeline with aggregate().
Find the document that matches the id
Unwind inbox.messages
Group by from field
Perform a $lookup to get another document
Perform a $unwind to destruct the array
Specify fields to be included in the output
let myUser = await User.aggregate([
{
$match: { "_id": mongoose.Types.ObjectId(incomingId) }
},
{
$unwind: "$inbox.messages"
},
{
$group: {
_id: { from: "$inbox.messages.from" },
messages: {
$push: {
messageId: "$inbox.messages.messageId"
// Add more info of the message here as needed
}
}
},
},
{
$lookup: {
from: "User",
localField: "_id.from",
foreignField: "_id",
as: "extraUserInfo"
}
},
{
$unwind: "$extraUserInfo"
},
{
$project: {
_id: 0,
from: "$_id.from",
inbox: { messages: "$messages" },
username: "$extraUserInfo.username",
images: "$extraUserInfo.images"
}
}
]);
Sample output:
{
"from": "user1",
"inbox": {
"messages": [{
"messageId": "message1-from-user1"
}]
},
"username": "user1-username",
"images": {
"imagePaths": ["image-path-user1"]
}
} {
"from": "user2",
"inbox": {
"messages": [{
"messageId": "message1-from-user2"
}, {
"messageId": "message2-from-user2"
}, {
"messageId": "message3-from-user2"
}]
},
"username": "user2-username",
"images": {
"imagePaths": ["image-path-user2"]
}
} {
"from": "user3",
"inbox": {
"messages": [{
"messageId": "message1-from-user3"
}, {
"messageId": "message2-from-user3"
}]
},
"username": "user3-username",
"images": {
"imagePaths": ["image-path-user3"]
}
}
Hope this answers part of your question. Though I'm not very clear how you would like to populate the messages array with the user info who sent the messages. But you can perform a $lookup() with a pipeline after $group() operation to attach additional info from the sender to the result.
Read more about $unwind, $group, $project and $lookup.

fetch 3 schema value using mongoose node JS

I have 3 collection schema CategorySchema, SubCategorySchema, ProductSchema like below.
var CategorySchema = new mongoose.Schema({
catgory_name: {
type: String,
required: [true, "Catgory name is required"]
},
modified_date: {
type: Date
}
});
module.exports = mongoose.model("Category", CategorySchema);
var SubCategorySchema = new Schema({
subcatgory_name: {
type: String,
required: [true, "subcategory name is required"]
},
category_id: {
type: Schema.Types.ObjectId,
ref: "Category",
required: [true, "category id is required"]
},
modified_date: {
type: Date
},
is_active: {
type: Boolean,
default: 1
}
});
module.exports = mongoose.model("SubCategories", SubCategorySchema);
const ProductSchema = new Schema({
product_name: {
type: String,
required: [true, "Product name is required"]
},
product_image: {
type: String,
required: [true, "Product image is required"]
},
category_id: {
type: Schema.Types.ObjectId,
ref: "Category",
required: [true, "category is required"]
},
subcategory_id: {
type: Schema.Types.ObjectId,
ref: "Subcategory",
required: [true, "Subcategory is required"]
},
modified_date: {
type: Date
},
is_active: {
type: Boolean,
default: 1
}
});
module.exports = mongoose.model("Products", ProductSchema);
Here i want to take all the active products (is_active = 1) with the corresponding categories and active subcategories (is_active = 1). No need to check is_active condition for categories but need to check active condition for subcategories and products
I tried with the below code in node JS controller
router.get("/list", (req, res, next) => {
products
.find({ is_active: true })
.populate("category_id")
.populate("subcategory_id", null, SubCategory, {
match: { is_active: true }
})
//.where("subcategory_id", !null)
.then(products => res.json({ status: 200, data: products }))
.catch(err => res.json(err));
});
But even subcategories are inactive it returns the product data
You can query using mongodb aggregation framework still using mongoose.
router.get("/list", (req, res, next) => {
products
.aggregate([
{
$match: {
is_active: true
}
},
{
$lookup: {
from: "subcategories",
localField: "subcategory_id",
foreignField: "_id",
as: "subcategories"
}
},
{
$unwind: "$subcategories"
},
{
$match: {
"subcategories.is_active": true
}
},
{
$lookup: {
from: "categories",
localField: "category_id",
foreignField: "_id",
as: "category"
}
},
{
$addFields: {
category: {
$arrayElemAt: ["$category", 0]
}
}
}
])
.then(products => res.json({ status: 200, data: products }))
.catch(err => res.status(500).json(err));
});
Playground
Let's have these sample documents:
db={
"products": [
{
"modified_date": "2020-01-08T09:06:51.544Z",
"is_active": true,
"_id": "5e159ca1bd95457404b22bc3",
"product_name": "Product1 Name",
"product_image": "Product1 Image",
"category_id": "5e159b77a746036404b5f0ae",
"subcategory_id": "5e159befbd95457404b22bc2"
},
{
"modified_date": "2020-01-08T09:06:51.544Z",
"is_active": false,
"_id": "5e159cb8bd95457404b22bc4",
"product_name": "Product2 Name",
"product_image": "Product2 Image",
"category_id": "5e159b77a746036404b5f0ae",
"subcategory_id": "5e159befbd95457404b22bc2"
},
{
"modified_date": "2020-01-08T09:06:51.544Z",
"is_active": true,
"_id": "5e159d3abd95457404b22bc6",
"product_name": "Product3 Name",
"product_image": "Product3 Image",
"category_id": "5e159b77a746036404b5f0ae",
"subcategory_id": "5e159ce0bd95457404b22bc5"
}
],
"categories": [
{
"modified_date": "2020-01-08T09:04:18.003Z",
"_id": "5e159b77a746036404b5f0ae",
"catgory_name": "Main Category 1"
}
],
"subcategories": [
{
"modified_date": "2020-01-08T09:06:51.544Z",
"is_active": true,
"_id": "5e159befbd95457404b22bc2",
"subcatgory_name": "Sub Category 1",
"category_id": "5e159b77a746036404b5f0ae"
},
{
"modified_date": "2020-01-08T09:06:51.544Z",
"is_active": false,
"_id": "5e159ce0bd95457404b22bc5",
"subcatgory_name": "Sub Category 2",
"category_id": "5e159b77a746036404b5f0ae"
}
]
}
The result will be:
[
{
"_id": "5e159ca1bd95457404b22bc3",
"category": {
"_id": "5e159b77a746036404b5f0ae",
"catgory_name": "Main Category 1",
"modified_date": "2020-01-08T09:04:18.003Z"
},
"category_id": "5e159b77a746036404b5f0ae",
"is_active": true,
"modified_date": "2020-01-08T09:06:51.544Z",
"product_image": "Product1 Image",
"product_name": "Product1 Name",
"subcategories": {
"_id": "5e159befbd95457404b22bc2",
"category_id": "5e159b77a746036404b5f0ae",
"is_active": true,
"modified_date": "2020-01-08T09:06:51.544Z",
"subcatgory_name": "Sub Category 1"
},
"subcategory_id": "5e159befbd95457404b22bc2"
}
]
As you see, even the Product 3 is active, it hasn't been retrieved because its subcategory 5e159ce0bd95457404b22bc5 is not active.

Resources