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) },
},
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 tried to use aggregate to find out each product's monthly sales in my order , but I ran into a problem.
Here's my data structures.
Order.model.ts
const OrderSchema: Schema = new Schema(
{
userId: {
type: String,
require: true,
},
products: [
{
product: {
_id: {
type: String,
},
title: {
type: String,
},
desc: {
type: String,
},
img: {
type: String,
},
categories: {
type: Array,
},
price: {
type: Number,
},
createdAt: {
type: String,
},
updatedAt: {
type: String,
},
size: {
type: String,
},
color: {
type: String,
},
},
quantity: {
type: Number,
default: 1,
},
},
],
quantity: {
type: Number,
},
total: {
type: Number,
},
address: {
type: String,
require: true,
},
status: {
type: String,
default: 'pending',
},
},
{ timestamps: true },
);
Order-service.ts
public async getIncome(productId?: string) {
const date = new Date();
const lastMonth = new Date(date.setMonth(date.getMonth() - 1));
const previousMonth = new Date(new Date().setMonth(lastMonth.getMonth() - 1));
//const lastYear = new Date(date.setFullYear(date.getFullYear() - 1));
const income = await this.order.aggregate([
{
$match: {
createdAt: { $gte: lastMonth },
...(productId && {
products: { $elemMatch: { product: { _id: productId } } },
}),
},
},
{
$project: {
month: { $month: '$createdAt' },
sales: '$total',
},
},
{
$group: {
_id: '$month',
total: { $sum: '$sales' },
},
},
]);
return income;
}
When I calculate whole sales without productId , it went well , I tried to use elemMatch to find productId , but it won't work , did I miss something ?
Try $unwind "products" array first, then apply $match:
const income = await this.order.aggregate([
{
$unwind: '$products'
},
{
$match: {
createdAt: { $gte: lastMonth },
product: { _id: productId },
},
},
{
$project: {
month: { $month: '$createdAt' },
sales: '$total',
},
},
{
$group: {
_id: '$month',
total: { $sum: '$sales' },
},
},
]);
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
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.