I have a question ...
I have a schema like this :
const chatSchema = new Schema({
[...]
title: {
type: String,
required: true
},
message: [
{
content: {
type: String,
required: true
},
creator: {
type: mongoose.Types.ObjectId,
required: true,
ref: 'User'
}
}
],
[...]
});
in my Node backend, how can I have access to my creators instances inside my message array ? I don’t know how to write the query with populate ...
Thank you all ! :-)
use the following command:
...after importing chatSchema as maybe chats
module.exports.populateCreator = (req, res) => {
chats.findOne({chatID})
.populate({
path: 'message',
populate: {
path: "creator"
}
})
.then(chats => res.json(chats))
}
You can use like this
Chats.find(query)
.populate({
path: 'message',
populate: {
path: 'creator'
}
})
.exec(function(err, docs) {});
the query you are looking for is:
https://mongoplayground.net/p/2dpeZWsXR-V
db.booking.aggregate([
{
"$match": {
id: "61fdfeef678791001880da25"
}
},
{
$unwind: "$cart"
},
{
"$lookup": {
"from": "products",
"localField": "cart.product",
"foreignField": "id",
"as": "prod"
}
},
{
"$unwind": "$prod"
},
{
"$project": {
id: 1,
status: 1,
cart: [
{
id: "$cart.id",
date: "$cart.date",
timeSlots: "$cart.timeSlots",
product: {
id: "$prod.id",
name: "$prod.name",
}
}
],
}
}
])
Related
I want to get the size of array in the model:
const UserSchema = mongoose.Schema(
{
username: { type: String, lowercase: true, required: true },
items: { type: [mongoose.Types.ObjectId], default: [] },
},
{
toJSON : {
virtuals : true
},
timestamps: true,
}
);
UserSchema.virtual("itemsCount").get(function () {
return this.items.length;
});
module.exports = {
UserModel: mongoose.model("user", UserSchema ),
};
const ProductSchema = mongoose.Schema(
{
name: { type: String, required: true },
owner: { type: mongoose.Types.ObjectId,required: true, ref:"user"
},
},
{
toJSON : {
virtuals : true
},
timestamps: true,
}
);
module.exports = {
ProductModel: mongoose.model("product", ProductSchema ),
};
But I want to hide items in the output whenever I try to use projection it gives an error:
Cannot read properties of undefined (reading 'length')
const newPosts = await ProductModel.find({}).populate([{ path: "owner", select: { itemsCount: 0 }}]);
if I don't use select it works:
const newPosts = await ProductModel.find({}).populate([{ path: "owner" }}]);
But I don't want to show items filed in output
You can use aggregation pipeline for this:
ProductModel.aggregate([
{
"$lookup": {
"from": "users",
"localField": "user",
"foreignField": "_id",
"as": "users"
}
},
{
"$unwind": "$users"
},
{
"$project": {
_id: "$_id",
name: "$name",
"users": {
itemsCount: {
$size: "$users.items"
}
}
}
}
])
Read more about $lookup, $unwind, $project to understand.
Here is the Mongodb playground to see the results: https://mongoplayground.net/p/R0ZQiV8I-YM
I have 2 schemas, this is parent collection schema:
const TimesheetSchema = Schema({
managersComment: {
type: String,
},
weekNum: {
type: Number,
},
year: {
type: Number,
},
user: { type: Schema.Types.ObjectId, ref: userModel },
status: {
type: String,
enum: ["Saved", "Submitted", "Approved", "Rejected"],
},
data: [{ type: Schema.Types.ObjectId, ref: TimesheetIndividualData }]
});
This is child collection schema
const TimesheetDataSchema = new Schema(
{
workingDate: {
type: Date,
},
dayVal: {
type: Number,
},
user: { type: Schema.Types.ObjectId, ref: userModel },
parentId: { type: String }
},
{ timestamps: true }
);
In TimesheetDataSchema parentId is basically the _id from TimesheetSchema.
Now i need to run a query which return docs from TimesheetDataSchema, but only the docs in which parentId(ObjectId) of TimesheetSchema has status Approved.
I am trying to do $lookup, but currently no success. Please help.
EDIT: Based upon #ashh suggestion tried this: but getting empty array.
const result = await TimesheetIndividualData.aggregate([
{
"$lookup": {
"from": "timesheetModel",
"let": { "parentId": "$parentId" },
"pipeline": [
{ "$match": { "status": "Approved", "$expr": { "$eq": ["$weekNum", "$parentId"] } } },
],
"as": "timesheet"
}
},
{ "$match": { "timesheet": { "$ne": [] } } }
])
You can use below aggregation
const result = await db.TimesheetDataSchema.aggregate([
{ "$lookup": {
"from": "TimesheetSchema",
"let": { "parentId": "$parentId" },
"pipeline": [
{ "$match": { "status": "approved", "$expr": { "$eq": ["$_id", "$$parentId"] }}},
],
"as": "timesheet"
}},
{ "$match": { "timesheet": { "$ne": [] }} }
])
But I would prefer two queries for better performance here
const timesheets = (await db.TimesheetSchema.find({ status: "approved" }, { _id: 1 })).map(({ _id }) => _id)
const result = await db.TimesheetDataSchema.find({ parentId: { $in: timesheets } })
I need to perform an operation that matches 2 conditions:
id1 -> matches array of Ids
id2 -> matches array of Ids.
I have Chat model with users (array of Id's).
const ChatSchema = new Schema(
{
users: {
type: [{
type: Schema.Types.ObjectId,
ref: 'User'
}]
},
messages: {
type: [{
type: Schema.Types.ObjectId,
ref: 'Message'
}]
},
},
{
toObject: { virtuals: true },
toJSON: { virtuals: true },
timestamps: true
}
);
Aggregation Query:
return await _chat.aggregate([{
$match: { // Here I nedd to match both cases
$expr: { $in: [userId1, "$users"] },
$expr: { $in: [mongoose.Types.ObjectId(userId2), "$users"] }
}
},
{ $addFields: { totalMessages: { "$size": "$messages" } } },
{
$project: {
users: 1,
messages: { $slice: ["$messages", -20] },
totalMessages: 1,
createdAt: 1
}
},
{
$lookup: {
from: this._message.collection.name,
localField: 'messages',
foreignField: '_id',
as: 'messages',
}
}
])
I tried using $and but it is not working in the $match stage.
Does anyone know how can I perform this?
You can simply use $all,
{
$match: {
users: {
$all: [userId1, mongoose.Types.ObjectId(userId2)]
}
}
}
I have tried other similar kind of questions available but nothing seems to work for me.
I have two collections:
leads:
const mongoose = require("mongoose");
const id = mongoose.Schema.Types.ObjectId;
const leadsSchema = mongoose.Schema(
{
_id: id,
userId: { type: id, ref: "User", required: true },
leadName: String,
leads: [
{
_id: id,
name: String,
status: { type: String, required: false, default: "New" },
leadActivity: { type: String, required: false, default: "No Campaign Set" },
headline: { type: String, required: false },
location: { type: String, required: false },
leadType: { type: id, ref: "LeadsCategory", required: true },
}
],
campaignAssociated: {type: id, ref: "campaign"},
},
{
timestamps: true
}
);
module.exports = mongoose.model("lead", leadsSchema);
leadCategory
const mongoose = require("mongoose");
const leadsCategorySchema = mongoose.Schema(
{
_id: mongoose.Schema.Types.ObjectId,
name: {
type: String,
required: false,
},
leadsData: [{ type: Array, ref: "lead" }],
},
{ timestamps: true }
);
module.exports = mongoose.model("LeadsCategory", leadsCategorySchema);
I am trying to reference/populate the name of the lead from leadscategory schema into the leads
exports.get_single_lead_info = (req, res) => {
const { userId } = req.user;
const { leadid } = req.body;
let idToSearch = mongoose.Types.ObjectId(leadid);
Lead.aggregate([
{
$lookup: {from: 'leadscategories', localField: 'leadType', foreignField: 'name', as: 'type as'}
},
{
$match: {
userId: mongoose.Types.ObjectId(userId),
},
},
{
$unwind: "$leads",
},
{
$match: {
"leads._id": idToSearch,
},
},
])
.exec(function (err, result) {
if (err) {
return res.status(400).json({ message: "Unable to fetch data", err });
}
if (!result.length) {
res.status(404).json("No result found");
} else {
res.status(200).json({ message: "Lead info found", result });
}
});
};
But it outputs me the lookup result as an empty array everytime:
{
"message": "Lead info found",
"result": [
{
"_id": "5ece11cbac50c434dc4b7f2c",
"leadName": "python",
"leads": {
"status": "New",
"leadActivity": "Campaign Set",
"name": "Hailey",
"headline": "Machine Learning | Python",
"location": "New Delhi Area, India",
"_id": "5ece11cbac50c434dc4b7f29",
"leadType": "5ebce0f81947df2fd4eb1060"
},
"userId": "5eba83d37d4f5533581a7d58",
"createdAt": "2020-05-27T07:07:55.231Z",
"updatedAt": "2020-05-27T10:47:42.098Z",
"__v": 0,
"type as": [] //<--- Need lead type name associated inside this
}
]
}
Input: "leadid": "5ece11cbac50c434dc4b7f29"
Any help appreciated.
[
{
$match: {
userId: mongoose.Types.ObjectId(userId),
},
},
{
$unwind: "$leads",
},
{
$match: {
'leads._id': idToSearch,
},
},
{
$lookup: {
from: 'leadscategories',
localField: 'leads.leadType',
foreignField: '_id',
as: 'type as'
}
},
]
I want to select user list from users collection and if a user has an unread message which status is 0/false in messages schema then select unread messages count too, which can be 0/5/10/1/0. thanking you in advance! i am sure it can be done with aggregate framework and already tried some query(below) but with that i am not getting what i need to be the result.
//User Schema
import mongoose from 'mongoose';
const userSchema = new mongoose.Schema({
name: { type: String, required: true, trim: true },
email:{type: String, required: true, unique: true, minlength: 3, trim: true},
password: { type: String, required: true, minlength: 6, trim: true },
});
export default mongoose.model('user', userSchema);
//Message Schema
import mongoose from 'mongoose';
const messageSchema = new mongoose.Schema({
from: { type: mongoose.Schema.Types.ObjectId, ref: 'user' },
to: { type: mongoose.Schema.Types.ObjectId, ref: 'user' },
text: { type: String, trim: true },
unread: { type: Boolean, default: false }
});
messageSchema.set('timestamps', true);
export default mongoose.model('message', messageSchema);
I want the result should be
[ { _id: 5cc984981fa38539f4a61ce0,
name: 'Jhon Doe',
unreadTotal: 5 },
{ _id: 5cc98f651fa38539f4a61cfd,
name: 'Markas',
unreadTotal: 3 },
{ _id: 5cc994b164745026c4e25546,
name: 'Mike',
unreadTotal: 0 } ]
// i tried the following but getting unread of total not for each user
const userId = 'loggedinUserId'
const userList = await User.aggregate([
{
$match: { _id: {$ne: ObjectId(userId) } }
},
{ $lookup:
{
from: 'messages',
let: { 'to': ObjectId(userId) },
pipeline: [
{
$match:
{
'unread': false,
$expr: { $eq: [ '$$to', '$to' ] }
}
},
{ $count: 'count' }
],
as: 'messages'
}
},
{
$addFields:
{
'unreadTotal': { $sum: '$messages.count' }
}
}
]);
Not sure this will help you or not, but you will get all unread messages of each user against the logged in user as you have described and later you can do whatever you want with like count etc.
const userList = await User.aggregate([
{
$match: { _id: {$ne: ObjectId(userId) } }
},
{
$lookup:
{
from: 'messages',
localField: '_id',
foreignField: 'from',
as: 'messages'
},
},
{
$project:
{
name: 1,
email: 1,
profilePhoto: 1,
messages:
{
$filter:
{
input: "$messages",
as: "message",
cond: {
$and: [
{ $eq: [ '$$message.unread', false ] },
{ $eq: [ '$$message.to', ObjectId(userId) ] }
]
}
}
}
}
}
]);