Formatting date in find() mongoDB/mongoose query result? - node.js

My MongoDB collection have an ISODate field and I want to format the result to dd/mm/yyyy.
My model:
const Pedido = new Schema({
id: {
type: String,
required: true
},
cliente: {
type: Schema.Types.ObjectId,
ref: 'clientes',
required: true
},
date: {
type:Date
}
})
mongoose.model('pedidos',Pedido)
And that's the query and render:
var query = await Pedido.find().populate('cliente').lean().exec()
res.render("admin/pedidos",{pedidos: query})
I'm using handlebars
{{#each pedidos}}
<h5 class="ordem1">Pedido #{{id}} <small>{{date}}</small></h5>
{{/each}}
It's showing a result like that:
Wed Apr 08 2020 21:00:00 GMT-0300 (GMT-03:00)
but I want to show: 08/04/2020
Could anybody help me with this? Thank you!!

we can use $dateToString operator to format date, check the mongoDb Docs
as you can see, we can use this $dateToString operator only in aggregate pipeline, in the $project step
here is a simple example in mongo playground mongoplayground
in your example, we could do the same process, but use $lookup instead of populate
the query may be something like that
Pedido.aggregate([
{
$match: {} // add your search here
},
{
$lookup: { // this is the alternative to the populate
from: 'clientes',
localField: 'cliente',
foreignField: '_id',
as: 'clientes'
}
},
{
$project: { // add all the fields you need from the collection, if you need to omit something from the query results, just don't mention it here
id: 1,
clientes: 1,
date: { $dateToString: { format: "%d/%m/%Y", date: "$date" } } // this will return the date in the format "dd/MM/yyyy"
}
}
])

Related

InsertMany() After Aggregation Pipeline - Not inserting projected fields correcrly

I am new to Mongodb so please bear with me. After I run the aggregation the projected fields are returned with res.json but they are not all inserted as new documents. Some of the projected fields are not in the schema (which im not sure if you can add anyway) but some fields like createdAt are in the schema but refuse to go into the newly created document and instead createdAt is populated with current date instead of projected field value.
Here is the code:
Click Schema:
const clickSchema = new mongoose.Schema({
page: String,
clickName: String,
clickProduct: String,
createdAt: { type: Date, default: new Date().toISOString().slice(0, 22).replace('T', ' ')},
})
PIPELINE:
Promise.all(tPromises).then(() => {
return Visit.aggregate([
{
$match: {
page: {
//Random Match Works
}
}
},
{
$lookup: {
from: "clickData",
localField: "clickName",
foreignField: "clickName",
as: "clickInfo"
}
},
{
$unwind: {
path: '$clickInfo'
}
},
{
// Send these fields from TrackingInfo to Merge
$project: {
page: '$page',
created_at: '$createdAt',
clickName: $clickName,
time: new Date().toISOString().slice(0, 22).replace('T', ' '),
randomField: 'randomVal'
}
},
])
}).then(docs => {
console.log(docs, 'docs')
Click.insertMany(docs)
return res.status(200).json({docs})
}).catch(err => {
console.log('error:', err)
return res.status(500).json({err})
});
I have tried many different ways of doing this including using a merge in the pipeline instead of Click.insertMany.
However a merge in the pipeline doesnt allow me to res.json() any results back (due to either some promise hell or because the pipeline doesnt return an array - I am not 100% sure why.)
Would appreciate any helpful pointers or tips!
Thankyou

MongoDB - update data in array of objects within object

I have a document in mongoDB structured like that
_id: ObjectId("generatedByMongo"),
name: {
required: true,
type: String,
trim: true
},
last: {
required: true,
type: String,
trim: true
},
grades: [{
grade: {
_id: ObjectId(""),
grade: Number,
date: date
}
}]
And to server I send array of objects containing 3 fields
[
{studentId}, {gradeId}, {newGrade}
]
What I'm trying to accomplish is I want to find in within that user collection grade with given gradeId and update it's value to newGrade. As far as I tried to do that I have done this
router.patch('/students/updateGrade',async(req,res) => {
const studentId = req.body.updateGradeArray[0].studentId;
const gradeId = req.body.updateGradeArray[0].gradeId;
const newGrade = req.body.updateGradeArray[0].newGrade;
try {
const student = await Student.find({_id: studentId})
.select({'grades': {$elemMatch: {_id: gradeId}}});
} catch(e) {
console.log(e);
}
}
);
If you intend to update just grade.grade(the number value), try this:
Student.updateOne(
// Find a document with _id matching the studentId
{ "_id": studentId },
// Update the student grade
{ $set: { "grades.$[selectedGrade].grade": newGrade } },
{ arrayFilters: [{ "selectedGrade._id": gradeId }] },
)
Why this should work:
Since you are trying to update a student document, you should be using one of MongoDB update methods not find. In the query above, I'm using the updateOne method. Inside the updateOne, I am using a combination of $set and $[identifier] update operators to update the student grade.
I hope this helps✌🏾

MongoDB Searching if element exist in array

So I have something like Survey Schema (I am using mongoose).
In this Schema, for each voting option, I have votes[] array that contains ObjectIds of Users.
Now I want to check if User can vote again, or if he already voted?
The simple solution is iterating thru votes with .indexOf() and checking if id exists. Now this is a blocking way for Node JS, since this operation is sync.
Is there a way to do this with Mongo aggregate or query? So for each voting option I would get additional field like:
didIVoted: true
My Schema looks like this:
const SurveySchema = new Schema({
title: {
type: String
},
options: [{
value: String,
votes: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }]
}]
}, { timestamps: true })
You can use $addFields and $map to overwrite existing options field. To check if userId exists in votes array you can use $indexOfArray
SurveySchema.aggregate([
{
$addFields: {
options: {
$map: {
input: "$options",
in: {
value: "$$this.value",
votes: "$$this.votes",
didIVote: { $ne: [ { $indexOfArray: [ "$$this.votes", userId ] }, -1 ] }
}
}
}
}
}
])

Using mongoose, how do I filter and then group by?

I am using mongoose and so far the query I use gets me all of the critiques based on a docId. I would like to group this result by distinct editors now. Except, my editors is an object.
This is what my critique query looks like:
Critique.find({docId:req.params.docId}).populate('editor', 'name username').exec(function(err, critiques){
if(err){
console.error("Cannot find critiques with docId: " + critiques.docId);
}
console.log(critiques);
res.jsonp(critiques);
});
This is my model I am querying:
var CritiqueSchema = new Schema({
className : String,
content: String,
eleId: Number,
type: String,
comments: String,
isAccepted: Boolean,
classes: String,
docId:{
type: Schema.ObjectId,
ref: 'Composition'
},
created: {
type: Date,
default: Date.now
},
editor: {
type: Schema.ObjectId,
ref: 'User'
},
});
UPDATE new query:
Critique.aggregate(
[ {$match : {docId : mongoose.Types.ObjectId(req.params.docId)}},
{$group : { _id : "$editor", critiques: { $push: "$$ROOT" } } }
]).exec(function(error, result){
if(!error)console.log(result);
else console.log(error);
});
What you need is $group in the aggregation framework. But aggregation and population don't go along. So you have two options populate and group the results by yourself by writing a loop or you can use $group to group them and then query each editor manually. The second is better as there will no duplication in editor queries whereas in population there will be significant duplication going on.
Critique.aggregate(
[{
$match:
{
docId: ObjectId(req.params.docid)
}
},
{ $group : { _id : "$editor", critiques: { $push: "$$ROOT" } } }
],
function(err,result){
if(!err){
/* result will be of the form:
[{_id:<an editor's objectid>,critiques:[{<critique1 document>},{<critique2 document>}...]}...]
*/
//you will have to manually query for each distinct editor(result[i]._id) which sucks
//because the call will be asynchronous in the loop and you can't send your response without using async library
//another option would be to use the $in operator on an array of the distinct critiques:
var editors = result.map(function(x) { return x._id } );
User.find({_id:{$in:editors}},{'username':1},function(err,editorDocs){
var editor_ids=editorDocs.map(function(x){return x._id})
var index;
for(var i=0;i<result.length;i++){
index=editor_ids.indexOf(result[i]._id);
result[i].editor=editorDocs[index].username;
}
//result is your final result. In the editor field of each object you will have the username of the editor
})
}
})
Check the docs for $$ROOT.

How to do query based on "date" column in sqlite3 using sequelize?

I'm using Sequelize with sqlite3 but having trouble querying data based on "date" type columns.
Here is my model definition:
var sensorData = sequelize.define('Data',
{
time: Sequelize.DATE,
value: Sequelize.FLOAT,
type: Sequelize.STRING,
source: Sequelize.STRING
},
{
timestamps : false
});
Now I want to get all the records with 'time' later than a given time in this function:
function selectData(start_date, num_of_records, callback)
{
sensorData.findAll({
limit: num_of_records,
where: [time, '???'], <====== How to specify the condition here?
order: 'time'}).success(callback);
}
I can't find any thing on Sequelize's website about 'date' type based query statement.
Any idea?
Just like Soumya says, you can use $gt, $lt, $gte or $lte with a date:
model.findAll({
where: {
start_datetime: {
$gte: new Date()
}
}
})
You can use sequelize.fn like this:
where:{timestamp: gte:sequelize.fn('date_format', fromDate, '%Y-%m-%dT%H:%i:%s')
,lte:sequelize.fn('date_format', toDate, '%Y-%m-%dT%H:%i:%s')
}

Resources