Sequelize/tedious parsing datetime wrong to where clause - node.js

I am getting no results in sql query because the filter in where clause was changed by sequelize/tedious before query is executed.
const query = `SELECT field FROM Table WITH(NOLOCK) WHERE CreationDate BETWEEN '${startDate}' AND '${endDate}'`
return sequelize.rawQuery(query)
.then((resultado) => {
return resultado;
})
.catch((error) => {
console.log('error')
});
This is the query generated by sequelize/tedious
SELECT [Field]
FROM [Table] AS [Table] WHERE
([Table].[CreationDate] > N'2018-02-01 16:36:00.000 -02:00' AND [Table].[CreationDate] < N'2018-02-01 16:46:00.000 -02:00')
Can you help me how to solve this?
CreationDate is stored as YYYY-MM-DD HH:MM:SS

I have also been banging my head on this for the last week.
It should have been solved here and has been discussed here and here. But I don't think it really has been solved.
I know this isn't the correct way, but you could change the schema from this:
CreationDate: {
type: DataTypes.DATE,
allowNull: true
},
to
CreationDate: {
type: DataTypes.INTEGER,
allowNull: true
},
and then convert your startDate and endDate to integers like:
Date.parse(startDate) / (1000 * 60 * 60 * 24)
I really hope someone can help, because the Tedious documentation is kind of hard to understand.
This solved my problem for now, until I fix it. Only ever had this problem with mssql.

Related

Date Format in mongoose, mongodb and node application

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
}

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']
}

sequelizejs update doesn't ignore null values

code to update :
return db.models.sprout.update({
first_name: args.input.patch.first_name,
last_name: args.input.patch.last_name,
date_of_birth: args.input.patch.date_of_birth,
image_url: args.input.patch.image_url,
updated_by: args.input.user_id
},{ where: { sprout_id : args.input.sprout_id }}).then((rowsUpdated) => {
none of the fields have not null constraint in table.
this should ideally update only, values which are provided, in the args.input.patch i provided only image_url
i get this error.
notNull Violation: sprout.first_name cannot be null,\nnotNull Violation: sprout.last_name cannot be null
weird, the insert works the way i want, only inserts whichever is present
return db.models.sprout.create({
sprout_id: uuidv4(),
first_name: args.input.sprout.first_name,
last_name: args.input.sprout.last_name,
date_of_birth: args.input.sprout.date_of_birth,
image_url: args.input.sprout.image_url,
created_by: args.input.user_id
})
if i dont give image_url or any other field , insert works fine, and ignore the null, but the update doesn't .
How can i make update to ignore the null values.
There is such thing in sequelize: omitNull, you should set it to true. Please check it in the docs: http://docs.sequelizejs.com/class/lib/model.js~Model.html
However here: https://github.com/sequelize/sequelize/issues/2352 engineer from their team said that "omitNull was a dirty hack, there are better ways to solve it now." and he didn't recommend the right approach.
Personally, I recommend you to clear null fields from your object before passing it to sequelize. Please look at this thread: Remove blank attributes from an Object in Javascript
I suggest only passing the fields you actually want to update. This will make the code a lot more readable.
Pass the fields into a function like below. This function will return a new object only containing the fields that are not null
const getFieldsToUpdate = (fields) => {
return {
...fields.first_name && { first_name: fields.first_name },
...fields.last_name && { last_name: fields.last_name },
...fields.date_of_birth && { date_of_birth: fields.date_of_birth },
...fields.image_url && { image_url: fields.image_url },
...fields.user_id && { updated_by: fields.user_id },
}
};
const fieldsToUpdate = getFieldsToUpdate(args.input.patch);
return db.models.sprout.update(fieldsToUpdate, {
where: { sprout_id : args.input.sprout_id }
})
.then((rowsUpdated) => {...
The error is seems to be due to the parameter allownull which is set as true...
good point ashish. WoW, very good understanding, it was because of the allownull being set to false on certain fields. i tried after removing it, and it worked as expected. Both ashish and Michael solution worked. I prefer Ashish solution, since it is more native, and filtering is done by sequelize as opposed to custom code (thanks Michael for the code) , i saw in sequilze code, they do similar thing

Sequelize is returning integer as string

I using nodejs v4 with sequelize, and I have a model like this:
var Device = sequelize.define('Device', {
id: {
type: DataTypes.BIGINT,
primaryKey: true,
autoIncrement: true
},
tenantId: {
type: DataTypes.BIGINT,
allowNull: false
},
token: {
type: DataTypes.STRING,
allowNull: false
}
}, {
tableName: 'devices'
});
When I select a device by id the type of id is a string, exemple:
Device.findById(9).then( function(result) {
console.log(result.toJSON().id + 10);
});
The output will be 910, rather than 19, so I look at json and a saw this:
{
id: "9"
tenantId: "123"
token: "adsadsdsa"
}
The id in found device is a string, but I defined it as a number...
Doesn't it should be { "id": 9 } ?
How can I select a device with the types that I defined previously?
BIGINT maximum value is 2^63-1, javascript can safely represent up to 2^53. To be on the safe side libraries return those numbers as strings.
If you want to have numbers instead of strings, you can use this library https://github.com/mirek/node-pg-safe-numbers which deals with this issue.
I found a fix to this problem on sequelize repo.
https://github.com/sequelize/sequelize/issues/4523
The pg module used for sequelize returns bigint as string because bigints are not guaranteed to fit into js numbers. So I change my model to use integer (DataTypes.INTEGER)
IMO, a smoother solution is to force parsing of int to int, instead of strings. Checkout this issue and comments.
Trying to put this line before your logic code worked for me, this forces parsing of int8 to int, instead of strings:
require("pg").defaults.parseInt8 = true;
...
try {
const list = await Posts.findAll();
console.log(JSON.stringify(list));
} catch (e) {
console.log(e.message);
}
By default, sequelize try format your results. You can set raw :true for get raw data

Multiple near with mongoose

I have the following "query":
MySchema
.where('address1.loc').near({
center: {
type: 'Point',
coordinates: user.address1.loc
},
maxDistance: 10 * 1000
}).where('address2.loc').near({
center: {
type: 'Point',
coordinates: user.address2.loc
},
maxDistance: 10 * 1000
})
.exec(function(err, objects) {
console.log(err);
console.log(objects);
if(err) return eachCallback(err);
return eachCallback();
});
My schema has two addresses (one pickup- and one handover-address). So I have to use two "nears". But this doesn't seem to be possible:
{ [MongoError: Can't canonicalize query: BadValue Too many geoNear expressions] name: 'MongoError' }
What are my alternatives?
Update:
I talked to some guys from MongoDB. Although my use case for this is valid, this doesn't seem to be possible out-of-the-box. So I'm looking for a "workaround". If you need details about the use case, let me know.
And here is how I defined the location inside my "addresses":
...
loc: {type: [Number], index: '2dsphere'},
...
I had encountered the same issue and error message. But I cannot remember exactly how I could overcome. I hope we can overcome your issue!
If your schema definition like the following, please try to the below solution;
loc : {
'type': { type: String, default: 'Point' },
coordinates: [Number]
}
Could you check address.loc.coordinates stores as number on your collection?
Alternate way
Try to use $geoWithin operator
I think you can change your questioning clauses like the following. You should use $geoWithin and $centerSphere instead of $near and $maxDistance. You've tried to find records that have loc fields in 10.000 meters proximity. $centerSphere indicates radius, it uses legacy coordinate values (only [longitude, latitude]) and it can used with geoJSON field and 2dsphere index. And your $centerSphere definition must be 10/6371 for 10 km. (tutorial).
If you use a second operator with $near operator it occurs a problem on MongoDB side.
You cannot combine the $near operator, which requires a special geospatial index, with a query operator or command that uses a different type of special index. For example you cannot combine $near with the $text query.
var r = 10/6371;
var area1 = { center: address1.loc.coordinates, radius: r, unique: true }
var area2 = { center: address2.loc.coordinates, radius: r, unique: true }
MySchema
.where('address1.loc').
.within()
.circle(area1)
.where('address2.loc').
.within()
.circle(area2)
.exec(function(err, objects) {
console.log(err);
console.log(objects);
if(err) return eachCallback(err);
return eachCallback();
});
Hope this helps..

Resources