how to user year() and month() functions in NH Criteria API? - nhibernate

I need to use year() and month() functions in Criteria API to be able to express a business filter constrain. Expressions like
cri.Add(Expression.Ge("year(Duration.DateFrom)", Year.Value));
cri.Add(Expression.Le("year(Duration.DateTo)", Year.Value));
obviously do not work - is there any solution how to achieve this?
I know it's entirely possible in HQL, but I need to construct the query using criteria API because there're some additional processes processing the query adding sorting, paging etc..
sample HQL solution which I'd like to rewrite to Criteria API:
var ym = year * 100 + month;
var hql = ...(:ym between 100 * year(f.Duration.DateFrom) + month(f.Duration.DateFrom) and 100 * year(f.Duration.DateTo) + month(f.Duration.DateTo)";

It's possible to achieve this using Projections.SQLFunction. Working solution:
ISQLFunction sqlAdd = new VarArgsSQLFunction("(", "+", ")");
ISQLFunction sqlMultiply = new VarArgsSQLFunction("(", "*", ")");
var ym = Year.Value * 100 + Month.Value;
var dateFromMonthProj = Projections.SqlFunction("month", NHibernateUtil.Int32, Projections.Property("PurchaseDuration.DateFrom"));
var dateFromYearProj = Projections.SqlFunction("year", NHibernateUtil.Int32, Projections.Property("PurchaseDuration.DateFrom"));
var dateToMonthProj = Projections.SqlFunction("month", NHibernateUtil.Int32, Projections.Property("PurchaseDuration.DateTo"));
var dateToYearProj = Projections.SqlFunction("year", NHibernateUtil.Int32, Projections.Property("PurchaseDuration.DateTo"));
var calculatedYMFrom = Projections.SqlFunction(sqlAdd, NHibernateUtil.Int32, Projections.SqlFunction(sqlMultiply, NHibernateUtil.Int32, dateFromYearProj, Projections.Constant(100)), dateFromMonthProj);
var calculatedYMTo = Projections.SqlFunction(sqlAdd, NHibernateUtil.Int32, Projections.SqlFunction(sqlMultiply, NHibernateUtil.Int32, dateToYearProj, Projections.Constant(100)), dateToMonthProj);
cri.Add(Restrictions.Le(calculatedYMFrom, ym));
cri.Add(Restrictions.Ge(calculatedYMTo, ym));

Would something like this work for you?
cri.Add(Expression.Ge("Duration.DateFrom", new Date(fromYear, 1, 1));
cri.Add(Expression.Le("Duration.DateTo", new Date(toYear, 12, 31));
Note that I changed your expression order -- I'm assuming you made a typo and you want to query for dates between DateFrom and DateTo. If the dates contain time data, the second expression would change to:
cri.Add(Expression.Lt("Duration.DateTo", new Date(toYear + 1, 1, 1));
In response to comment:
cri.Add(Expression.Ge("Duration.DateFrom", new Date(fromYear, fromMonth, 1));
// Actual code needs to get last day of to month since it will not always be 31
cri.Add(Expression.Le("Duration.DateTo", new Date(toYear, toMonth, 31));
Is your user input in the form "YYMM"? If that's the case, then you just have to parse out year and month from that string to create fromYear, fromMonth, etc.
Edit: my 3rd and final attempt:
// First parse the input, e.g: september 2009 into 9 (inMonth) and 2009 (inYear)
var fromDate = new DateTime(inYear, inMonth, 1);
var toDate = fromDate.AddMonths(1).AddDays(-1);
cri.Add(Expression.Ge("Duration.DateFrom", fromDate));
cri.Add(Expression.Le("Duration.DateTo", toDate));

I'm not sure I understod what you mean with your question but I had a similar question, and I solved the problem with:
crit.Add(Expression.Sql("(YEAR({alias}.ObsDatum) = ?)", year, NHibernateUtil.String))
crit.Add(Expression.Sql("(MONTH({alias}.ObsDatum) = ?)", manad, NHibernateUtil.Int32))

Related

Javascript Age Comparison

basically I am setting up a age verification script for a alcohol company and each country of course has a specific age to consume or buy alcohol.
I have set up the basics just now but I am running in to a problem. Right now I want to be able to gather there DOB using the input fields. Convert this to a date and them somehow compare it to the legal drinking age of the country in question.
Unfortunately testing the DOB is proving difficult and I know it is a problem with the event handler I am using as it won't output my required result to the console.
If anyone could help that would be fantastic.
Here is a link to my fiddle.
var countries = {
"Albania": 18,
"Algeria": 18,
"Argentina": 21,
"Armeria": 18,
"Australia": 18,
"Austria": 18
};
var individualCountry;
var day;
var month;
var year;
$("#verify").submit(function() {
day = $("#day").val();
month = $("#month").val();
year = $("#year").val();
var fullDate = day + month + year;
console.log(fullDate);
individualCountry = $("#countries").val();
var ageLimit = countries[individualCountry];
if (personsAge >= ageLimit) {
}
})
https://jsfiddle.net/h989qrLs/
try this code
function_calculateAge(birthday) { // birthday is a date
var ageDifMs = Date.now() - birthday.getTime();
var ageDate = new Date(ageDifMs); // miliseconds from epoch
return Math.abs(ageDate.getUTCFullYear() - 1970);
}
for more details open this link by André Snede Hansen

NHibernate Linq Query with Projection and Count error

I have the following query:
var list = repositoy.Query<MyClass>.Select(domain => new MyDto()
{
Id = domain.Id,
StringComma = string.Join(",", domain.MyList.Select(y => y.Name))
});
That works great:
list.ToList();
But if I try to get the Count I got an exception:
list.Count();
Exception
NHibernate.Hql.Ast.ANTLR.QuerySyntaxException
A recognition error occurred. [.Count[MyDto](.Select[MyClass,MyDto](NHibernate.Linq.NhQueryable`1[MyClass], Quote((domain, ) => (new MyDto()domain.Iddomain.Name.Join(p1, .Select[MyListClass,System.String](domain.MyList, (y, ) => (y.Name), ), ))), ), )]
Any idea how to fix that without using ToList ?
The point is, that we should NOT call Count() over projection. So this will work
var query = repositoy.Query<MyClass>;
var list = query.Select(domain => new MyDto()
{
Id = domain.Id,
StringComma = string.Join(",", domain.MyList.Select(y => y.Name))
});
var count = query.Count();
When we use ICriteria query, the proper syntax would be
var criteria = ... // criteria, with WHERE, SELECT, ORDER BY...
// HERE cleaned up, just to contain WHERE clause
var totalCountCriteria = CriteriaTransformer.TransformToRowCount(criteria);
So, for Count - use the most simple query, i.e. containing the same JOINs and WHERE part
If you really don't need the results, but only the count, then you shouldn't even bother writing the .Select() clause. Radim's answer as posted is a good way to both get the results and the count, but if your database supports it, use future queries to execute both in the same roundtrip to the database:
var query = repository.Query<MyClass>;
var list = query.Select(domain => new MyDto()
{
Id = domain.Id,
StringComma = string.Join(",", domain.MyList.Select(y => y.Name))
}).ToFuture();
var countFuture = query.Count().ToFutureValue();
int actualCount = countFuture.Value; //queries are actually executed here
Note that there in NH prior to 3.3.3, this would still execute two round-trips (see https://nhibernate.jira.com/browse/NH-3184), but it would work, and if you ever upgrade NH, you get a (minor) performance boost.

Write Round in NHibernate criteria

I need to write this in NHibernate Criteria as a projection:
The subAlias is _not_ the root alias, so {alias} cannot replace the correct sql alias, and my problem is that other parts of the query makes the subAlias vary in the generated sql
ROUND(alias.Property / parameterValueFromMethodParameter + ", 0)
* parameterValueFromMethodParameter2 AS SQLAlias
This is how far (off) I got:
.Add(Projections.SqlFunction(new VarArgsSQLFunction("(", "/", ")")
, NHibernateUtil.Int32
, Projections.SqlFunction("round"
, NHibernateUtil.Decimal
, Projections.Property("subAlias.Property"))), "SQLAlias"))
This produces the following SQL code:
ROUND( subAlias3(4).Property
)AS y1_
Does anyone have experience with projections like this?
I found this patch i hibernate, but seems like it was not implemented.
If I understand your example properly, the most easy solution would be to use SQL projection:
// the parameterValueFromMethodParameter
// and parameterValueFromMethodParameter2
var computationParams = new object[] {2, 4}; // just an example numbers
// SQL To be generated
// see that here we work with COLUMN name, not property
var sqlSnippet = " ( ROUND({{alias}}.ColumnName / {0}, 0) * {1} ) AS computed ";
// put that all together
var projectSql = string.Format(sqlSnippet, computationParams);
// IProjection
var projection = Projections.SqlProjection(projectSql, new string[0], new IType[0]);
// add it to SELECT clause
criteria.SetProjection(Projections.ProjectionList()
.Add(projection)
...
);
That should work...
I Solved it by writing my own SQL IProjection. With a litte help from this example.
public SqlString ToSqlString(ICriteria criteria, int loc, ICriteriaQuery criteriaQuery, IDictionary<string, IFilter> enabledFilters)
{
string replacedString = Regex.Replace(
this.sql,
#"{([a-zA-Z_]((\.)?[a-zA-Z0-9_])*)}",
m =>
{
ICriteria critter = criteria.GetCriteriaByAlias(m.Groups[1].Value);
if (critter != null)
{
return criteriaQuery.GetSQLAlias(critter);
}
return m.Groups[0].Value;
});
return new SqlString(replacedString);
}
So now I can do (In a SqlGroupProjection):
ROUND({subAlias}.XCoord / " + aggregationSize + ", 0) * " + aggregationSize + " AS SQLAlias
There are some other attempts on extending (N)Hibernate to handle this alias in raw SQL problem:
Expression.Sql should support aliases other than {alias}
Support for referencing non-root entities in Criteria SQL expressions

Breeze - Writing complex query with multiple parameters from client

I am trying to write a Breeze query for the Northwind database to show orders that contain all three products that a user specifies. So if a user selects ProductID 41, 51, and 65 from drop downs, the query would return order id 10250.
This is just a sample scenario that I am looking to base another query on in a project I am working on, but I thought using Northwind to explain it would be easier than describing the project. I can easily do it in T-SQL using derived tables, but I need to get the parameters from the client. Any thoughts? Thanks in advance!
If you're still interested in doing this on the client, you can try the following Breeze query.
var listOfProductIds = [41, 51, 65];
var preds = listOfProductIds.map(function (id) {
return Predicate.create("OrderDetails", "any", "ProductID", "==", id);
});
var whereClause = Predicate.and(preds);
var query = EntityQuery.from('Orders').where(whereClause);
This will retrieve all Orders that have at least all 3 of the Products specified.
To further filter this so you have all Orders that have only all 3 of the Products specified, you can write,
entityManager.executeQuery(query)
.then(filterOrders);
//once you get results on the client
function filterOrders(data) {
var allOrders = data.results;
var filteredOrders = allOrders.filter(function (o) {
return o.OrderDetails.length == listOfProductIds.length;
});
}
You can only filter on the client since OData doesn't yet support the Aggregate Count function like Linq to Entities does. This is probably not ideal but it's an option if you decide not to do it on the server.
Sorry in my comment I said look at the and() method but in actuality for your use you needed to look at the or() method -
var p1 = breeze.Predicate("Product.ProductID", "==", 41);
var p2 = breeze.Predicate("Product.ProductID", "==", 51);
var p3 = breeze.Predicate("Product.ProductID", "==", 65);
var newPred = breeze.Predicate.or(p1, p2, p3);
var query = EntityQuery.from('Order_Details').select('Order.OrderID').where(newPred);
The only issue with these queries on the client is that depending on how you are building them client-side and how many predicates you are adding they can get very long in some situations, such as select all 200 records except 1 id type queries which can bite you on IE8.
You can use the EntityQuery.withParameters method to call a server side query that has your linq query. i.e. something like this.
// On the client
var query = EntityQuery.from("GetOrdersWithProductIds")
.withParameters({ productIds: [41, 51, 65] });
// On the server
[HttpGet]
public IQueryable<Order> GetOrdersWithProductIds([FromUri] int[] productIds) {
return ContextProvider.Context.Orders.Where(... your linq query here ...);
}
or you might try using the support for 'any'/'all' query operators. i.e. something like this.
var predicate = breeze.Predicate("ProductID", "==", 41)
.or("ProductID", "==", 51)
.or("ProductID", "==", 65);
var query = EntityQuery.from("Orders")
.where("OrderDetails", "all", predicate)

SQL Syntax for date range in a multiple search query

I have this code written so far and it works for what I am doing but if I search for June 13 it will only look up until June 12, can someone help me figure whats wrong in my code? or where I can add a day interval? I tried and its just not working for me.
var db = Database.Open("RMS") ;
var selectCommand = "SELECT * FROM RMS";
var formSSO = "";
var fromDate= "";
var toDate= "";
formSSO = Request.QueryString["formSSO"];
fromDate = Request.QueryString["fromDate"];
toDate = Request.QueryString["toDate"];
selectCommand = "(SELECT * from RMS WHERE SSO LIKE #0)";
if(!Request.QueryString["fromDate"].IsEmpty() ) {
selectCommand = "SELECT * FROM RMS WHERE SSO LIKE #0 AND Created BETWEEN #1 AND #2";
}
if(Request.QueryString["formSSO"].IsEmpty() ) {
<div class="simple"><strong>*SSO ID is Required.</strong></div>
}
var data = db.Query(selectCommand, formSSO, fromDate, toDate);
var columns = new[]{"ID", "SSO", "Category", "System", "Subject", "Created"};
var grid = new WebGrid(data, ajaxUpdateContainerId: "grid", defaultSort: "ID", columnNames: columns);
if (Request.QueryString[grid.SortDirectionFieldName].IsEmpty()) {
grid.SortDirection = SortDirection.Descending;
}
}
One thing you can try is using <= and >= instead of BETWEEN like this:
selectCommand = "SELECT * FROM RMS WHERE SSO LIKE #0 AND Created >= #1 AND Created <= #2";
I hope that does the trick!
If not you can also brute force the to date to be one day further into the future and then use the BETWEEN operator just you are now like this:
DateTime to_date = Convert.ToDateTime(to_date_string);
to_date = to_date.AddDays(1);
to_date_string = to_date.ToString();