Date.now() returns wrong date - javascript

I have this mongoose schema
const postSchema= new Schema({
body:{ type: String, required:true, validate:bodyValidators},
createdBy: { type: String},
to: {type:String, default:null },
createdAt: { type:Date, default:Date.now()},
likes: { type:Number,default:0},
likedBy: { type:Array},
dislikes: { type:Number, default:0},
dislikedBy: { type:Array},
comments: [
{
comment: { type: String, validate: commentValidators},
commentator: { type: String}
}
]
});
every posts on my app is returns Aug 20, 2017 at 10:01 PM but where i am it's 2 hours more.
here's the HTML, i'm using Angular4.
{{post.createdAt | date: 'MMM dd, yyyy'}} at {{post.createdAt | date:'shortTime'}}
I just can't understand why

Mongoose is receiving the value calculate at the time you start it, instead of the function. You should pass it the function instead of running it. Whenever you use parenthesis you are executing the function instead of referencing it. I had the same problem once.

In mongoose you can do like following. Using update() or findOneAndUpdate()
with {upsert: true} option
you can use $setOnInsert without having to pass them in everytime new MyModel() is called.
var update = {
updatedAt: new Date(),
$setOnInsert: {
createdAt: new Date()
}
};

You have assigned the value in the wrong way. try this:
createdAt: {
type: Date,
default: Date.now
}
Read this link for further understanding:
http://mongoosejs.com/docs/guide.html

Related

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

How to count number of ObjectId occurrences in an array of ObjectIds' and attach the result to each document that is being returned?

I have a Tag schema:
const TagSchema = new Schema({
label: {type: String, unique: true, minlength: 2},
updatedAt: {type: Date, default: null},
createdAt: {type: Date, default: new Date(), required: true},
createdBy: {type: mongoose.Schema.Types.ObjectId, ref: 'Account', default: null}
});
const Tag = mongoose.model('Tag', TagSchema);
Then I have a Page schema:
const PageSchema = new Schema({
tags: {type: [{type: mongoose.Schema.Types.ObjectId, ref: 'Tag'}], default: [], maxlength: 5}
});
const Page = mongoose.model('Page', PageSchema);
As you can see it contains a Tag reference in its tags array.
Now what I need to do is when I fetch all tags via /tags, I also need to count the number of times each tag is used in all Pages.
So if a Tag is used 2times across all Pages, it should set an .occurrences property on the returned tag. For example this would be a response from /tags:
[
{_id: '192398dsf7asdsdds8f7d', label: 'tag 1', updatedAt: '20170102', createdAt: '20170101', createdBy: '01238198dsad8s7d78ad7', occurrences: 2},
{_id: '19239asdasd8dsf7ds8f7d', label: 'tag 2', updatedAt: '20170102', createdAt: '20170101', createdBy: '01238198dsad8s7d78ad7', occurrences: 1},
{_id: '192398dsf7zxccxads8f7d', label: 'tag 1', updatedAt: '20170102', createdAt: '20170101', createdBy: '01238198dsad8s7d78ad7', occurrences: 5},
]
I would have imagined that I could achieve this pretty easily in a mongoose pre('find') hook like this:
TagSchema.pre('find', function() {
Page.count({tags: this._id}, (err, total) => {
this.occurrences = total;
});
});
However, there are two problems here:
Page.count throws an error saying it's not a function, which I don't understand why because I use it somewhere else perfectly fine. And Page has been imported properly. Suggesting that you can't use count in a hook or something similar.
this is not the Tag document.
So I guess that the way I am going about this is completely wrong.
Since I am a novice in MongoDB maybe someone can provide me with a better, working, solution to my problem?
db.Page.aggregate([
{
$unwind:"$tags"
},
{
$group:{
_id:"$tags",
occurrences:{$sum:1}
}
},
{
$lookup:{ //get data for each tag
from:"tags",
localField:"_id",
foreignField:"_id",
as:"tagsData"
}
},
{
$project:{
tagData:{$arrayElemAt: [ "$tagsData", 0 ]},
occurrences:1
}
}
{
$addFields:{
"tagData._id":"$_id",
"tagData.occurrences":"$occurrences"
}
},
{
$replaceRoot: { newRoot: "$tagData" }
}
])
Ok first, you need getQuery() this allows you to access the properties that you are finding the tag with. so if you find the tag by _id, you will have access to it in the pre hook by this.getQuery()._id
Second, you are going to need to use the "Parallel" middleware, can be found in the docs. This allows the find method to wait until the done function is called, so it is gonna wait until the tag is updated with the new occurrences number.
As the object can no be accessed inn the pre hook as it is yet to be found, you are going to have to use findOneAndUpdate method in the pre hook.
So the code should look like :
The find method :
Tag.find({ _id: "foo"}, function (err, foundTag) {
console.log(foundTag)
})
The pre hook :
TagSchema.pre('find', true, function (next, done) {
mongoose.models['Page'].count({ tags: this.getQuery()._id }, (err, total) => {
mongoose.models['Tag'].findOneAndUpdate({ _id: this.getQuery()._id }, { occurrences: total }, function () {
next()
done()
})
});
});

Synced Cron Expire on Simple Schema

I'm having some trouble setting up a cron job with percolate:synced-cron package to expire collection entries based on simple schema date and time fields. Is there another way to do this, or am I doing something wrong?
I'm receiving the following error below:
TypeError: Posts.find(...).toArray is not a function
Synced Cron Code
SyncedCron.start();
SyncedCron.add({
name: 'Expire Events',
schedule: function(parser) {
// parser is a later.parse object
return parser.text('every 15 minutes');
},
job: function() {
expireToday = Posts.find ({
date: new Date().toISOString().substring(0,10)
}).toArray();
console.log(expireToday);
for (i = 0; i < expireToday.length; i++) {
expireId = expireToday.eq(i)._id;
console.log(expireId);
if (expireToday.eq(i).time < new Date().toTimeString().substring(0,5)) {
Posts.deleteOne({_id : expireId});
}
}
}
});
Simple Schema Coffee Code
Schemas.Posts = new SimpleSchema
title:
type:String
max: 60
optional: true
content:
type: String
optional: true
autoform:
rows: 5
createdAt:
type: Date
autoValue: ->
if this.isInsert
new Date()
updatedAt:
type:Date
optional:true
autoValue: ->
if this.isUpdate
new Date()
time:
type: String
optional: false
autoform:
afFieldInput:
type: 'time'
date:
type: String
optional: false
autoform:
afFieldInput:
type: 'date'
owner:
type: String
regEx: SimpleSchema.RegEx.Id
autoValue: ->
if this.isInsert
Meteor.userId()
autoform:
options: ->
_.map Meteor.users.find().fetch(), (user)->
label: user.emails[0].address
value: user._id
Example mongo date and time
"date" : "2017-09-10"
"time" : "01:01"
The error message is telling you that this failed:
expireToday = Posts.find ({
date: new Date().toISOString().substring(0,10)
}).toArray();
It means that your Posts.find() didn't return anything that could be converted to an array.
It returns a cursor, maybe you meant to add a .fetch() to get an array of objects?
In either case you should check the return of a call like this to make sure it is returning what you expect - just basic defensive coding practice
See this related post
Going from dates to strings isn't great for searching in mongodb. If you want documents that expire today then you want to define today as going from midnight to midnight. You can also delete in bulk when running code on the server (SyncedCron jobs always run on the server).
SyncedCron.add({
name: 'Expire Events',
schedule: function(parser) {
return parser.text('every 15 minutes');
},
job: function() {
let todayStart = new Date();
todayStart.setHours(0,0,0,0);
let todayEnd = todayStart.setHours(23,59,59,999);
Posts.remove ({ date: {$gte: todayStart, $lte: todayEnd });
}
});
But this assumes you are storing datetimes in mongodb datetime fields, not as strings (which by the way, you should absolutely do, if nothing else but to gain timezone support).
If you want to use your schema with your date and time as strings then you can do:
SyncedCron.add({
name: 'Expire Events',
schedule: function(parser) {
return parser.text('every 15 minutes');
},
job: function() {
let today = new Date().toISOString().substring(0,10);
let now = new Date().toTimeString().substring(0,5);
Posts.remove ({ date: today, time: { $lte: now });
}
});

duplicate key error when creating a new mongoose sub-document

When creating new document and then try to upsert a new sub-document I get this error:
Object {error: "E11000 duplicate key error index: sales.users.$gro…p key:
{ : ObjectId('537b7788da19c4601d061d04') }"}
error: "E11000 duplicate key error index: sales.users.$groups.groupId_1
dup key: { : ObjectId('537b7788da19c4601d061d04') }"
__proto__: Object
The sub-document I'm trying to insert is defined as sub-schema that has a groupId field with the requirements {unique: true}, {sparse: true}. The mongoose method call I'm using to do the upsert is:
User.findByIdAndUpdate(userId,
{ $push: { 'groups': userUpdate} },
function (err, obj) where userUpdate = { groupId: groupId }.
After dropping the indexes the problem is fixed and this error no longer occurs.
var UserSchema = new Schema({
email: {
type: String,
required: true,
unique: true
},
active: {
type: Boolean,
default: true
},
username: {
type: String,
required: true,
unique: true
},
password: {
salt: {
type: String,
required: true
},
hash: {
type: String,
required: true
}
},
securityQuestion: {
question: String,
salt: String,
hash: String
},
mobile: {
PIN: Number,
Number: Number
},
createDate: {
type: Date,
default: Date.now
},
updateDate: Date,
lastLoginDate: Date,
prevLoginDate: Date,
passChangeDate: Date,
locked: Boolean,
lockDate: Date,
failedCount: Number,
failedDate: Date,
profile: profile,
preference: preference,
courses: [UserCourseSchema],
groups: [UserGroupSchema],
rewards: [UserRewardSchema],
roles: UserRoleSchema,
scores: [UserScoreSchema]
});
var UserGroupSchema = new Schema({
groupId: {
type: Schema.Types.ObjectId,
unique: true,
sparse: true
},
joinDate: {
type: Date,
default: Date.now
},
receiveNotifications: {
type: Boolean,
default: true
},
isAdmin: {
type: Boolean,
default: false
},
isOwner: {
type: Boolean,
default: false
},
isModerator: {
type: Boolean,
default: false
},
updateDate: Date
});
If you are applying upsert on array of object then this will always create new document as it do not compare sub documents of an array and you have unique index on groupId so it is not allowing you to create new record with the same value. For it you should find that record and if exists, then update it else create new record.
Another best way is to use $addToSet.
Hope this helps.
The {unique: true} requirement on the groupId field means that no two documents in the collection may contain the same groupId, rather than what you intended, enforcing uniqueness of the groupIds within the document. You can do what you want by using the MongoDB $addToSet operator instead.
If you are trying to update existing group from groups array, $push is not the solution.
User.findAndUpdate({_id:userId,'groups.groupId': userUpdate.groupId},
{ $set: {'groups.$': userUpdate}},
function (err, obj){})
otherwise as other suggested $addToSet will add the element into set if it exists.
User.findByIdAndUpdate(userId,
{ $addToSet : { 'groups': userUpdate} },
function (err, obj){})

Creating query(join) or correct the design?

I am having problem with building up some simple queries with this design. The problem is simple I want get the posts with included user information, which should be all in one array so i can pass it to the express view.
Simple and for me it seems the wrong solution would be after finding all the posts(db.post.find({email:'mo#gmail.com'}) and then looping through the posts and making one find query yo user collection and then merge the results.
Other solution would be using the DBref linking the author this may be better but i haven't found out how to make queries.
// User, the profile is not complete there will be more fields
var u = {
name: 'Michael', // Is not unique
email: 'mo#gmail.com', // Should be also unique
fbid: '4545454asdadsa'
}
db.user.insert(u);
// User can have 0 to many posts
var p = {
title: 'Suprise',
body: 'Well done',
tags: ['first', 'post', 'english'],
author: 'mo#gmail.com',
created: new Date(),
modified: new Date()
};
db.post.insert(p);
var p = {
title: 'Weather in Estonia',
body: 'Always looks bad',
tags: ['bad', 'weather', 'estonia'],
author: 'mo#gmail.com',
created: new Date(),
modified: new Date()
}
db.post.insert(p);
var p = {
title: 'Suprise',
body: 'Well done',
tags: ['first', 'post', 'english'],
author: {
name: 'Michael', // Is not unique
email: 'mo#gmail.com', // Should be also unique
fbid: '4545454asdadsa'
},
created: new Date(),
modified: new Date()
};
You embed documents, you denormalize. This is how it works. Now you can just get all the posts and dont have to query for the users because they are already there.
Data structure
I'll spell out how this can be done. I went completely overboard with the denormalization. This means you never need joins. Note that some of this can be removed if it's just cruft and never needed.
Posts collection
{
title: String,
body: String,
tags: [String],
author: {
name: String,
email: String,
fbid: String
},
created: Date,
modified: Date,
comments: [{
body: String,
created: Date,
modified: Date
}]
}
User collection
{
name: String,
email: String,
fbid: String,
posts: [{
title: String,
body: String,
tags: [String],
created: Date,
modified: Date,
comments: [{
body: String,
created: Date,
modified: Date
}]
}]
}
Comment collection
{
body: String,
created: Date,
modified: Date,
author: {
name: String,
email: String,
fbid: String
},
post: {
title: String,
body: String,
tags: [String],
created: Date,
modified: Date
comments: [{
body: String,
created: Date,
modified: Date
}]
}
}

Categories

Resources