Mongoose - Can't push array in subdocument - javascript

I'm trying to update an array by pushing it through findOneAndUpdate but I can't get it to update:
This is the code with which I try to push but does not make any movement:
let pettyCashItem = await PettyCashItems.findOneAndUpdate({
_id: id,
"items._id": idItem },
{
$set: {
"items.$.concept": req.body.concept,
"items.$.incomeAmount": req.body.incomeAmount,
"items.$.description": req.body.description,
"items.$.expenseAmount": req.body.expenseAmount
$push: {
'items.$.lastModificationBy': {
"uid": req.uid,
"username": req.user.username,
"comments": req.body.comments
}
}
}
}, { new: 'true'});
This is my model:
const PettyCashItemsSchema = Schema (
{
items:[{
concept: {
type: String,
maxlength:50,
required: [true, 'El Concepto es obligatorio']
},
incomeAmount:{
type: Number,
maxlength:50,
default:0,
required: [true, 'El Ingreso es obligatorio']
},
expenseAmount:{
type: Number,
maxlength:50,
default:0,
required: [true, 'El Egreso es obligatorio']
},
description: {
type: String,
maxlength:50,
required: [true, 'La Observación es obligatoria']
},
status: {
type: Boolean,
default: true,
required: [true, 'El Estatus es obligatorio']
},
createdAt: {
type: Date,
default: Date.now
},
lastModificationBy: [{
uid:{
type: String,
required:true
},
username:{
type: String,
required:true
},
date:{
type: Date,
default: Date.now
},
comments: {
type: String,
maxlength:300,
required: [true, 'El Comentario es obligatorio']
}
}]
}]
The update using $set for the other objects is correct but when trying to do a $push in the array this never works.
Thanks.

Try placing $push at same level with $set instead of inner level of $set.
let pettyCashItem = await PettyCashItems.findOneAndUpdate({
_id: id,
"items._id": idItem },
{
$set: {
"items.$.concept": req.body.concept,
"items.$.incomeAmount": req.body.incomeAmount,
"items.$.description": req.body.description,
"items.$.expenseAmount": req.body.expenseAmount
},
$push: {
'items.$.lastModificationBy': {
"uid": req.uid,
"username": req.user.username,
"comments": req.body.comments
}
}
}, { new: 'true'});
Sample Mongo Playground (Query)

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);
});
});

Mongoose Population issue

I'm trying to populate the records schema with users, So i can account for a user for each record. However, i am only getting a blank array back.
Here are my schemas:
1.The Records schema:
const mongoose = require('mongoose')
const RecordsSchema = new mongoose.Schema({
Title: {
type: String,
required: true
},
users: [{
type: [mongoose.Schema.Types.ObjectId],
ref: 'users'
}],
Author: {
type: String,
required: true
},
ISBN: {
type: String,
required: true
},
Review: {
type: String
},
SelectedFile: {
type: String
},
Likes: {
type: Number,
default: 0
},
Date: {
type: Date,
default: Date.now()
}
});
module.exports = Records = mongoose.model('records', RecordsSchema ');
Here is the The user Schema:
const mongoose = require('mongoose')
const userSchema = new mongoose.Schema({
username: {
type: String
},
email: {
type: String,
required: true,
unique: true
},
records: [{
type: [mongoose.Schema.Types.ObjectId],
ref: 'records'
}],
password: {
type: String,
required: true
},
Date: {
type: Date,
default: Date.now,
immutable: true
}
});
module.exports = User = mongoose.model('users', userSchema);
The express route:
router.get('/all', async (req, res) => {
try {
const records = await Records.find()
.sort({ Date: -1})
.populate('users')
.exec()
res.json(records);
} catch (err) {
console.error(err.message);
res.status(404).send('Server Error');
}
});
Result:
{
"users": [],
"Likes": 0,
"_id": "5fed8c12a4fb2c1e98ef09f6",
"Title": "New Age",
"Author": "Situma Prisco",
"ISBN": "23422",
"SelectedFile": "",
"Review": "",
"Date": "2020-12-31T08:30:10.321Z",
"__v": 0
}
I'm getting a blank Array After populating. What am I doing wrong?

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);
});

mongodb unknown argument to $lookup: foreignKey

I'm trying to "join" two models (schedule and user) in Schedule aggregate using $lookup, but my response is "unknown argument to $lookup: foreignKey". I'm using Node v8.11.3 and MongoDB 4.0 I'm using I've been searched for days and don't know how to solved this.
routes/report.js
Schedule.aggregate([{
$match: {
'store': req.body.store,
'scheduleStart': {
$lte: start,
$gte: req.body.period
},
'status': {
$lte: 3,
$gte: 1
}
}
},
{
$group: {
"_id": {
"name": "$customer.name",
"cpf": "$customer.cpf",
"id": "$customer.id",
"phone": "$customer.phone"
},
"totalValue": {
$sum: "$value"
},
"totalServices": {
$sum: 1
},
}
},
{
$lookup: {
from: 'user',
localField: 'customer.id',
foreignKey: '_id',
as: 'user_detail'
}
}
])
models/schedule.js
const ScheduleSchema = new Schema({
store: {
type: String,
required: true
},
customer: {
id: {
type: String,
required: true
},
name: {
type: String,
required: true
},
avatar: String,
phone: {
type: String,
required: true
},
cpf: {
type: String,
required: true
},
}, {
timestamps: {
createdAt: 'created',
updatedAt: 'updated'
}
});
models/user.js
const UserSchema = new Schema({
name: {
type: String,
required: true
},
storeKey: {
type: String,
required: true
},
avatar: String,
birthday: String,
phone: {
type: String,
required: true
},
cpf: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
passwordHash: String,
salt: String
},
}, {
timestamps: true
});
The foreignKey field in the $lookup aggregation stage should be foreignField per https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/#equality-match.

Categories

Resources