Populate + Aggregate in Mongoose [duplicate] - node.js

This question already has answers here:
Mongoose - How to group by and populate?
(3 answers)
Closed 6 years ago.
I have two Mongoose models: one for transactions and the other one for the tags associated with them. In order to implement some reports, I need aggregate code like this:
Transaction.aggregate([
{ $unwind: '$tags' },
{
$group: {
_id: '$tags',
amount: {
$sum: '$amount'
}
}
}
])
Question
This produces output containing _id and amount. Now, I'd like to populate the other fields (e.g. name) from the model, keeping the calculated amount column. Can I do that within a simple populate?
Edit
The schemas for the models I'm describing:
var TransactionSchema = new Schema({
description: {
type: String,
trim: true
},
amount: {
type: Number,
required: 'Forneça um valor',
},
date: {
type: Date,
required: 'Forneça uma data',
default: Date.now
},
fromOfx: {
type: Boolean,
default: false
},
created: {
type: Date,
default: Date.now
},
correlated: {
type: Boolean,
default: false
},
tags: [{
type: Schema.Types.ObjectId,
ref: 'TransactionTag'
}],
correlates: [{
type: Schema.Types.ObjectId,
ref: 'Transaction'
}],
user: {
type: Schema.Types.ObjectId,
ref: 'User'
}
});
var TransactionTagSchema = new Schema({
name: {
type: String,
required: 'Forneça um nome',
trim: true
},
description: {
type: String,
trim: true
},
amount: {
type: Number
}
});

You can populate an aggregation after you fetched the data from the MongoDB. This will look something like this:
// Your aggregate query from your question
Transaction.aggregate([{
$unwind: '$tags'
}, {
$group: {
_id: '$tags',
amount: {
$sum: '$amount'
}
}
}])
.exec(function(err, transactions) {
// Don't forget your error handling
// The callback with your transactions
// Assuming you are having a Tag model
Tag.populate(transactions, {path: '_id'}, function(err, populatedTransactions) {
// Your populated translactions are inside populatedTransactions
});
});

Related

update collection based upon other collection field

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 },
status: { type: String }
},
{ timestamps: true }
);
I want to update all status field in TimesheetDataSchema(child collection) based upon the parentId, here parentId is basically the _id of TimesheetSchema (parent collection).
Not sure how to do that through query in mongoose/mongo, so i am trying to do that through code in express.
Please help.
How about db.timeSheetData.updateMany({"parent_id": ObjectId(_id)}) ?
(Edit)
Fetch data from TimeSheetSchema collection.
Iterate through it and for each data get the _id.
db.timeSheetsData.updateMany({"parent_id": _id}, {"$set": {"status": data.status}

Mongoose, how to sort the Date of a selected object inside a collection

I have this mongoose schema where I want to sort menus items according to their createdAt field
user:
...
menus: [
{
name: { type: String },
recipes: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Recipe',
},
],
image: {
url: { type: String },
type: {
type: String,
enum: avatarTypes,
}
},
createdAt: {
type: Date,
required: true,
default: Date.now
}
},
],
and in nodeJs when I fetch them I want to sort them according the createdAt in the menus (latest first)
and this is my code but it's not working
const menus = await User.findOne({
_id: id,
}).sort('-menus.createdAt')

How to select parent based on children's value using mongoose and GraphQL?

I'm trying to achieve something equivalent to a conditional JOIN query, but then with GraphQL.
I'm using Mongoose for my db model and MongoDB as database.
I'll illustrate my problem with the following graphQL schema:
type Booking {
_id: ID!
client: Client!
rooms: Room!
activities: Activity!
nrOfAdults: Int!
arrivalDate: String!
departureDate: String!
}
type Room {
_id: ID!
name: String!
contents: String
priceNight: Float!
maxAdults: Int!
reservations: [Booking]
}
The Mongoose schema:
const bookingSchema = new Schema(
{
client: {
type: Schema.Types.ObjectId,
ref: 'Client'
},
rooms: [{
type: Schema.Types.ObjectId,
ref: 'Rooms'
}],
nrOfAdults: {
type: Number,
required: true
},
arrivalDate: {
type: Date,
required: true
},
departureDate: {
type: Date,
required: true
}
},
{ timestamps: true }
);
const roomSchema = new Schema({
name: {
type: String,
required: true
},
priceNight: {
type: Number,
required: true
},
maxAdults: {
type: Number,
required: true
},
reservations: [
{
type: Schema.Types.ObjectId,
ref: 'Booking'
}
]
});
I can query rooms, for example, if I want to get the rooms for 3 or more adults I run:
Room.find({
maxAdults: { $gte: 3 }
});
This works fine.
However, I'd also like to show the available rooms, which means I need to impose a condition on the booking objects which are hold in reservation.
I thought this would be fairly easy, using something like:
Room.find({
maxAdults: { $gte: 3 },
reservations: { $elemMatch: { arrivalDate: { $gte: *some date*}}}
});
But it returns an empty array, while it should return some value, based on the data in mongodb:
To make things a little more clear, I'd like to achieve the same outcome as the following SQL query would give me:
SELECT *
FROM room
JOIN booking ON room.id = booking.roomId
WHERE
room.maxAdults >= 3
AND
(
booking.arrivalDate > CAST('2020-05-15' AS DATE)
OR
booking.departureDare < CAST(2020-05-06' AS DATE)
)
Assuming that you have saved the values similar to what you have mentioned in the mongoose schema.
Explore the how to do join in mongodb. Aim is to do the join before executing the query on the sub fields from the different collection.
Relevant Answer: How do I perform the SQL Join equivalent in MongoDB?
I suggest using aggregate pipeline for accomplishing what you want.
Suggested code :
Room.aggregate([
{
$match: {
maxAdults: { $gte: 3 }
}
},
{
$lookup: {
from: "bookings",
localField: "reservations",
foreignField: "_id",
as: "booking"
}
},
{
$unwind: '$booking'
},
{
$match: {
booking.arrivalDate: { $gte: *some date* }
}
},
])

How to limit one field only in mongoose

I am creating something like fb... I want to show 3 comments only on home page... How to limit one field only... my schema is this:
const postSchema = new Schema({
admin: { type: Types.ObjectId, ref: 'users', required: true },
text: { type: String, required: true },
comments: [{
postId: { type: Types.ObjectId, ref: 'posts', required: true },
admin: { type: Types.ObjectId, ref: 'users', required: true },
comment: { type: String, required: true },
time: { type: Date, default: Date.now }
}],
createdAt: { type: Date, default: Date.now },
modified: { type: Boolean, default: false }
});
I have all comments in an array... I want to limit them... Please help
Try using $slice (projection) in MongoDB. The $slice operator controls the number of items of an array that a query returns.
If you want to fetch first 3 comments only then your query will be as below:
db.slice.find( {}, { comments: { $slice: 3 } } )
In case if you want last 3 comments then your query will be:
db.slice.find( {}, { comments: { $slice: -3 } } )

How filter sub sub document in MongoDB

Hello I am working with the full Stack 'MEAN' and i have a data structure in MongoDB as indicated below:
var ExpenseSchema = new Schema({
date: {
type: Date,
default: Date.now,
required: 'Ingrese la fecha del comprobante'
},
supplier: {
type: Schema.ObjectId,
ref: 'Supplier',
},
created: {
type: Date,
default: Date.now
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
var SupplierSchema = new Schema({
name: {
type: String,
default: '',
required: 'Ingrese la Razon Social del Proveedor',
trim: true
},
category: {
type: Schema.ObjectId,
ref: 'Category',
},
created: {
type: Date,
default: Date.now
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
var CategorycompraSchema = new Schema({
name: {
type: String,
default: '',
required: 'Please fill Rubrocompra name',
trim: true
},
created: {
type: Date,
default: Date.now
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
Each 'Expense' has a 'Supplier' and each supplier has a 'Category'
I need to query so that I filter all 'Expenses' in a particular category. Someone could tell me how this can be done with MongoDB or mongoose?
That is an important case in mongodb, aggregations in mongodb is the right approach to solve this. You need to $unwind the supplier array and then category array and then use $group to put it back together:
My solution may differ depending upon your requirement, but this is something you have to do:
db.test.aggregate(
{ $match: {...}}, //match query
{ $unwind: '$supplier'},
{ $unwind: '$supplier.category'},
{ $match: {supplier.category.a: ...}}, //match query after unwinding of supplier and category
{ $group: {_id: '$_id', ...})
It will first unwind the supplier array and then unwind category array
But since you are also using mongoose, you can use plain JavaScript. You can fetch all expenses and then loop through them and
obtain your result
Expense.find().then(function(expenses) {
expenses.forEach(function(suppliers){
suppliers.forEach
...
})
})
Although this javascript way would increase effort in single threaded enviroment(node js), but still it comes in handy for some typical mongodb queries

Resources