Ambiguous Unexpected Identifier error - javascript

Mongo isn't liking some identifier that I've got in the forEach loop holding the second aggregation, and for the life of me I can't find which one it is. I've been looking at it all day and at this point I just need another pair of eyes on it. My eyes, brain, and heart thank you in advance!
use events
var affected = []
var start = new Date()
var end = new Date("2017-06-01T00:00:00Z")
for (var dayPast = new Date(start); start >= end; start.setDate(start.getDate() - 1)) {
dayPast.setDate(dayPast.getDate() - 1)
var results = db.completion_event.aggregate([{
$match: {
applicationId: 1,
dateCreated: {
$lt: start,
$gte: dayPast
},
"data.name": "eventComplete",
"data.metadata.aggregationId": /identifying_string.*/,
"data.sponsorIds": {$in: [1,2,3,4,5,6]}
}
}, {
$project: {
customerId: 1,
dateCreated: 1,
"data.metadata.aggregationId": 1
}
}, {
$group: {
_id: {
customerId: "$customerId",
dateCreated: "$dateCreated",
aggregationId: "$data.metadata.aggregationId"
},
"total": {
$sum: 1
}
}
}], {
$limit: 1
}, {
allowDiskUse: true
}).toArray()
results.forEach(function(event) {
use rewards
var state = db.customer_state.find({customerId: event._id.customerId}).sort({_id: -1}).limit(1).toArray()[0]
var planId = state.planId
var plan = db.plan.find({id: planId}).toArray()[0]
if(plan.schedule.activeStart < new Date() < plan.schedule.activeEnd) {
use events
var latest = db.completion_event.aggregate([{
$match: {
applicationId: 1,
customerId: event._id.customerId,
dateCreated: {
$gte: plan.schedule.activeStart
},
"data.name": "outterEventComplete",
"data.metadata.aggregationId": event._id.aggregationId
}
},
{
$project: {
consumerId: 1,
dateCreated: 1,
"data.sponsorIds": 1,
"data.metadata.aggregationId": 1
}
}], {
$limit: 1
}).toArray()
affected.push(latest[0])
}
})
}
print(affected)
And the current bane of my existence:
E QUERY SyntaxError: Unexpected identifier

I'm betting on use rewards and use events. Those are shell shortcuts, you're not supposed to use them in the middle of regular javascript code. Here's an alternative:
Instead of switching db via use rewards use this
var rewards_db = db.getSisterDB('rewards');
rewards_db.customer_state.find(...)
Same for events, naturally.

Related

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

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

Check DB Array for Duplicate Key on Form Submit

This is a MERN stack app. I have a "WeekMealPlans" collection and "Days" collection, where each Day must have a WeekMealPlan and a "DayOfWeek", as in "Friday," and it has a name that combines WeekMealPlan and DayOfWeek like "Week 1 - Monday."
I want to prevent a duplicate combo of WeekMealPlan + DayOfWeek, so in my Day Mongoose Model, "name" has attribute "unique."
In React, I set a Days array in State with an Axios call, so all Days are available to check for duplicates on form submit.
On form submit, I just want to take the generated "Day Name," check it against the Days array for a duplicate, and alert the user if a dup is found.
I've tried an If Statement inside a For Loop, as well as Array.find. Both do not seem to work per the below. Do If Statements, For Loops and Array.find, as well as Undefined variable not work the same in React as in Vanilla JS?!
//Here's the Days Array:
[
{
_id: "609f3e444ee536749c75c72b",
dayOfWeek: "Monday",
weekMealPlan: {
_id: "609f3e444ee536749c75c72a",
name: "JD Hypertrophy Week 1",
GRFUser: "609f3e444ee536749c75c729",
createdAt: "2021-05-15T03:21:40.285Z",
updatedAt: "2021-05-15T03:21:40.285Z",
__v: 0,
},
__v: 0,
},
{
_id: "610dbb89bebaea6004ce9f53",
dayOfWeek: "Sunday",
weekMealPlan: {
_id: "609f3e444ee536749c75c72a",
name: "JD Hypertrophy Week 1",
GRFUser: "609f3e444ee536749c75c729",
createdAt: "2021-05-15T03:21:40.285Z",
updatedAt: "2021-05-15T03:21:40.285Z",
__v: 0,
},
createdAt: "2021-08-06T22:45:29.826Z",
updatedAt: "2021-08-06T22:45:29.826Z",
__v: 0,
},
{
_id: "622ac86263a8575ecb8c0f5e",
name: "JP Nash's WMP - Friday",
dayOfWeek: "Friday",
weekMealPlan: {
_id: "62283f3d398c00aee52b7e99",
name: "JP Nash's WMP",
GRFUser: "62283f21398c00aee52b7e93",
createdAt: "2022-03-09T05:46:37.756Z",
updatedAt: "2022-03-09T05:46:37.756Z",
__v: 0,
},
createdAt: "2022-03-11T03:56:18.136Z",
updatedAt: "2022-03-11T03:56:18.136Z",
__v: 0,
},
];
//For Loop method:
let isDayDup = false;
const daysArray = this.state.days;
const dayName = this.state.name;
let i = 0;
{
for (i = 0; i < daysArray.length; i++) {
if (daysArray[i].name == dayName) {
isDayDup = true;
}
}
}
console.log(isDayDup);
//No matter whether the passed Day Name is or is not a duplicate, the result is "false"!
//Array.find method:
const daysArray = this.state.days;
const dayName = this.state.name;
function findDayDup(thisDay) {
return thisDay.name == dayName;
}
const dupDay = daysArray.find(findDayDup);
if (dupDay == undefined) {
console.log("Duplicate Day Name!");
} else {
console.log("Day is OK!");
}
//No matter whether the passed Day Name is or is not a duplicate, the result is "Duplicate Day Name"!

Running sequelize with two where conditions

I have a mysql db instance with a table consisting of a various fields. Relevant fields are start, start time, and status
start: YYYY-MM-DD
startTime: HH:mm:ss
status: ENUM('cancelled', 'scheduled, etc)
If I want to get a list of all entries that don't have status = 'cancelled' and that occur today or after, I would write this:
return {
where: {
status: {
$ne: 'cancelled'
},
$or: {
start: { $gte: moment().utc().format('YYYY-MM-DD') },
$and: {
isRepeating: 1,
$or: [{
end: {
$gte: moment().format(),
}
},
{
end: {
$eq: null,
}
}]
},
}
},
I am trying to modify this query to not only give me entries that occur today or after, but also greater than right now (time wise, UTC). My attempt was to first filter based on startTime, and then filter based on startDate, but it does not seem to be working:
return {
where: {
status: {
$ne: 'cancelled'
},
$or: {
startTime: { $gt: moment.utc().format('HH:mm:ss') },
$and: {
start: { $gte: moment().utc().format('YYYY-MM-DD') },
$and: {
isRepeating: 1,
$or: [{
end: {
$gte: moment().format(),
}
},
{
end: {
$eq: null,
}
}]
}
},
}
},
(does not work, because it just returns everything!)
I also cannot do something more simple like
where: {
startTime: { $gt: moment.utc().format('HH:mm:ss') },
start: { $gte: moment().utc().format('YYYY-MM-DD') },
}
Because then it will ignore, for example, entries that occur tomorrow date wise, but occur earlier in the day than the current timestamp.
Thanks!
You can use Op.and operator to combine those conditions.
const { Op } = require("sequelize");
...
where: {
[Op.and]: [
startTime: { $gt: moment.utc().format('HH:mm:ss') },
start: { $gte: moment().utc().format('YYYY-MM-DD') }
]
}
...

how to count length of aggregate before skip(pagination)

I am building an api to get details of jobs and I need to do pagination for it. For pagination I need to get total number of pages, but I'm getting only skip pages. Please help me to get total number of pages before skip.
let cJobs = await CrewbiesJobs.GetAllJobs();
let flashJobsResult = [];
let totalPages = 0;
let filter = {};
let queryLimit = parseInt(req.query.limit) || 10;
let pageNo = parseInt(req.query.page) || 1;
let query = {};
if (pageNo < 0 || pageNo === 0) {
throw new CustomError('invalid page number, should start with 1', HttpStatus.BAD_REQUEST);
}
query.skip = req.query.limit * (pageNo - 1) || 1;
query.limit = queryLimit;
let jobsAggregate = await Jobs.aggregate([{
$lookup: {
from: CrewbiesJobs.collection.name,
localField: "jobId",
foreignField: "jobId",
as: "job_docs"
}
},
{
$unwind: "$job_docs"
},
{
$project: {
_id: 1,
jobTitle: 1,
jobId: 1,
jobDescription: 1,
postedDate: 1,
filter1: 1,
filter2: 1,
filter3: 1,
createdAt: 1,
updatedAt: 1
}
},
{
$match: filter
},
{
$skip: query.skip
},
{
$limit: query.limit
}
]).exec(function(err, doc) {
if (err) {
res.send(err);
} else {
totalPages = Math.ceil(doc.length / queryLimit);
if (pageNo > totalPages) {
throw new CustomError('Invalid page number', HttpStatus.BAD_REQUEST);
}
console.log('matched jobs ', doc.length);
res.json({
msg: 'Jobs listed successfully',
item: {
totalPages: doc.length,
currentpage: pageNo,
jobs: doc
},
jobsCount: doc.length
});
}
});
}
catch (err) {
CustomError.Handle(err, res);
}
totalPages counts should return 21 but am getting only 10 instead:
*{
"msg": "Jobs listed successfully",
"item": {
"totalPages": 10,
"currentpage": 1,
},
"jobsCount": 10
}*
An option to make a single trip to db and get the count of documents is to use $facet which allows to process multiple aggregation pipelines. Since $count wouldn't work with $addFields or $project in conjunction with the actual pipeline result.
Query: (After unwinding the job_docs do $facet stage)
Jobs.aggregate([
{
$lookup: {
from: CrewbiesJobs.collection.name,
localField: "jobId",
foreignField: "jobId",
as: "job_docs"
}
},
{
$unwind: "$job_docs"
},
{
$facet: {
totalDocs: [
{
$count: "value"
}
],
pipelineResult: [
{
$project: {
_id: 1,
jobTitle: 1,
jobId: 1,
jobDescription: 1,
postedDate: 1,
filter1: 1,
filter2: 1,
filter3: 1,
createdAt: 1,
updatedAt: 1
}
},
{ $match: filter },
{ $skip: query.skip },
{ $limit: query.limit }
]
}
},
{ $unwind: "$totalDocs" }
]).exec();
Resultant Document: Demo
{
"totalDocs" : {
"value" : 44
},
"pipelineResult" : [
{
"_id" : ObjectId("5da7040e45abaee927d2d11a"),
"jobTitle" : "Foo",
"jobDescription": "Bar",
...
"job_docs" : {...}
},
...
]
}
Where totalDocs contain the count of documents in value property and pipelineResult would contain the documents of main pipeline operations.
As far as I know you need to have a seperate query for total count.
The both aggregations have common stages so first I created the baseStages.
Then I added skip and limit stages to this base stage for the jobs data, and added the count stage to the base stage to get total count of collections.
So you can try something like this:
const baseStages = [
{
$lookup: {
from: CrewbiesJobs.collection.name,
localField: "jobId",
foreignField: "jobId",
as: "job_docs"
}
},
{
$unwind: "$job_docs"
},
{
$project: {
_id: 1,
jobTitle: 1,
jobId: 1,
jobDescription: 1,
postedDate: 1,
filter1: 1,
filter2: 1,
filter3: 1,
createdAt: 1,
updatedAt: 1
}
},
{ $match: filter }
];
const jobsStages = [
...baseStages,
{ $skip: query.skip },
{ $limit: query.limit }
];
let jobsAggregate = await Jobs.aggregate(jobsStages);
const countStages = [...baseStages, { $count: "COUNT" }];
let countAggregate = await Jobs.aggregate(countStages);
To construct the response I would console.log(countAggregate), and inspect where the count value resides, and use that value in response.

Mongoose $slice and get orginal size array

I'm currently trying to get the total amount of items in my News object, and return a slice of the items as objects.
I found out how to use the $slice operator in my query, but I don't know how to get the original size of the array of items.
The code I'm currently using in NodeJS:
if (req.query.limit) {
limit = 5;
}
News.findOne({ connected: club._id }, {items: {$slice: limit}}).exec(function (err, news) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else if (!news || news.items.length === 0) {
res.jsonp([]);
} else {
const returnObj = { items: [], totalNumber: 0 };
const items = news.items.sort(function (a, b) {
return b.date - a.date
});
res.jsonp({
items: items,
totalNumber: news.items.length
});
}
});
The Mongo model:
var mongoose = require('mongoose'),
validator = require('validator'),
Schema = mongoose.Schema;
var NewsSchema = new Schema({
connected: {
type: Schema.Types.ObjectId,
required: 'Gelieve een club toe te wijzen.',
ref: 'Club'
},
items: [{
userFirstName: String,
action: String,
date: Date,
targetName: String
}],
created: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model('News', NewsSchema);
How would I do this efficiently?
Thanks!
EDIT: final code which works:
News.aggregate([{
$match: {
connected: club._id
}
}, {
$project: {
totalNumber: {
$size: '$items'
},
items: {
$slice: ['$items', limit]
}
}
}
]).exec(function (err, news) {
console.log(news);
if (!news || news[0].items.length === 0) {
res.jsonp([]);
} else {
res.jsonp(news[0]);
}
});
You cannot have both information at once using find and $slice.
The soluce you have :
Use aggregate to return the count and only the sliced values.
Like :
[{
$project: {
count: {
$size: "$params",
},
params: {
$slice: ["$params", 5],
},
},
}]
To help you out making aggregate, you can use the awesome mongodb-compass software and its aggregate utility tool.
Use a find without $slice, get the number of item there, and then slice in javascript the array before returning it.
EDIT :
[{
$sort: {
'items.date': -1,
},
}, {
$project: {
count: {
$size: "$items",
},
params: {
$slice: ["$items", 5],
},
},
}]

Categories

Resources