Mongoose - for each get count and select - javascript

I have two schemas Advert:
var AdvertSchema = new Schema({
created: {
type: Date,
default: Date.now
},
title: {
type: String,
default: '',
trim: true,
required: 'title cannot be empty'
},
advert: {
type: String,
default: '',
required: 'advert cannot be empty'
},
sport: [{
type: Schema.ObjectId,
ref: 'Sport'
}],
});
mongoose.model('Advert', AdvertSchema);
and Sport:
var SportSchema = new Schema({
sportName: {
type: String,
default: '',
required: 'sport cannot be empty'
}
});
mongoose.model('Sport', SportSchema);
And I would like to know how can I for each sport get count of adverts and select _id and sportName of Sport schema. Note that in Advert schema is sport as array. The result then should be something like this:
[{
_idOfSport: "some ID",
sportName: "sport name",
countOfSportAdverts: "number"
},
etc for other sports...
]
I guess I can use aggregation framework but I don't know how. Is it somehow possible to do it?

I suppose this could do the trick:
Sport.find({}, function(err, sports) {
var results = [], sportMap = {};
sports.forEach(function(sport) {
sportMap["_idOfSport"] = sport._id;
sportMap["sportName"] = sport.sportName;
Advert.aggregate([
{
$match: {
sport: sport._id
}
},
{
$group: {
_id: null,
count: { $sum: 1 }
}
}
], function(err, res){
sportMap["countOfSportAdverts"] = res[0].count;
results.push(sportMap);
});
});
});

Related

MongoDB: how can I pull an entire object from an object array that matches the Objectid?

My question is, how I can pull a team object in teams that matches the unique _id of a team(in teams array).
Here is what I have tried, however, the problem is that all entries in teams get deleted, instead of only the object that matches the team _id.
router.put("/delete/team/:id/:org", (req, res) => {
const orgid = req.params.org;
const _id = req.params.id;
Organization.findOneAndUpdate(
{
_id: orgid,
},
{
$pull: {
teams: { _id: _id },
},
},
{ multi: true }
)
.then((organization) => {
res.status(200).json(organization);
})
.catch((err) => {
res.status(400).json(err);
});
});
Each Organization in OrganizationSchema has a unique _id.
Also, each team(object) in teams array has a unique _id.
const OrganizationSchema = new Schema({
owner: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
register_date: {
type: Date,
default: Date.now,
},
teams: [
{
sport: {
type: String,
required: false,
},
access_code: {
type: Number,
required: false,
},
admin: [{ type: mongoose.Schema.Types.ObjectId, ref: "User" }],
events: [
{
date_time: {
type: Date,
offset: true,
},
opponent: {
type: String,
required: true,
},
home_away: {
type: String,
required: true,
},
expected_attendance: {
type: Number,
},
people_attending: [
{ type: mongoose.Schema.Types.ObjectId, ref: "User" },
],
amenities: [String],
},
],
},
],
});
The probable reason for the output you are getting is, because you are matching an ObjectId with a string.
You need to convert your string to an object.
You can do this by adding,
const ObjectID = require('mongodb').ObjectID
And then,
$pull: {
teams: { _id: new ObjectID(_id) },
},

How can I push a new object and simultaneously items to an array of that same object in MongoDB?

The problem is that I only can push the object itself, however, it won't let me push new elements to the array of the object that I want to push at the same time.
Is there another way to do it, inserting another $push to append the array, inside the object $push, doesn't work. The array "amenities" of the new object stays empty.
enter code here
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const OrganizationSchema = new Schema({
logo: {
type: String,
required: true,
},
name: {
type: String,
required: true,
unique: true,
},
admin_email: {
type: String,
required: true,
unique: true,
},
owner: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
register_date: {
type: Date,
default: Date.now,
},
teams: [
{
sport: {
type: String,
required: false,
},
access_code: {
type: Number,
required: false,
},
admin: {
type: String,
required: false,
},
events: [
{
date_time: {
type: Date,
},
opponent: {
type: String,
required: true,
},
expected_attendance: {
type: Number,
},
amenities: [String],
},
],
},
],
});
module.exports = Organization = mongoose.model(
"organization",
OrganizationSchema
);
router.put("/create/event/:teamid", (req, res) => {
const teamid = req.params.teamid;
const { date_time, competitor, amenities } = req.body;
Organization.findOneAndUpdate(
{ "teams._id": teamid },
{
$push: {
"teams.$.events": {
competitor: competitor,
date_time: date_time,
$push: {
amenities: {
amenities,
},
},
},
},
}
)
.then((list) => {
res.status(200).json(list);
console.log(JSON.stringify(list));
})
.catch((err) => {
res.status(400).json(err);
});
});

I have a Schema with Sub Schema. I want to update particular data in the Sub Schema

this is my schema
const mongoose = require('mongoose')
const MaterialListSchema = new mongoose.Schema({
nomenclature:{
type: String,
required: true
},
national: {
type: String,
required: true
},
partnumber:{
type:String,
required:true
},
quantity: {
type: Number,
required: true
},
unit:{
type: String,
required: true
},
price: {
type: Number,
required: true
}
})
const MrrlSchema = new mongoose.Schema({
aircrafttype:{
type: String,
required: true
},
mrrlcategory:{
type: String,
required: true
},
materiallist: [MaterialListSchema]
})
const Mrrl = mongoose.model('Mrrl', MrrlSchema)
module.exports = Mrrl
this is my update code . but it will delete all the sub document on the selected and will only have remaining
Mrrl.updateOne({
'materiallist': {$elemMatch: { _id: req.params.id}}
},{
$set: { materiallist: req.body }
}).then((data)=>{
console.log(data)
})
If i understand your question, you need use arrayFilters
// it is your req.body
const newMaterial = {
nomenclature: '2'
};
const result = await MrrlSchema.updateMany({ 'materiallist': {$elemMatch: { _id: req.params.id }} },
{ "$set": { "materiallist.$[material]": newMaterial } },
{ "arrayFilters": [ { "material._id": req.params.id } ] });
Note: arrayFilters can be use only with 3.6+ mongo

Filter array of objects from another array of objects. Both arrays have been pulled from mongodb

I am needing to filter an array of object by another array of objects. Here is my scenario:
I have a drivers endpoint where I am taking params of tripId, departTime, and returnTime. From there I pull an array of all my drivers. I then use aggregate to pull in the conflicted drivers. These are the two arrays I need to filter by.
router.get("/:id/:departTime/:returnTime", [auth, admin], async (req, res) => {
const trip = await Trip.findById(req.params.id);
if (!trip) return res.status(404).send("Trip with given ID not found");
//console.log(trip);
const allDrivers = await User.find({ isDriver: true });
const conflictedDrivers = await Trip.aggregate([
{
$unwind: "$drivers"
},
{
$match: {
_id: { $ne: trip._id },
$or: [
{
departTime: {
$gte: new Date(req.params.departTime),
$lte: new Date(req.params.returnTime)
}
},
{
returnTime: {
$gte: new Date(req.params.departTime),
$lte: new Date(req.params.returnTime)
}
}
]
}
},
{
$project: {
_id: "$drivers._id",
name: "$drivers.name",
email: "$drivers.email"
}
}
]);
console.log("conflicted drivers: ", conflictedDrivers);
if (conflictedDrivers.length === 0) return res.send(allDrivers);
const availableDrivers = allDrivers.filter(driver => {
return !conflictedDrivers.find(cd => {
return driver._id === cd._id;
});
});
console.log("available drivers: ", availableDrivers);
res.send(availableDrivers);
});
My problem is that the comparisons between conflicted drivers Id and and all drivers Id are not being accurately returned. If I
return cd.email === driver.email
then my my returned filtered array is correct.
Here is my userSchema:
const userSchema = new Schema({
name: {
type: String,
min: 3,
max: 50,
required: true
},
email: {
type: String,
required: true,
min: 5,
max: 255,
unique: true
},
password: {
type: String,
required: true
},
isAdmin: {
type: Boolean,
default: false
},
isSupervisor: {
type: Boolean,
default: false
},
isDriver: {
type: Boolean,
default: false
},
google: {
id: String,
token: String,
email: String,
name: String
}
});
and my tripSchema:
const tripSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
destination: String,
physicalAddress: String,
departTime: Date,
returnTime: Date,
departureLocation: String,
organization: String,
distance: Number,
cost: Number,
occupants: Number,
tripOwner: {
type: new mongoose.Schema({
name: {
type: String,
minlength: 5,
maxlength: 50
},
email: {
type: String,
minlength: 5,
maxlength: 100
}
})
},
phoneNumber: String,
vehicleTypeReq: {
type: new mongoose.Schema({
name: {
type: String
}
})
},
numberOfPrimaryVehicles: Number,
supportVehicles: Number,
estimateNeeded: Boolean,
numberOfDrivers: Number,
totalVehicles: Number,
comments: String,
isDenied: Boolean,
isArranged: {
type: Boolean,
default: false
},
supervisor: {
type: new mongoose.Schema({
name: {
type: String,
minlength: 5,
maxlength: 50
},
email: {
type: String,
minlength: 5,
maxlength: 100
}
})
},
isApproved: {
type: Boolean,
default: false
},
drivers: [userSchema],
vehicles: [vehicleSchema]
});
I would just give in and use the comparison using email but I will need to do a very similar filter using vehicles next.
Am I taking the right approach here? Maybe there is a way to handle this in the mongo query?
I was able to take care of it thanks to Rohit Dalal's suggestion.
router.get("/:id/:departTime/:returnTime", [auth, admin], async (req, res) => {
const trip = await Trip.findById(req.params.id);
if (!trip) return res.status(404).send("Trip with given ID not found");
const conflictedDrivers = await Trip.aggregate([
{
$unwind: "$drivers"
},
{
$match: {
_id: { $ne: trip._id },
$or: [
{
departTime: {
$gte: new Date(req.params.departTime),
$lte: new Date(req.params.returnTime)
}
},
{
returnTime: {
$gte: new Date(req.params.departTime),
$lte: new Date(req.params.returnTime)
}
}
]
}
},
{
$project: {
_id: "$drivers._id",
name: "$drivers.name",
email: "$drivers.email"
}
}
]);
const conflictedDriversIdArray = conflictedDrivers.map(driver => {
return driver._id;
});
const availableDrivers = await User.find({
$and: [{ _id: { $nin: conflictedDriversIdArray } }, { isDriver: true }]
});
res.send(availableDrivers);
});

Mongo DB $push object wraps it in an array first

When I use $push in mongodb, the expected outcome turns out differently. It actually wraps the object I want to push in an array. This is problematic because I'd have to map over the result just to extract it. Any help would be greatly appreciated.
My Mongo Query
const pushAction = {
$push: {
cart: {
id: itemId,
quantity: quantity
}
}
}
// Add item to user's cart
User.update({_id: userId}, pushAction, (err, success) => {
if (err) {
res.status(422).json({'error': 'There was a problem adding the item to your cart.'});
}
if (success) {
// Find user and return the cart
User.findOne({_id: userId}, {cart: 1}, (err, user) => {
res.status(200).json({'message': 'The item was successfully added to your cart.', cart: user.cart});
})
}
});
User Schema
// Define User Model
const userSchema = new Schema({
firstName: {
type: Schema.Types.String,
required: true
},
lastName: {
type: Schema.Types.String,
required: true
},
password: {
type: Schema.Types.String,
required: true
},
email: {
type: Schema.Types.String,
required: true
},
cart: {
type: Schema.Types.Array
},
dateCreated: {
type: Schema.Types.Date,
default: Date.now,
required: true
},
dateUpdated: [
{
date: {
type: Schema.Types.Date
},
details: {
type: Schema.Types.ObjectId
}
}
],
verified: {
type: Schema.Types.Boolean,
required: true
},
role: {
type: Schema.Types.String,
default: ROLES_BASIC_USER
}
});
Expected Outcome
"cart" : [
{
"id" : "587b6b69799ad7ff650edbb5",
"quantity" : 1
},
{
"id" : "587b6b69799ad7ff650edbb5",
"quantity" : 1
},
{
"id" : "587b6b69799ad7ff650edbb5",
"quantity" : 1
}
],
Actual Result
"cart" : [
[
{
"id" : "587b6b69799ad7ff650edbb5",
"quantity" : 1
}
],
[
{
"id" : "587b6b69799ad7ff650edbb5",
"quantity" : 1
}
],
[
{
"id" : "587b6b69799ad7ff650edbb5",
"quantity" : 1
}
]
]
// Define User Model
const userSchema = new Schema({
firstName: {
type: Schema.Types.String,
required: true
},
lastName: {
type: Schema.Types.String,
required: true
},
password: {
type: Schema.Types.String,
required: true
},
email: {
type: Schema.Types.String,
required: true
},
cart:[ {
id: Schema.Types.ObjectId,
quantity: Number
}],
dateCreated: {
type: Schema.Types.Date,
default: Date.now,
required: true
},
dateUpdated: [
{
date: {
type: Schema.Types.Date
},
details: {
type: Schema.Types.ObjectId
}
}
],
verified: {
type: Schema.Types.Boolean,
required: true
},
role: {
type: Schema.Types.String,
default: ROLES_BASIC_USER
}
});
Try changing pushAction as follows:
const pushAction = {
$push: {
cart: { $each: [ {id: itemId, quantity: quantity } ] }
}
}
Clean existing items in cart field before trying this.
If it still fails then the issue might be with the schema.

Categories

Resources