Date Format in mongoose, mongodb and node application - node.js

First time question and newbie coder so treat me like I'm 5 years old.
Okay so as I'm learning, I'm horrible at trying to understand how to use documentation to fix my problems. I'm setting up a node application with mongodb as the backend. The starting point is it is something that will pull via api baseball game results into a database (date, teams, scores). The problem I've ran into is I want to take in dates. When I do it comes back with the generic 1970's date and I can't seem to get it to just keep the formatted date. The source of the data and the date will be given programmatically and so it won't be prone to human error so I don't need date validation. I just need it to recognize the date and keep it's format so I can find or filter on it. The data will only have the year/month/date and not the time.
I'm setting this up in a mongoose file to load into my mongodb DB.
I've looked at https://mongoosejs.com/docs/api.html#mongoose_Mongoose-Date information, but I'm either missing the point or not seeing what's in front of me. Any hints on how to accept date information?
const GameDayStats = mongoose.model('GameDayStats', {
Date: {
type: Date
},
Home: {
type: String
},
HomeScore: {
type: Number
},
Visitor: {
type: String
},
VisitorScore: {
type: Number
},
Final: {
type: Boolean
}
})
const game = new GameDayStats({
Date: 2021-08-04,
Home: 'Cardinals',
HomeScore: 8,
Visitor: 'Pirates',
VisitorScore: 2,
Final: true
})
game.save().then(() => {
console.log(game)
}).catch((error) => {
console.log('Game Stat Error!', error)
})
What the output looks like
{
_id: 602e774fbee0692662ea16fa,
Date: 1970-01-01T00:00:02.009Z, <----I want this in normal date form
Home: 'Cardinals',
HomeScore: 8,
Visitor: 'Pirates',
VisitorScore: 2,
Final: true,
__v: 0
}

Related

Timezone problems with MongoDB and NodeJs

So the problem I'm currently having is :
I've a list of gym classes, which every class has an OpeningTime. I want to fetch all the classes from the current day, but. I'm not getting the same result locally and in production, cause for some reason, when Im deploying my backend to Heroku, the timezone is being setted by default to UTC (when I've a GMT-3 timezone).
Here is my query :
var now = moment();
var startOfDay = now.startOf('day').format();
var endOfDay = now.endOf('day').format();
var clasesDB = await Clase.find({ $and: [{ openingTime: { $gte: startOfDay } }, { openingTime: { $lte: endOfDay } }] })
So, like I said before, the problem is ocurring when, for example:
When I fetch the classes at my local time (Ex: 02-26-19 21:00hs ( GMT-3) ), the queries are actually showing the classes from 02-27, because, at MINE 21:00hs, on the server side is already 00:00, so, the date is not 02-26 anymore. And I dont want this kind of output.
How can I get a workaround to solve this?
Thanks in advance! :)
Don't use .format(), this makes a string. Compare directly Date values, i.e. use
var now = moment();
var startOfDay = now.startOf('day').toDate();
var endOfDay = now.endOf('day').toDate();
By default moment uses local times, so moment().startOf('day') returns midnight of local time. If you want to get midnight of UTC then use moment.utc().startOf('day').
If you don't rely on "local" time zone, then you can specify it like moment.tz("America/New_York").startOf('day')
No matter which time you need, never compare Date values by string, use always actual Date value.
By default in MongoDB a date field is stored in UTC, You can create a date with offset while writing to store it in your timeZone. But you've not done it while writing documents then you need to convert date field to your required timeZone while reading data from database. Check this below example.
JavaScript Code :
const today = new Date().toLocaleDateString(undefined, {
day: '2-digit',
month: '2-digit',
year: 'numeric'
}) // 02/26/2020. If your Heroic server is in different location then specify your locale in place of undefined - whereas undefined picks local time by default. Ex:- 'en-Us' for USA
Query :
db.collection.aggregate([
{ $addFields: { timewithOffsetNY: { $dateToString: { format: "%m/%d/%Y", date: "$openingTime", timezone: "America/New_York" } } } },
{ $match: { timewithOffsetNY: today } }, { $project: { timewithOffsetNY: 0 } }
])
Above query is written for New York timeZone, you can convert it according to your timeZone, In test url you can see 1st doc is not returned though it is on 2020-02-26 cause offset to New York as of today is 5hrs, So after converting date becomes as 2020-02-25.
Test : MongoDB-Playground
Ref : $dateToString

Time format in mongoose scheema Object

I have schema object like below
schedule: [{
time: {
type: Date,
required: true
}
}]
and when I try to post data in postman as
"schedule":[
{
"time":"18:00:00"
}]
I'm getting following error
validation failed: schedule.1.time: Cast to Date failed for value "12:00:00"
if I define type as String, everything is working fine.
I think I'm wrong at defining the type to accept the time. Can any one please help me to better way to define the object type
I have a simple question for you, Do you want to store just time(no date component eg. 18:00:00) or as a timestamp(date and time eg. 2019-06-11T06:24:46.642Z)?
If it's just time, then unfortunately you can't store it in date typed field because it accepts either date or a timestamp whereas string type can accept any arbitrary string.
I would either suggest storing 18:00:00 as string then process in however way you want after retrieval or storing a timestamp with field typed as date and use it along with its corresponding date.
Just to explain about the error, it occurs because 18:00:00 is no valid way to represent a date since the field is date typed.
As you have noticed, the type of field you have defined does not match the data you want to store in it. If you define it as type: Date, it will only accept valid dates and "18:00:00" is not.
From what I understand you want to do, I think the best option may be to store a valid date object even if you are only going to show or edit in your code the time on the client side.
Another possibility could be to declare three number fields at your model: hours, minutes and seconds.
EDIT:
To store just the time as you are asking for, the model could look like:
schedule: [{
hours: {
type: Number, required: true, min: 0, max: 23
},
minutes: {
type: Number, required: true, min: 0, max: 59
},
seconds: {
type: Number, required: true, min: 0, max: 59
}
}]
I don't know how are you going to treat this data but you could get a string like the one you were trying to store with a function like this:
function timeToString(h, m, s) {
if (h < 10) h = '0' + h;
if (m < 10) h = '0' + h;
if (s < 10) h = '0' + h;
return h + ':' + m + ':' + s;
}
Look the message it has telling
time: Cast to Date failed for value
Time and Date are different things
If you need to use the time following works as well
{
"time":"2019-06-12T13:34:00.000"
}
It's possible to create a custom type and store time. But I suggest you store the time as a string. In this case, a string is easy to parse, maintain and retrieve.
Add custom validation like this.
time: {
type: String,
validate: {
isAsync: true,
validator: function(v, cb) {
setTimeout(function() {
var timeRegex = /^(?:2[0-3]|[01][0-9]):[0-5][0-9]:[0-5][0-9]$/;
var msg = v + ' is not a valid time format!';
cb(timeRegex.test(v), msg);
}, 5);
},
message: 'Default error message'
},
required: [true, 'Time is required']
}

Mongoose create new data with incremented index-like field

What I want to do:
Whenever I add a new item to the collection (in my case a game), it will have a incremented "index"-like value (in my case I'm naming it index too).
My games collection should looks like:
[
{ "index":0, ... data }
{ "index":1, ... data }
{ "index":2, ... data }
]
The term is so hard to search. I always end up with:
$inc for update. Not this, I want to have incremented number on create.
Schema.index does look like what I want, but somehow it doesn't work at all:
const gameModel = new Schema({
index: {
type: Number,
default: 0
},
players: [{
name: String,
score: Number
}]
}, {
timestamps: {
createdAt: 'date'
}
});
gameModel.index({
index: 1
});
With this I always get index: 0. If I turn off default no index is created.
What do I do now? (I would prefer to keep the _id intact)
You can use a npm package named mongoose-auto-increment which provides this functionality. It is also very easy and well documented

String + autoincrement number on mongoose

I want to create a String + number every time a value is inserted on database (the number must be autoincrement).
Is it possible to do in Schema? Or do I need to do that before the value's inserted on database?
'question' -> String followed by an autoincrement number
var random = [{
question: 'question1'
},{
question: 'question2'
},{
question: 'question3'
},{
question: 'question4'
}];
const RandomSchema = new Schema({
question: {
type: String,
unique: true
}
})
Autoincrement fields in Mongodb, don't work exactly the same way that they do in an RDBMS. There is a lot more overhead involved. Never the less, creating an auto field is a solved problem. There is also a third party mongoose-autoincrement package that make it a lot easier.
Extending, from that. Your problem is a solved problem too. Because
the string part will always be the same
Simply use string concatenation to prepend 'question' to the auto increment field at display time.
Here is what I implemented with one of the approaches #e4c5 pointed out, without the use of a third-party package.
Define add.js as below:
db.counters.insert(
{
_id: "itemid",
seq: 0
}
)
function getNextSequence(id) {
var ret = db.counters.findAndModify(
{
query: { _id: id },
update: { $inc: { seq: 1 } },
new: true
}
);
return ret.seq;
}
db.randoms.insert(
{
question: "question"+getNextSequence("itemid"),
}
)
db.randoms.insert(
{
question: "question"+getNextSequence("itemid"),
}
)
After starting a mongod instance, do mongo < add.js.

In Mongoose, how to filter an array of objects

I have the following schema:
var sampleSchema = new Schema({
name: String,
dates: [{
date: Date,
duration: Number
}]
});
I'd need to filters the records according to the following rule: if one of dates is later than a given date date_begin, keep the record, otherwise, don't.
I have the impression that $gte or $lte are the function I need, but I can't find a way to use them correctly. I tried
sampleSchema.find({date_begin: {$gte: 'date'}});
or some variants of that, but I can't seem to be able to make it work. Anyone has an idea of how I am supposed to do this?
To do querying on elements inside arrays, $elemMatch is used :
SampleModel.find( { dates : { $elemMatch: { date : { $gte: 'DATE_VALUE' } } } } )
If you're using a single query condition, you can directly filter:
SampleModel.find( { 'dates.date': { $gte: 'DATE_VALUE' } } )

Resources