mongodb unknown argument to $lookup: foreignKey - javascript

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.

Related

Mongoose - Can't push array in subdocument

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)

Querying mongo array of embedded documents in aggregation pipeline $eq diff value

I have an array of mogoDB embedded documents
const ArticleSchema = new mongoose.Schema({
title: { type: String, required: true },
author: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true },
group: { type: mongoose.Schema.Types.ObjectId, ref: "ArticleGroup" },
});
// ArticleGroup
const ArticleGroupSchema = new mongoose.Schema({
name: { type: String, required: true },
desc: { type: String, default: "" },
state: { type: String, required: true, default: "public" },
members: [
{
desc: String,
role: String,
user: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true },
},
],
});
// User
const userSchema = new mongoose.Schema(
{
email: { type: String, unique: true },
password: String,
},
);
test data :
{
_id: "6131aa5b367318e2df14b988",
title: "功能更新清单",
author: ObjectId("607edeb4b1e1bea9acb5af38"),
group: ObjectId("612d00a43c52975d4ade10d4")
}
I want to add a new field 'can_edit' to the document by comparing 'author._id == group.members. User' in 'aggregate' pipeline.
Article.aggregate([
{
$lookup: {
from: "users",
localField: "author",
foreignField: "_id",
as: "author",
},
},
{
$lookup: {
from: "articlegroups",
localField: "group",
foreignField: "_id",
as: "group"
}
},
{
$addFields: {
can_edit: {
$eq: ["$group.members.user", "$author._id"],
},
}
}
])
However, the 'can_edit' I got was always' false ', and I'm sure that the 'author._id' value in my test data is the same as' group.members.
I want to get the data:
{
_id: "6131aa5b367318e2df14b988",
can_edit: true,
title: "功能更新清单",
author: {
_id: "607edeb4b1e1bea9acb5af38",
email: "frmachao#126.com",
},
group: {
_id: "612d00a43c52975d4ade10d4",
desc: "开发",
state: "public",
name: "开发小组",
members: [
{
_id: "612d00a43c52975d4ade10d5",
role: "admin",
user: "607edeb4b1e1bea9acb5af38",
}
]
}
}
Is there any way I can meet my needs? Does anyone know how to do that? I would grateful to you 🙏.
Just now, I try :
Article.aggregate([
{
$lookup: {
from: "users",
localField: "author",
foreignField: "_id",
as: "author",
},
},
{
$lookup: {
from: "articlegroups",
localField: "group",
foreignField: "_id",
as: "group"
}
},
{
$addFields: {
can_edit: {
$in: [
"$author._id",
"$group.members.user"
],
},
},
}
])
However, the 'can_edit' I got was always' false '
With $in (aggregation),
Returns a boolean indicating whether a specified value is in an array.
$in: [
"$author._id",
"$group.members.user"
]
Updated:
Since Post Owner added the issue:
$in requires an array as a second argument
which means $group.members potentially is null and not an array.
Add condition checking for checking $group.members must be an array.
{
case: {
$ne: [
{
"$type": "$group.members"
},
"array"
]
},
then: false
},
$ne - Not equal check
$type - Return the BSON type of field
Complete query:
db.collection.aggregate([
{
$addFields: {
can_edit: {
$switch: {
branches: [
{
case: {
$ne: [
{
"$type": "$group.members"
},
"array"
]
},
then: false
},
{
case: {
$ne: [
{
"$type": "$group.members"
},
"array"
]
},
then: false
},
{
case: {
$in: [
"$author._id",
"$group.members.user"
]
},
then: true
}
],
default: false
}
}
}
}
])
Sample 1 in Mongo playground
Alternatively, the query can be shortened with $switch not needed as $in returns the same boolean result.
$and - Logical AND of multiple expressions
Complete query:
db.collection.aggregate([
{
$addFields: {
can_edit: {
$and: [
{
$eq: [
{
"$type": "$group.members"
},
"array"
]
},
{
$in: [
"$author._id",
"$group.members.user"
]
}
]
}
}
}
])
Sample 2 in Mongo Playground

Mongodb aggregate run in terminal but when run in node.js with mongoose it's return "APIError: Arguments must be aggregate pipeline operators"

When I run the aggregate in the mongo throw terminal it returns the correct data, but when I run the same aggregate in the code it returns an error.
I had tried to use just match and group to test, but I still get the same error even if I used mongoose functions but without any luck
I plan to try to improve my aggregate game but for now, I need this to run and I'm stuck here for the last couple of days.
"APIError: Arguments must be aggregate pipeline operators\n at new ExtendableError (/vagrant/server/helpers/APIError.js:15:11)\n at new APIError (/vagrant/server/helpers/APIError.js:31:5)\n at app.use (/vagrant/config/express.js:64:22)\n at Layer.handle_error (/vagrant/node_modules/express/lib/router/layer.js:71:5)\n at trim_prefix (/vagrant/node_modules/express/lib/router/index.js:315:13)\n at /vagrant/node_modules/express/lib/router/index.js:284:7\n at Function.process_params (/vagrant/node_modules/express/lib/router/index.js:335:12)\n at next (/vagrant/node_modules/express/lib/router/index.js:275:10)\n at /vagrant/node_modules/express/lib/router/index.js:635:15\n at Immediate.next (/vagrant/node_modules/express/lib/router/index.js:260:14)\n at Immediate._onImmediate (/vagrant/node_modules/express/lib/router/index.js:635:15)\n at runCallback (timers.js:706:11)\n at tryOnImmediate (timers.js:676:5)\n at processImmediate (timers.js:658:5)"
customer model
const mongoose = require('mongoose');
const customerContactsSchema = require('./customerContacts.model');
const appSettings = require('../../config/appSettings');
/**
* Customer Schema
*/
const CustomerSchema = new mongoose.Schema({
avatar: {
type: String,
required: false
},
firstName: {
type: String,
required: false
},
lastName: {
type: String,
required: false
},
password: {
type: String,
required: false,
select: false
},
address: {
country: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Country',
required: false
},
city: {
type: mongoose.Schema.Types.ObjectId,
ref: 'City',
required: false
},
},
language: {
type: String,
required: true,
enum: appSettings.languages,
default: 'english'
},
mobileNumber: {
number: {
type: String,
required: true,
trim: true
},
country: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Country',
required: true
}
},
email: {
type: String,
lowercase: true,
trim: true,
required: false
},
birthday: {
type: Date,
required: false
},
gender: {
type: String,
enum: ['male', 'female'],
required: false
},
friends: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Customer',
required: false
}],
contacts: [customerContactsSchema],
favourites: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Company',
required: false
}],
transactions: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'CustomerTransaction',
required: false
}],
googleId: {
type: String
},
facebookId: {
type: String
},
twitterId: {
type: String
},
fcmToken: {
type: String
},
otpToken: {
type: Number,
required: false,
default: 1234
},
otpTokenExpiration: {
type: Date,
required: false
},
status: {
type: String,
enum: ['active', 'pending', 'suspended', 'blocked', 'deleted'],
required: true,
default: 'pending'
}
}, { timestamps: true });
/**
* #typedef Customer
*/
module.exports = mongoose.model('Customer', CustomerSchema);
CustomerTransaction model
const mongoose = require('mongoose');
const locationSchema = require('./location.model');
/**
* CustomerTransaction Schema
*/
const CustomerTransactionSchema = new mongoose.Schema({
company: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Company',
required: true
},
customer: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Customer',
required: true
},
type: {
type: String,
// debit - , credit +
enum: ['debit', 'credit'],
required: true
},
expirationDate: {
type: Date,
required: false
},
point: {
amount: {
type: Number,
required: false
},
value: {
type: Number,
required: false
}
},
closeValue: {
type: Number,
required: false
},
closePoints: {
type: Number,
required: false
},
offer: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Offer',
required: false
},
voucher: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Voucher',
required: false
},
stamp: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Stamp',
required: false
},
punches: {
type: Number,
required: false
},
date: {
type: Date,
required: false,
default: Date.now()
},
gift: {
type: Boolean,
default: false
},
giftSender: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Customer',
required: false
},
giftReceiver: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Customer',
required: false
},
status: {
type: String,
enum: ['active', 'used', 'sent', 'expired', 'deleted'],
required: true,
default: 'active'
},
location: locationSchema
}, { timestamps: true });
/**
* #typedef CustomerTransaction
*/
module.exports = mongoose.model('CustomerTransaction', CustomerTransactionSchema);
aggregate
db.getCollection('customers').aggregate([
{
$match: {
gender: new RegExp(`.*${'' || ''}.*`, 'i'),
birthday: {
$gte: ISODate('1991-04-29T00:00:00.000Z'),
$lte: ISODate('2019-04-29T00:00:00.000Z'),
},
createdAt: {
$gte: ISODate('1991-04-29T00:00:00.000Z'),
$lte: ISODate('2019-04-29T00:00:00.000Z'),
},
}
},
{
$lookup: {
from: 'customertransactions',
localField: '_id',
foreignField: 'customer',
as: 'CustomerTransaction'
}
},
{
$match: {
'CustomerTransaction.company': ObjectId('5cd2af3a5bfc5b1a40b7de49'),
}
},
{
$project: {
firstName: 1,
lastName: 1,
CustomerTransaction: 1,
CustomerStampTransaction: {
$setDifference: [
{
$map: {
input: '$CustomerTransaction',
as: 'el',
in: {
$cond: [
{
$and: [
{ $gte: ['$$el.stamp', 1] },
{ $lt: ['$$el.offer', 0] },
{ $lt: ['$$el.voucher', 0] },
{ $eq: ['$$el.company', ObjectId('5cd2af3a5bfc5b1a40b7de49')] }
]
},
{
_id: '$$el._id'
},
false
]
}
}
},
[false]
]
},
CustomerPointTransaction: {
$setDifference: [
{
$map: {
input: '$CustomerTransaction',
as: 'el',
in: {
$cond: [
{
$and: [
{ $lt: ['$$el.stamp', 0] },
{ $lt: ['$$el.offer', 0] },
{ $lt: ['$$el.voucher', 0] },
{ $eq: ['$$el.company', ObjectId('5cd2af3a5bfc5b1a40b7de49')] }
]
},
{
_id: '$$el._id',
createdAt: '$$el.createdAt',
closePoints: '$$el.closePoints',
},
false
]
}
}
},
[false]
]
}
}
},
{
$project: {
firstName: 1,
lastName: 1,
CustomerPointTransaction: 1,
stampCount: {
$cond: {
if: { $isArray: '$CustomerStampTransaction' },
then: { $size: '$CustomerStampTransaction' },
else: 0
}
},
//"CustomerTransaction": "$CustomerTransaction"
}
},
{
$unwind: {
path: '$CustomerPointTransaction',
preserveNullAndEmptyArrays: true
}
},
{ $sort: { 'CustomerPointTransaction.createdAt': 1 } },
{
$group: {
_id: '$_id',
firstName: { $first: '$firstName' },
lastName: { $first: '$lastName' },
stampCount: { $first: '$stampCount' },
CustomerPointTransaction: { $last: '$CustomerPointTransaction' }
}
},
{
$group: {
_id: '$_id',
firstName: { $first: '$firstName' },
lastName: { $first: '$lastName' },
stampCount: { $first: '$stampCount' },
pointCount: { $first: '$CustomerPointTransaction.closePoints' }
}
},
]);
aggregate result
{ "_id" : ObjectId("5c8918ab9979f345214158ba"), "firstName" : "mohammed", "lastName" : "alqahtani", "stampCount" : 1, "pointCount" : null }
{ "_id" : ObjectId("5cc161039f11d13109185b15"), "firstName" : "Mohammedaadsxxxa", "lastName" : "AlQahtaniss", "stampCount" : 0, "pointCount" : 9 }
code
function search(req, res) {
Customer.aggregate([
{
$match: {
gender: new RegExp(`.*${'' || ''}.*`, 'i'),
}
},
{
$lookup: {
from: 'customertransactions',
localField: '_id',
foreignField: 'customer',
as: 'CustomerTransaction'
}
},
{
$match: {
'CustomerTransaction.company': req.company._id,
}
},
{
$project: {
firstName: 1,
lastName: 1,
CustomerTransaction: 1,
CustomerStampTransaction: {
$setDifference: [
{
$map: {
input: '$CustomerTransaction',
as: 'el',
in: {
$cond: [
{ $and: [
{ $gte: ['$$el.stamp', 1] },
{ $lt: ['$$el.offer', 0] },
{ $lt: ['$$el.voucher', 0] },
{ $eq: ['$$el.company', req.company._id] }
] },
{
_id: '$$el._id'
},
false
]
}
}
},
[false]
]
},
CustomerPointTransaction: {
$setDifference: [
{
$map: {
input: '$CustomerTransaction',
as: 'el',
in: {
$cond: [
{ $and: [
{ $lt: ['$$el.stamp', 0] },
{ $lt: ['$$el.offer', 0] },
{ $lt: ['$$el.voucher', 0] },
{ $eq: ['$$el.company', req.company._id] }
] },
{
_id: '$$el._id',
createdAt: '$$el.createdAt',
closePoints: '$$el.closePoints',
},
false
]
}
}
},
[false]
]
}
}
},
{
$project: {
firstName: 1,
lastName: 1,
CustomerPointTransaction: 1,
stampCount: { $cond: { if: { $isArray: '$CustomerStampTransaction' }, then: { $size: '$CustomerStampTransaction' }, else: 0 } },
//"CustomerTransaction": "$CustomerTransaction"
}
},
{
$unwind: {
path: '$CustomerPointTransaction',
preserveNullAndEmptyArrays: true
}
},
{ $sort: { 'CustomerPointTransaction.createdAt': 1 } },
{
$group: {
_id: '$_id',
firstName: { $first: '$firstName' },
lastName: { $first: '$lastName' },
stampCount: { $first: '$stampCount' },
CustomerPointTransaction: { $last: '$CustomerPointTransaction' }
}
},
{
$group: {
_id: '$_id',
firstName: { $first: '$firstName' },
lastName: { $first: '$lastName' },
stampCount: { $first: '$stampCount' },
pointCount: { $first: '$CustomerPointTransaction.closePoints' }
}
},
], { cursor: {} })
.then((customers) => {
console.log('kkkkkkkkk');
if (customers) {
res.json(Response.success(customers));
} else {
res.json(Response.success('klklk'));
}
})
.catch(err => res.status(httpStatus.INTERNAL_SERVER_ERROR)
.json(Response.failure(err)));
}
keep in mind even if I run the code like the following it's still throwing the same error
function search(req, res) {
Customer.aggregate([
{
$match: {
gender: new RegExp(`.*${'' || ''}.*`, 'i'),
}
}
], { cursor: {} })
.then((customers) => {
console.log('kkkkkkkkk');
if (customers) {
res.json(Response.success(customers));
} else {
res.json(Response.success('klklk'));
}
})
.catch(err => res.status(httpStatus.INTERNAL_SERVER_ERROR)
.json(Response.failure(err)));
}
Customer.aggregate([
{
$match: {
gender: new RegExp(`.*${'' || ''}.*`, 'i'),
}
}
], { cursor: {} }) // ERROR IS HERE
With mongoose and .aggregate, there is no option parameter like how it is on .find. Instead try this
Customer.aggregate([
{
$match: {
gender: new RegExp(`.*${'' || ''}.*`, 'i'),
}
}
]).cursor()
UPDATED
Customer.aggregate([
{
$match: {
gender: new RegExp(`.*${'' || ''}.*`, 'i'),
}
}
]).exec((error, docs) => {
console.log(docs)
res.json(docs)
})
will this worked with me
async function search(req, res) {
try {
const customers = await Customer.aggregate([
{
$match: {
gender: 'male'
}
}
]
)
.cursor({})
.exec();
customers.get(function(err, ress){
if (ress) {
res.json(Response.success(ress));
} else {
res.json(Response.success('klklk'));
}
});
} catch(e) {
res.status(httpStatus.INTERNAL_SERVER_ERROR) .json(Response.failure(e));
}
}
but I could not figure out why it's working and what was wrong with my previous code

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