I have mongoose schema structured somewhat like this:
{
"_id": {
"$oid": "59d00283893f4500127271f6"
},
"__v": 0,
"bills": [
{
"timestamp": 1513539218509,
"table_no": 2,
"user_id": {
"$oid": "59cf912782b85b0012860ecc"
},
"order_id": {
"$oid": "5a36c5979a54980014f06787"
},
"_id": {
"$oid": "5a36c6929a54980014f0678b"
},
"disc_amount": 320,
"amount": 320
},
{
"timestamp": 1513539299486,
"table_no": 2,
"user_id": {
"$oid": "59cf912782b85b0012860ecc"
},
"order_id": {
"$oid": "5a36c6c99a54980014f0678c"
},
"_id": {
"$oid": "5a36c6e39a54980014f0678f"
},
"disc_amount": 160,
"amount": 160
},
{
"timestamp": 1513540879109,
"table_no": 2,
"user_id": {
"$oid": "59cf912782b85b0012860ecc"
},
"order_id": {
"$oid": "5a36ccfb9a54980014f06790"
},
"_id": {
"$oid": "5a36cd0f9a54980014f06793"
},
"disc_amount": 320,
"amount": 320
},
{
"timestamp": 1513540986507,
"table_no": 2,
"user_id": {
"$oid": "59cf912782b85b0012860ecc"
},
"order_id": {
"$oid": "5a36cd639a54980014f06794"
},
"_id": {
"$oid": "5a36cd7a9a54980014f06797"
},
"disc_amount": 240,
"amount": 320
}
]
}
From the subschema bills i want to fetch only those bills which are a week old only.
My first Question is, is it a right way to first find Schema by id, then get its bills and in bills array do processing to fetch only last week data. I guess that won't be efficient because subschema bills can be too large, therefore, it won't be a good idea to fetch whole array. So, what would be the proper way?
And Second question, is there more appropriate way to create schema and do query.(I created subschema bills, so that all bills related to particular id remain at one place, so easy to query. Is this efficient?).
Open to all kind of suggestion. Thanks in advance.
You can create model for your collection in mongoose and use it as following:
var start = new Date(new Date().getTime() - (7 * 60 * 60 * 24 * 1000)); // get date of last week
Bill.find({
'bills.timestamp' : {"$gte": start.getTime()}
}, {
// Only include the subdocument(s) that matched the query.
'bills.$' : 1
}, function(err, data){
console.log(data)
});
Model:
var mongoose = require('mongoose');
/**
* Bill Mongo DB model
* #name billModel
*/
var billModel = function () {
var billSchema = mongoose.Schema({
bills:{ type : Array , "default" : [] }
});
return mongoose.model('Bill', billSchema);
};
module.exports = new billModel();
For single id, below code can be used:
Bill.aggregate([
{
$match: {
'_id': new mongoose.Types.ObjectId('5968dcf965854c7817606014')
}
},
{
$project: {
bills: {
$filter: {
input: "$bills",
as: "bill",
cond: { $gte: [ "$$bill.timestamp", start.getTime() ] }
}
}
}
},
], function (err, result) {
if (err) {
console.log(err);
} else {
res.json(result);
}
});
Related
im building a backend for a dummy ecommerce app for my portfolio in nodejs using express and mongoose, in this moment a have an Order model that give me this kind of response:
"order": {
"_id": "62c89ae8aca0f50e4ced43ea",
"shippingFee": 15000,
"subtotal": 625000,
"total": 640000,
"orderItems": [
{
"name": "Air Zoom Alphafly",
"image": "/uploads/calzado-de-carrera-de-carretera-air-zoom-alphafly-next-flyknit-mGK8M0.jpeg",
"color": "purple",
"price": 625000,
"amount": 1,
"product": "62be1d1a70c464d05c92485f",
"_id": "62c89ae8aca0f50e4ced43eb"
}
],
"status": "canceled",
"user": "62c72bf9d304f89a8b65fe3c",
"methodPayment": "transferencia",
"createdAt": "2022-07-08T21:00:24.611Z",
"updatedAt": "2022-07-13T00:05:27.124Z",
"__v": 0
}
What Im trying to do is build a query that search for an specific product in the orderItems array and create a new item that add the price for that product. In this moment in my backend I have this code:
export const getMonthlyIncome = async (req, res) => {
const productId = req.query.pid;
const date = new Date();
const lastMonth = new Date(date.setMonth(date.getMonth() - 1));
const previousMonth = new Date(new Date().setMonth(lastMonth.getMonth() - 1));
try {
const income = await Order.aggregate([
{
$match: {
createdAt: { $gte: previousMonth },
status: "paid",
...(productId && {
orderItems: { $elemMatch: { product: productId } },
}),
},
},
{
$project: {
month: { $month: "$createdAt" },
sales: "$total",
productSales: "orderItems.price",
},
},
{
$group: {
_id: "$month",
total: { $sum: "$sales" },
test: { $sum: "$productSales" },
},
},
]);
res.status(StatusCodes.OK).json({ income });
} catch (error) {
res.status(StatusCodes.INTERNAL_SERVER_ERROR).json(error);
}
};
When I test the endpoint in postman the result is an empty array.
What im doing wrong? I hope somebody can help me, thanks in advance.
I'm pretty new in MongoDB,I want to delete a particular object from an array of objects in a document. I tried a lot and finds a lot of questions in StackOverflow.But none of them worked (maybe it's my fault)
following is a document from a collection that is matched by the user with userId
61f15af01a3c53dfa87e38e6
The Document
{
"_id": "61f15af01a3c53dfa87e38ed",
"user": "61f15af01a3c53dfa87e38e6",
"transactions": [
{
"amount": 50,
"category": "Bills",
"type": "Expense",
"_id": "61f15af01a3c53dfa87e38ee"
},
{
"amount": 100,
"category": "Lottery",
"type": "Income",
"_id": "61f15af01a3c53dfa87e38ef"
},
{
"amount": 200,
"category": "Salary",
"type": "Income",
"_id": "61f15af01a3c53dfa87e38f0"
},
]
}
I want to delete the transaction with an Id of 61f15af01a3c53dfa87e38ef
I tried the below one (I don't know whether it is correct)
const allTransactions = await Transactions.findOne({ user: req.user._id });
const updatedTransactions = await allTransactions.update(
{ },
{ $pull: { transactions: { id: req.params.id } } },
);
Can anyone Spot the mistake and how can I achieve it ?
Thanks in advance :)
You can do it in one database call using below statement:
await Transactions.update(
{ user: "61f15af01a3c53dfa87e38e6" },
{ $pull: { "transactions": { _id: "61f15af01a3c53dfa87e38f0" } } }
)
Mongo Playground
Also make sure types match. If _id is of type ObjectId instead of string you should convert req.params.id to ObjectId first. See below:
await Transactions.update(
{ user: mongoose.Types.ObjectId(req.user._id) },
{ $pull: { "transactions": { _id: mongoose.Types.ObjectId(req.params.id) } } }
)
I've been stuck on this for a couple days and can't seem to figure it out.
Here's a couple example collections in my MongoDB database:
(just an example. sorry if they're not formatted correctly)
Products :
{
"_id": {
"$oid": "5e4633eaa7095f26d44a43c3"
},
"name": "Acrylic Frame Magnetic",
"units: "50",
"fbasku": "AFM-CL-0507-FBA",
"upc": "642709233954"
},
{
"_id": {
"$oid": "5e4633eaa7095f26d44a43c4"
},
"name": "Apron Polka Dot",
"units: "488",
"fbasku": "APD-RD-03PC-FBA",
"upc": "642709233961"
},
{
"_id": {
"$oid": "5e4633eaa7095f26d44a43c5"
},
"name": "Acrylic Sign Holder - 5x7",
"units: "632",
"fbasku": "ASH-GD-0507-FBA",
"upc": "642709233978"
}
Transactions :
{
"_id": {
"$oid": "5e44e8ed8f4cd40bd09d5ce8"
},
"type": "adjust",
"fbasku": "AFM-CL-0507-FBA",
"units": {
"$numberInt": "25"
},
"comment": "example transaction"
},
{
"_id": {
"$oid": "5e4c9ab642c5a232042f4b67"
},
"type": "send",
"fbasku": "AFM-CL-0507-FBA",
"units": {
"$numberInt": "75"
},
"comment": ""
},
{
"_id": {
"$oid": "5e4c9ab642c5a232042f4b69"
},
"type": "send",
"fbasku": "AFM-CL-0507-FBA",
"units": {
"$numberInt": "5"
},
"comment": ""
}
This is for a REST api I'm trying to build, so when I send a request to get all products, I want to:
Get an object containing all the products
Look through the transactions collection for each product in the object above, and add/subtract units from the listed unit quantity.
Give a response containing an object with the "fbasku", and a new calculated unit quantity for each product.
I got stuck when it came to all the async/await and Promise stuff, don't really understand it
You can try below code - it is a sample of how to do your requirement using native nodejs-mongodb driver and node.js :
const MongoClient = require('mongodb').MongoClient;
const dbConnection = async function () {
/** Connection URL */
const url = 'mongodb://localhost:27017/test'; // Check whether you're passing correct DB name & full formed connection string
let client;
try {
/** Use connect method to connect to the Server */
client = await MongoClient.connect(url);
const db = client.db(); // MongoDB would return client and you need to call DB on it.
let dbResp = await db.collection('Products').aggregate([
{
$lookup:
{
from: "Transactions",
let: { fbasku: "$fbasku" },
pipeline: [
{
$match:
{
$expr:
{ $eq: ["$fbasku", "$$fbasku"] }
}
},
{ $project: { units: 1, _id: 0 } }
],
as: "transaction_docs"
}
}, {
$project: {
_id: 0, fbasku: 1,
units: {
$add: [{ $toInt: '$units' }, {
$reduce: {
input: '$transaction_docs',
initialValue: 0,
in: { $add: ["$$value", "$$this.units"] }
}
}]
}
}
}
]).toArray();
client.close();
return dbResp;
} catch (err) {
(client) && client.close();
console.log(err);
throw err
}
};
/** In general dbConnection() has to be in one config file & it needs to be imported where ever db transactions are done like below */
dbConnection().then(res => console.log('Printing at calling ::', res)).catch(err => console.log('Err at Calling ::', err));
Note : This code has query which assumes units in products is a string, So we're converting it into int & adding that values to (sum of units of transactions => intOf(units of a products) + (sum of units of all matched transactions)).
Test Query : MongoDB-Playground
Hi I am working on writing a Node/Express endpoint and I am trying to write a MongoDB query using Mongoose to return a sum value of my documents that have an expiration within the current day or month.
Currently, I am just trying to return the sum for the current day.
First I want to match all documents that contains the user's id.
Then I want to match all documents within the current day.
Lastly, I want to return the sum of the $amount field for all those
documents.
I know that the syntax for this is wrong and I am having a hard time find good examples matching what I am doing.
let now = Date.now(),
oneDay = 1000 * 60 * 60 * 24,
today = new Date(now - (now % oneDay)),
tomorrow = new Date(today.valueOf() + oneDay);
router.get("/user_current_month/:id", [jsonParser, jwtAuth], (req, res) => {
return Expense.aggregate([
{
$match: { user: new mongoose.Types.ObjectId(req.params.id) }
},
{
$match: {
$expiration: {
$gte: today,
$lt: tomorrow
}
}
},
{
$group: {
_id: "$month",
total: {
$sum: "$amount"
}
}
}
])
.then(sum => {
res.status(200).json(sum);
})
.catch(err => res.status(500).json({ message: "Something went terribly wrong!" }));
});
This is what a document looks like:
{
"_id": {
"$oid": "5daea018b3d92643fc2c8e6c"
},
"user": {
"$oid": "5d9929f065ef083d2cdbf66c"
},
"expense": "Post 1",
"expenseType": "One Time",
"notes": "",
"amount": {
"$numberDecimal": "100"
},
"expiration": {
"$date": "2019-10-22T06:22:01.628Z"
},
"status": "Active",
"createdAt": {
"$date": "2019-10-22T06:22:16.644Z"
},
"__v": 0
}
You need to pass the two match expressions in a AND clause and then group to perform the addition operation. You need to do something like this:
Expense.aggregate([
{ $match: { $and: [{ user: new mongoose.Types.ObjectId(req.params.id) },
{
$expiration: {
$gte: today,
$lt: tomorrow
}
} ]}},
{ $group: {
_id: "$month",
total: {
$sum: "$amount"
}
}}
]);
I've got mongoose schema like this:
var mySchema = new mongoose.Schema({
user: String,
photos: [{
url: String,
thumbnail: String,
time: Date,
mainPhoto: Boolean
}]
});
now, I'd like to make a "setMainPhoto" function - for given id - set mainPhoto flag to true, and set it to false for other photos.
There is a document like this:
{
"_id": {
"$oid": "56269dea07a455920a21bca7"
},
"user": "someUser",
"photos": [
{
"time": {
"$date": "2015-05-25T08:37:56.000Z"
},
"thumbnail": "https://s3-eu-west-1.amazonaws.com/...jpg",
"url": "https://s3-eu-west-1.amazonaws.com/...jpg",
"_id": {
"$oid": "56269e2e07a455920a21bcab"
},
"mainPhoto": false
},
{
"time": {
"$date": "2015-05-25T09:27:17.000Z"
},
"thumbnail": "https://s3-eu-west-1.amazonaws.com/...jpg",
"url": "https://s3-eu-west-1.amazonaws.com/...jpg",
"_id": {
"$oid": "56269e3507a455920a21bcae"
},
"mainPhoto": true
},
{
"time": {
"$date": "2015-05-25T09:27:17.000Z"
},
"thumbnail": "https://s3-eu-west-1.amazonaws.com/...jpg",
"url": "https://s3-eu-west-1.amazonaws.com/...jpg",
"_id": {
"$oid": "56269e3507a455920a21bcaf"
},
"mainPhoto": false
}
]}
I'm trying to do this with mongoose save function:
var myModel = mongoose.model('myCollection', mySchema);
myModel.find({user:params.user,_id:params.documentId}, function(err,data){
var newMyModel = new myModel(data[0]);
for(var i=0; i<newMyModel.photos.length; i++){
if(newMyModel.photos[i]._id == params.photoId){
newMyModel.photos[i].mainPhoto = true;
}else{
newMyModel.photos[i].mainPhoto = false;
}
}
return newTrack.save(function(err,results){
console.log(err,results);
return cb(err,results);
});
});
but I'm getting an error:
'E11000 duplicate key error index: xx.$_id_ dup key: { : ObjectId(\'56269dea07a455920a21bca7\') }',
apart from the fact I feel there is a better way to do this
You may want to try a conditional type of update operation that first updates the documents based on the given criteria using the .update() method with a callback, and then do the other update in the callback for documents that do not satisfy the given criteria. For example, you could restructure this as:
var Model = mongoose.model('myCollection', mySchema);
Model.update(
{
"_id": params.documentId,
"user": params.user,
"photos.mainPhoto": true
},
{
"$set": {
"photos.$.mainPhoto": false
}
},
function(err, numAffected) {
Model.update(
{
"_id": params.documentId,
"user": params.user,
"photos._id": params.photoId /* criteria for setting the updating the documents to true */
},
{
"$set": {
"photos.$.mainPhoto": true
}
},
function(err, numAffected) {
/* Update complete. */
}
)
}
)