mongoDB find most popular posts within a week from the current date - javascript

I have a controller where I am trying to query the most popular posts within the last week, sorted by most popular, and has a max cap of 50 posts. I am trying to use the aggregate() method; however, I am not sure if I am doing it correctly. When I run the query In insomnia I get an error like so:
{
"ok": 0,
"code": 8000,
"codeName": "AtlasError"
}
Here is my post model:
const postSchema = mongoose.Schema({
title: {
type: String,
required: true
},
message: {
type: String,
required: true
},
//replace creator with name
name: String,
creator: String,
tags: [String],
size: String,
selectedFile: String,
likes: {
type: [String],
default: [],
},
comments: {
type: [String],
default: []
},
createdAt: {
type: Date,
default: new Date(),
},
dogTreats: {
type: Number,
default: 0,
required: false,
}
});
and here is my controller/post.js
export const getPopular = async (req, res) => {
//get current time
let currentTime = new Date()
//get from 7 days ago
currentTime.setDate(currentTime.getDate()-7)
console.log(currentTime) // -> output 2022-09-04T19:29:39.612Z
try {
//sort posts by most likes and within 7 days ago, but with a max of 50 posts
const mostPopular = await PostMessage.aggregate([{"$sort": { likes: -1}}, { "$limit": 50}, { "$gt": currentTime }])
res.status(200).json(mostPopular)
} catch (error) {
res.status(500).json(error)
}
}

You can use find method. It is better to use here.
If you need to reach a value from another table populated, aggregation is better to use. However, at here, find is the best way to reach datas.
const mostPopular = await PostMessage.find({createdAt: {$gt : currentTime}}).sort({likes: -1}).limit(50)

Try this aggregation
export const getPopular = async (req, res) => {
//get current time
let currentTime = new Date()
//get from 7 days ago
currentTime.setDate(currentTime.getDate() - 7)
console.log(currentTime) // -> output 2022-09-04T19:29:39.612Z
try {
//sort posts by most likes and within 7 days ago, but with a max of 50 posts
const mostPopular = await PostMessage.aggregate([
{ $match: { createdAt: { $gt: currentTime } } },
{ $sort: { likes: -1 } },
{ $limit: 50 }
])
res.status(200).json(mostPopular)
} catch (error) {
res.status(500).json(error)
}
}

Related

How to add an object to an array of object, using addToSet, or push operators in mongodb

I have an array of reviews, I want to add a review using addToSet that will check if user is present in the array, then we do not want to add since one user can only review once.
My schema looks like this:
const sellerSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
unique: true,
},
reviews: [
{
by: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
unique: true,
},
title: {
type: String,
},
message: {
type: String,
},
rating: Number,
imagesUri: [{ String }],
timestamp: {
type: Date,
default: Date.now,
},
},
],
});
I might be doing the query wrong, but can't figure out how to add a review and check if current user has not reviewed before.
Here is the query where I add the review:
router.post("/review/:_id/", async (req, res) => {
try {
const stylist_id = mongoose.Types.ObjectId(req.params._id);
const review = {
by: req.user._id,
title: req.body.title,
message: req.body.message,
rating: parseFloat(req.body.rating),
};
if (req.body.imagesUri) {
//if there is images, we need to set it up
review.imagesUri = req.body.imagesUri;
}
await Seller.updateOne(
{ _id: seller_id },
{ $addToSet: { reviews: review } } //get the review that matches to the user_id
);
return res.status(200).send(true);
}catch(err){
res.status(502).send({
error: "Error creating a review.",
});
}
});
I'm thinking of checking for seller's id and also check that no review is by current user, but it is not working.
const userID = req.user._id;
await Seller.updateOne(
{ _id: seller_id, reviews: { $elemMatch: { by: { $ne: { userID } } } } },
{ $addToSet: { reviews: review } } //get the review that matches to the user_id
);
ANSWER:
I was able to solve the issue, in case other people have same issue. I did this:
await Seller.updateOne(
{
_id: seller_id,
"reviews.by": { $nin: [req.user.id] },
//knowing req.user._id is a mongoose.Types.ObjectId.
//You can also use [id1, id2, ...] to the array to check for other id's
},
{ $addToSet: { reviews: review } } //get the review that matches to the user_id
);
Here is the documentation for $nin operator: https://www.mongodb.com/docs/manual/reference/operator/query/nin/
You are pushing the review object inside an object.
Instead do this:
await Seller.updateOne(
{ _id: seller_id },
{ $addToSet: { reviews: review } }
);

Mongoose does not produce a result but mongo shell does

I have schema for products and it has a field storeZones object in it defined as
...
storeZones: {
type: {
masterZone: {type: Schema.Types.ObjectId, model: 'Zone', index: 1 },
zone: { type: Schema.Types.ObjectId, model: 'Zone', index: 1 },
subZone: { type: Schema.Types.ObjectId, model: 'Zone', index: 1 },
},
default: {
masterZone: null,
zone: null,
subZone: null,
},
},
...
I am counting for products in a specific masterZone. So my query is
const condition = { 'storeZones.masterZone': masterZone };
console.log(condition); // { 'storeZones.masterZone': '60533e6a745d465ab6cb3fc9' }
const total = await Product.count(condition);
This returns 0 results.
But when i paste the exact query in mongo shell; Robo3t to be exact.
db.products.find({'storeZones.masterZone': ObjectId('60533e6a745d465ab6cb3fc9') } )
It produces the desired output. Can someone please provide some assistance?
Fixed it by converting the masterZone from request to an ObjectId. Idk why i needed to do this, but that fixed it! so...
const m = mongoose.Types.ObjectId(masterZone);
const condition = { 'storeZones.masterZone': m };
console.log(condition); // { 'storeZones.masterZone': '60533e6a745d465ab6cb3fc9'}
const total = await Product.count(condition);

How to exclude Sundays from date range in mongodb query?

I need to get each user's transactions every three days. I want to know users that don't have up to a certain amount(200) within the three days period, then get the sum of all the transactions for each user. I want to exclude Sunday since transactions are always low on Sundays.
I want to make sure this is done right from the DB because the transactions from each user can run into thousands even millions.
I am using dayjs to manipulate the time but I am not getting it right
I have been able to get the three previous date and the current date. The previous date will be the startDate and the current date will be endDate.
But I need to remove if Sunday is in the range and use that to query the database.
This is what I have done what I am not close to fixing it.
How can I query the transaction table by dateCreated and exclude sundays?
schema sample
export const TransactionSchema = new mongoose.Schema({
description: {
type: String
},
ref: {
type: String,
required: true
},
userID: {
type: mongoose.SchemaTypes.ObjectId,
ref: 'User'
},
amount: {
type: Number,
required: true,
},
commission: {
type: Number,
default: 0
},
responseDescription: {
type: String
},
authCode: {
type: Number
},
isDeleted: {
type: Boolean,
default: false
},
dateCreated: {
type: Date,
default: Date.now
},
dateUpdated: {
type: Date,
required: false
},
openingBalance: {
type: Number
},
closingBalance: {
type: Number
},
})
method
async getUserRequiredTargetTrans() {
let now = dayjs();//endDate
let fromThreeDays = now.subtract('2', 'day')
let sunday = now.day(0)
let withOutSunday = now.diff(fromThreeDays);//startDate
const response = await this.transactionModel.find({ isDeleted: false, dateCreated: { $gte: withOutSunday, $lt: now } })
To exclude sundays from date range, you can use $where operator like this:
async getUserRequiredTargetTrans() {
let from = dayjs();//endDate
let fromThreeDays = now.subtract('2', 'day')
const response = await this.transactionModel.find({ isDeleted: false, dateCreated: { $gte: fromThreeDays, $lt: now },
// exculde sunday
$where: `function() { return this.dateCreated.getDay() !== 0;}`
} )
You pass a dayjs, don't know whether you can use it directly. Perhaps you have to use
find({ isDeleted: false, dateCreated: { $gte: withOutSunday.toDate(), $lt: now.toDate() } })

Can't get populate() to fill out array in Mongoose

Let me begin by saying I know that this seems to be a frequently asked question and I've spent a couple of days trying to figure it out but no answer seems to work so I'm trying on here.
I have two models, User and Chapter: a Chapter can have have many members (Users). When I do router.get('/chapters') I want to see an array of all the Users associated with a Chapter as a property along with the other Chapter properties, like so:
[
{
"leads": [],
"members": [
{"_id":"someString1","firstName":"...", "lastName":"..."},
{"_id":"someString2","firstName":"...", "lastName":"..."},
],
"date": "2018-12-12T15:24:45.877Z",
"_id": "5c11283d7d13687e60c186b3",
"country": "5c11283d7d13687e60c185d6",
"city": "Buckridgestad",
"twitterURL": "qui",
"bannerPic": "http://lorempixel.com/640/480/people",
"__v": 0
}
]
But what I'm getting is this:
[
{
"leads": [],
"members": [],
"date": "2018-12-12T15:24:45.877Z",
"_id": "5c11283d7d13687e60c186b3",
"country": "5c11283d7d13687e60c185d6",
"city": "Buckridgestad",
"twitterURL": "qui",
"bannerPic": "http://lorempixel.com/640/480/people",
"__v": 0
}
]
These are my Schemas:
Chapter
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
// Create Schema
const ChapterSchema = new Schema({
country: {
type: Schema.Types.ObjectId,
ref: "countries"
},
city: {
type: String,
required: true
},
leads: [
{
type: Schema.Types.ObjectId,
ref: "users"
}
],
members: [
{
type: Schema.Types.ObjectId,
ref: "users"
}
],
twitterURL: {
type: String,
required: true
},
bannerPic: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now()
}
});
module.exports = Chapter = mongoose.model("chapters", ChapterSchema);
User
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
// Create Schema
const UserSchema = new Schema({
username: {
type: String,
required: true
},
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
organisation: {
type: String,
required: true
},
chapter: {
type: Schema.Types.ObjectId,
ref: "chapters"
},
email: {
type: String,
required: true
},
admin: {
type: Boolean,
default: false
},
lead: {
type: Boolean,
default: false
},
password: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now()
}
});
module.exports = User = mongoose.model("users", UserSchema);
Like I said, when I call the endpoint, I want it to return me all the Chapters with all the Users as a populated property.
I've tried a lot of variations of .populate() but to know luck. The closest I got was going through the early levels of callback hell which I know isn't necessary with today's tech, but nothing is working!
My routes/api/chapters.js
// #route GET api/chapters
// #desc Get all Chapters
// #access Public
router.get("/", (req, res) => {
Chapter.find()
.populate("members")
.then(chapters => {
return res.json(chapters);
})
.catch(err =>
res.status(404).json({ nochaptersfound: "No Chapters found" })
);
});
I can get it to work the other way around:
My routes/api/users.js
// #route GET api/users
// #desc Return all users
// #access Public
router.get("/", (req, res) => {
User.find()
.populate("chapter")
.exec()
.then(users => res.status(200).json(users))
.catch(err => console.log(err));
Returns a user with the populated Chapter, but I can't populate the chapter.members array
Any help would be greatly appreciated!
Thanks!!
From your comment, I believe you are not actually storing users in your chapters. What you are doing is this:
User.create({..., chapter: id})...
And assuming chapter now has a user. Its not the way it works with mongoose, so if you want to actually save in both place, you will need to do it yourself. You are thinking about this as if it were a relational database
You will need to do something like:
const user = await User.create({..., chapter: id})
const chapter = await Chapter.findOne({ _id: id })
chapter.members.push(user)
chapter.save()
If your populate wasn't working, you'd not get an empty array, you'd get an array with ids. Your current populate query is fine, you just don't have any data to populate
With promises, it would look like this:
var userPromise = User.create({..., chapter: id}).exec()
var chapterPromise = Chapter.findOne({ _id: id }).exec()
Promise.all([userPromise, chapterPromise]).then((user, chapter) => {
chapter.members.push(user)
return chapter.save()
}).then(chapter => {
// send response
})
If you need 10 chapters with 10 to 50 users, I'd create 50 users, then push all of them into the chapters and save the chapter.

How to retrieve data from collection using find() method in custom date format

my Schema
Customers.js
import mongoose from 'mongoose';
const Customers = mongoose.Schema({
CustomerID: { type: String, default: "" },
Name: { type: String, default: "" },
Email: { type: String, default: "" },
PhoneNumber: { type: String, default: "" },
Join_Date: { type: Date, default: null }
}, { collection: 'Customers' });
export default mongoose.model('Customers', Customers);
my router controller
import Customers from "./Customers";
router.post('/List_All_Customers', (req, res) => {
Customers.find().lean().exec().then((Data) => {
res.json({Data: Data});
}).catch((err) => {
console.log(err);
});
})
My Current Result
{
Data: [
{
CustomerID: "6ad050d4-04ac-41f2-8c93-49f68f106889",
Name: "Uday Kumar",
Email: "uday#blaabla.com",
PhoneNumber: "+91-991010191",
Join_Date: "2018-04-24T12:00:00.000Z"
},
{
CustomerID: "792b67f9-9026-43bc-9017-46cd2568b4e9",
Name: "Prem Kumar",
Email: "prem#blaabla.com",
PhoneNumber: "+91-881010091",
Join_Date: "2018-04-24T15:00:00.000Z"
}
]
}
Expecting Result
{
Data: [
{
CustomerID: "6ad050d4-04ac-41f2-8c93-49f68f106889",
Name: "Uday Kumar",
Email: "uday#blaabla.com",
PhoneNumber: "+91-991010191",
Join_Date: "Apr-24 2018, 12:00:00"
},
{
CustomerID: "792b67f9-9026-43bc-9017-46cd2568b4e9",
Name: "Prem Kumar",
Email: "prem#blaabla.com",
PhoneNumber: "+91-881010091",
Join_Date: "Apr-24 2018, 15:00:00"
}
]
}
Is there anyway in mongodb for result in custom dates. I am using mongoose mongodb connection in my project.
I can manipulate data using for loop with momentjs but it is taking time.
So i need one solution for my question.
Thanks in advance.
Comments are appreciated.
You Can use javascript map function
like
import Customers from "./Customers";
router.post('/List_All_Customers', (req, res) => {
Customers.find().lean().exec().then((Data) => {
Promise.resolve(arr.map(item=>{
item.Join_Date = fnToConvertDateToYourFormate(item.Join_Date);
return item;
})).then(Data=>{
res.json({Data: Data});
});
}).catch((err) => {
console.log(err);
});
})
OR
You Can Use Mongoose MapReduce
http://mongoosejs.com/docs/api.html#mapreduce_mapReduce
var o = {};
o.map = function () {
this.Join_Date = fnToConvertDateToYourFormate(this.Join_Date);
emit(this.CustomerId,this)
}
o.reduce = function (k, vals) { }
mongoose.model('Customers').mapReduce(o, function (err, results) {
console.log(results)
});
You can use aggregate query. An example for this query is given below:-
db.customers.aggregate([
{$project:
{yearMonthDayUTC:
{$dateToString:
{format: "%Y-%m-%d",
date: "$date"
}
},
}
}
])
For this query, the date should be in ISO. So, while inserting element into the db, you can use new Date() as this returns the current date as a Date object. The mongo shell wraps the Date object with the ISODate helper.
An example for inserting data in db is given below:-
db.sales.insert({ "_id" : 4, "item" : "mansi", "price" : 10, "quantity" : 2, "date" : new Date(Date.now()) })
You can try mongoDB $dateToString aggregation.
Customers.aggregate([
{
$project: {
"other_field": 1, // and so on as many fields you need
Join_Date: {
{ $dateToString: { format: "%Y-%m-%d", date: "$Join_Date" } }
}
}
}
]).then((Data) => {
res.json({Data: Data});
}).catch((err) => {
console.log(err);
});
Read more about it at: https://docs.mongodb.com/manual/reference/operator/aggregation/dateToString/

Categories

Resources