Odata filter DateTimeOffset less that date - sql

I am trying to retrieve all records before specific date as follow:
?$filter=CreatedDate lt '2020-06-04T14:27:12.38'
but i keep receiving this error
"message": "The query specified in the URI is not valid. A binary operator with incompatible types was detected. Found operand types 'Edm.DateTimeOffset' and 'Edm.String' for operator kind 'GreaterThan'.",
I tried to cast date
?$filter=CreatedDate lt cast('2020-06-04T14:27:12.38', Edm.DateTimeOffset))
but still the same .
also tried
?$filter=CreatedDate lt datetime'2020-06-04T14:27:12.38'
and received
The query specified in the URI is not valid. Unrecognized 'Edm.String' literal 'datetime'1995-09-01T00:00:00'' at '21' in 'CreatedDate gt datetime'1995-09-01T00:00:00''.
is there anyway to achieve that ?

You have to query date without quotation marks. An example from my application:
http://localhost/EDS4OData/EmployeeHoursVacationRequests?$filter=(Username eq 'jsmith' and DeleteInd eq false and DayOffType eq 2 and StartDate ge 2020-10-01T00:00:00.000Z and StartDate le 2020-12-31T23:59:59.999Z)&$count=true
In my C# model for the controller:
public DateTime StartDate { get; set; }

A quick googling lead me to this answer,
so try
?$filter=CreatedDate lt datetime'2020-06-04T14:27:12.38'

I managed to get it working like this:
?$filter=CreatedDate lt 2021-03-19T12:50:54.219Z
Check this answer: https://stackoverflow.com/a/27775965/3264998

if you are using .NET sdk to generate Storage Table query try using the TableQuery.GenerateFilterConditionForDate version
var timestampToFilter1 = TableQuery.GenerateFilterConditionForDate("Timestamp", QueryComparisons.GreaterThanOrEqual,
DateTimeOffset.Parse("2022-06-15T04:32:07.000Z") );
var timestampToFilter2 = TableQuery.GenerateFilterConditionForDate("Timestamp", QueryComparisons.LessThanOrEqual,
DateTimeOffset.Parse("2022-06-15T12:32:07.000Z"));
var timestampToFilter = TableQuery.CombineFilters(timestampToFilter1, TableOperators.And, timestampToFilter2);

Related

Prisma queryRaw returning date as string

Issue: When I use prisma.$queryRaw, it returns my date as a string, even though I specify the query's return type. If I use prisma.find then it returns it correctly. But, I have to use queryRaw because of the complexity of the query.
schema.prisma has the date defined like such:
effectiveDate DateTime? #map("effective_date") #db.Date
So, the model object has the field defined like effectiveDate: Date | null
The query looks something like this:
const catalogCourses: CatalogCourse[] = await prisma.$queryRaw<CatalogCourse[]>`
SELECT
id,
campus,
effective_date as "effectiveDate",
...rest of the query ommitted here because it's not important
If I then do something like
console.log(`typeof date: ${typeof catalogCourses[0].effectiveDate}, value ${catalogCourses[0].effectiveDate}`)
The result shows typeof date: string, value 2000-12-31. Why isn't it a date? I need to be able to work with it as a Date, but if I do effectiveDate.getTime() for example, it errors during runtime, saying 'getTime is not a function', which it is doc. If I try and do new Date(effectiveDate), that doesn't work either because typescript sees the field as a Date object already. EDIT: I was incorrect about why the previous statement wasn't working; doing new Date(effectiveDate) does work.
I do see in the prisma docs that it says:
Type caveats when using raw SQL When you type the results of
$queryRaw, the raw data does not always match the suggested TypeScript
type.
Is there a way for queryRaw to return my date as a Date object?

How to filter SQL Server DateTime using .NET Core WebAPI with OData v4

I am running a simple .NET Core WebApi application with OData Query v4 and SQL Server 2012.
This works, but it's extremely slow:
GET /api-endpoint?$filter=date(MyDateTimeField) ge 2018-01-01&$top=100
SQL Query generated by the URL above:
SELECT TOP 100 * FROM MyTable WHERE ((((DATEPART(year, [MyDateTimeField]) * 10000) + (DATEPART(month, [MyDateTimeField]) * 100)) + DATEPART(day, [MyDateTimeField])) >= (((2018 * 10000) + (1 * 100)) + 1))
When I try to do this:
GET /api-endpoint?$filter=MyDateTimeField ge 2018-01-01T00:00:00.00Z&$top=100
It generates the following SQL query:
SELECT TOP 100 * FROM MyTable WHERE [MyDateTimeField] > '2018-01-01T00:00:00.0000000'
Which returns this error:
Conversion failed when converting date and/or time from character
string.
What would the OData Query syntax be to generate a SQL query similiar to this?
SELECT TOP 100 * FROM MyTable WHERE [MyDateTimeField] > '2018-01-01'
Assuming that the field of MyDateTimeField is datetime instead of datatime2, decorate the MyDateTimeField with a column annotation [Column(TypeName="datetime")] firstly :
public class MyTable
{
// ... other props
[Column(TypeName="datetime")]
public DateTime MyDateTimeField {get;set;}
}
To query with datetime, cast it into DateTimeOffset:
?$filter=MyDateTimeField ge cast(2018-01-01T00:00:00.00Z,Edm.DateTimeOffset)
The generated sql is something like :
SELECT ..., [$it].[MyDateTimeField],
FROM [MyTable] AS [$it]
WHERE [$it].[MyDateTimeField] >= '2018-01-01T08:00:00.000'
Note the datetime above is 2018-01-01T08:00:00.000 instead of 2018-01-01T00:00:00.0000000.
A screenshot of Demo:
After drowning incredulous in my own frustration I finally found a solution which would not force my api consumers to cast the DateTime string in an, ugly, verbose, disturbing expression.
I also want my model to transparently look like using DateTimeOffset instead of DateTime, this will allow me in the future to refactor the database and finally use DateTimeOffset even if so far I don't need to handle time zones.
My limitation is that I cannot update my legacy database and so here is the solution.
Is this solution for you?
This solution is useful only if:
You cannot (or want to) update your db column type (if you can you should and solve the problem)
You use or can change you entities to use DateTimeOffset instead of DateTime (If it is a new api consider doing this to comply with odata standard and allowing you to refactor in the future the underlay database)
You don't need to handle different time zones (this can be done but you need to work on the solution to do it)
You use EF Core >= 2.1
Solution
Update your entity using DateTimeOffset
public class MyEntity {
[...]
public DateTimeOffset CreatedDateTime { get; set; }
public DateTimeOffset ModifiedDateTime { get; set; }
[...]
}
Create a ValueConverter
public static class ValueConvertes
{
public static ValueConverter<DateTimeOffset, DateTime> DateTimeToDateTimeOffset =
new ValueConverter<DateTimeOffset, DateTime>(
model => model.DateTime,
store => DateTime.SpecifyKind(store, DateTimeKind.UTC));
}
Update your model mapping
public void Configure(EntityTypeBuilder<QuestionQML> builder)
{
builder.ToTable("MyEntityTable");
builder.Property(e => e.CreatedDateTime)
.HasColumnName("CreatedDateTime") // On DB datetime Type
.HasConversion(ValueConvertes.DateTimeToDateTimeOffset);
builder.Property(e => e.ModifiedDateTime)
.HasColumnName("ModifiedDateTime") // On DB datetime Type
.HasConversion(ValueConvertes.DateTimeToDateTimeOffset);
[...]
}
This allow you to filter in the following ways:
?$filter=CreatedDateTime gt 2010-01-25T02:13:40Z
?$filter=CreatedDateTime gt 2010-01-25T02:13:40.01234Z
?$filter=CreatedDateTime gt 2010-01-25
Special thanks to chris-clark
EDIT:
Corrected code DateTimeKind.UTC can be used when the datetime stored in the database is in UTC, if you store in a different timezone you need to set the Kind to the timezone you use, but this changes the way your datetimes will be shown in the results, showing for UK Timezone, for example, Z(GMT time) or +01:00 (BST time.

Handling Null DataType

I'm using the Over function from Piggybank to get the Lag of a row
res= foreach (group table by fieldA) {
Aord = order table by fieldB;
generate flatten(Stitch(Aord, Over(Aord.fieldB, 'lag'))) as (fieldA,fieldB,lag_fieldB) ;}
This works correctly and when I do a dump I get the expected result, the problem is when I want to use lag_fieldB for any comparison or transformation I get datatype issues.
If I do a describe it returns fieldA: long,fieldB: chararray,lag_fieldB: NULL
I'm new with PIG but I already tried casting to chararray and using ToString() and I keep getting errors like these:
ERROR 1052: Cannot cast bytearray to chararray
ERROR 1051: Cannot cast to bytearray
Thanks for your help
Ok after some looking around into the code of the Over function I found that you can instantiate the Over class to set the return type. What worked for me was:
DEFINE ChOver org.apache.pig.piggybank.evaluation.Over('chararray');
res= foreach (group table by fieldA) {
Aord = order table by fieldB;
generate flatten(Stitch(Aord, ChOver(Aord.fieldB, 'lag'))) as (fieldA,fieldB,lag_fieldB) ;}
Now the describe is telling me
fieldA: long,fieldB: chararray,lag_fieldB: chararray
And I'm able to use the columns as expected, hope this can save some time for someone else.

QueryDSL like operation SimplePath

Similarly to this question I would like to perform an SQL "like" operation using my own user defined type called "AccountNumber".
The QueryDSL Entity class the field which defines the column looks like this:
public final SimplePath<com.myorg.types.AccountNumber> accountNumber;
I have tried the following code to achieve a "like" operation in SQL but get an error when the types are compared before the query is run:
final Path path=QBusinessEvent.businessEvent.accountNumber;
final Expression<AccountNumber> constant = Expressions.constant(AccountNumber.valueOfWithWildcard(pRegion.toString()));
final BooleanExpression booleanOperation = Expressions.booleanOperation(Ops.STARTS_WITH, path, constant);
expressionBuilder.and(booleanOperation);
The error is:
org.springframework.dao.InvalidDataAccessApiUsageException: Parameter value [7!%%] did not match expected type [com.myorg.types.AccountNumber (n/a)]
Has anyone ever been able to achieve this using QueryDSL/JPA combination?
Did you try using a String constant instead?
Path<?> path = QBusinessEvent.businessEvent.accountNumber;
Expression<String> constant = Expressions.constant(pRegion.toString());
Predicate predicate = Expressions.predicate(Ops.STARTS_WITH, path, constant);
In the end, I was given a tip by my colleague to do the following:
if (pRegion != null) {
expressionBuilder.and(Expressions.booleanTemplate("{0} like concat({1}, '%')", qBusinessEvent.accountNumber, pRegion));
}
This seems to do the trick!
It seems like there is bug/ambiguity. In my case, I need to search by couple fields with different types (String, Number), e.g. SQL looks like:
SELECT * FROM table AS t WHERE t.name = "%some%" OR t.id = "%some%";
My code looks like:
BooleanBuilder where = _getDefaultPredicateBuilder();
BooleanBuilder whereLike = new BooleanBuilder();
for(String likeField: _likeFields){
whereLike = whereLike.or(_pathBuilder.getString(likeField).contains(likeValue));
}
where.and(whereLike);
If first _likeFields is type of String - request works fine, otherwise it throws Exception.

Spring Hibernate SQL Query

I have a VO class which has the getter and setter of another VO class too. For example:
Class DocumentVO{
PrintJobVO job;
PrintRunVO run;
String id;
getters and setters..
}
Now I have a requirement to use the Native SQL Query using spring hibernate. When I want to map the ids I have a problem. My query is,
select {r.*},{d.*}
from runs {r}, documents {d}
where {r}.RUN_ID as {r.id} = d.RUN_ID as {d.run.id}
Here run is of type PrintRunVO which has its id and other values. How can I map them in my SQL? I am getting an error like invalid user.table.column, table.column, or column specification.
What's the way to overcome this?
Use the Result Transformer concept in your plain SQL query.
String query = "myquery";
SQLQuery q = session.createSQLQuery(query);
q.addScalar("param1", Hibernate.STRING);
q.addScalar("param2", Hibernate.STRING);
q.setResultTransformer(Transformers.aliasToBean(MyVO.class));
q.setParameter("queryParam1", "some value");
return q.list();