Multiple Subqueries With QueryOver - nhibernate

I need help converting this sql query into QueryOver Nhibernate criteria.
select distinct * from event e where e.name like '%req%'
or e.Id in (select r.eventId from requirement r where r.name like '%req%')
or e.Id in (select r.eventId from requirement r where r.id
in (select s.requirementId from solution s where s.name like '%sol%'))
var queryOver = session.QueryOver<Event>()
.Where(x => x.Name.IsInsensitiveLike("%"+searchTerms[1]+"%"))
.OrderBy(x => x.CreatedOn).Asc;
So far I have the main query but couldn't find enough reference material on how to add the subqueries. Haven't been successful using joinQueryOver.
Event has one-to-many rel with requirement and requirement has one-to-many rel with solution.
Requirement reqAlias = null;
Solution solAlias = null;
var subQuery = QueryOver.Of<Event>()
.JoinAlias(x => x.Requirements, () => reqAlias)
.Where(x => x.Name.IsInsensitiveLike(searchTerms[2]))
.JoinAlias(() => reqAlias.Solutions, () => solAlias)
.Where(x => x.Name.IsInsensitiveLike(searchTerms[3]))
.Select(Projections.Group<Event>(x => x.Id));
var events = session.QueryOver<Event>()
.Where(x => x.Name.IsInsensitiveLike(searchTerms[1]))
.WithSubquery.WhereProperty(x => x.Id).In(subQuery)
.List().ToList();
still not working.

When you use IsInsensitiveLike NHibernate appends the % after parsing, and uses lower to do a lower case comparison. In your code, you are appending the % yourself, which results in,
select distinct * from event e where e.name like %lower('%req%')%
which in turn, doesn't work.
Also, you have 3 subqueries, no a big one, so you need to restructure your code to account for that:
select r.eventId from requirement r where r.name like '%req%'
to
var firstQuery = QueryOver.Of<Requirement>()
.Where(r => r.Name.IsInsensitiveLike(searchTerms[2]))
.Select(r => r.EventId);
then,
select s.requirementId from solution s where s.name like '%sol%'
to
var solutionQuery = QueryOver.Of<Solution>()
.Where(s => s.Name.IsInsensitiveLike(searchTerms[3]));
then,
select r.eventId from requirement r where r.id
in (select s.requirementId from solution s where s.name like '%sol%')
to
var requirementQuery = QueryOver.Of<Requirement>()
.WithSubquery
.WhereProperty(r => r.Id).In(solutionQuery)
.Select(r => r.EventId);
Then you need to construct the main query using Restrictions.Or to include the 3 queries.

Related

Nhibernate query<T> / queryover<T> orderby a subquery

I am having issues getting Nhibernate 3.3.2.4000 to generate the correct subquery used in the orderby clause as displayed below:
select *
from dbo.Person p inner join dbo.Task t on p.Task_FK = p.TaskId
order by (select p.CustomerNumber where p.IsMain=1) desc
We have two entities: Task and Person
One task can have N persons related to it. I.e Task has an IList property.
How can I make Nhibernate generate the correct subquery ? I have gotten as far as something like this with the Query API:
query = query.OrderBy(x => x.Persons.Single(t => t.CustomerNumber));
but I am unsure how I can correctly generate the where clause as displayed in the original sql query. Is this perhaps easier done using the queryover api somehow?
Any advice or guidance is most welcome.
Task task = null
Person person = null;
var subquery = QueryOver.Of<Task>()
.Where(t => t.Id == task.Id)
.JoinQueryOver(t => t.Persons, () => person)
.Where(p => p.IsMain)
.Select(() => person.CustomerNumber);
var query = session.QueryOver(() => task)
.OrderBy(Projections.SubQuery(subquery))
.FetchMany(x => x.Persons)
return query.List();

Duplicated and unnecessary joins when using Linq in NHibernate

Basically I crossed the same problem of Linq provider in this linq-to-nhibernate-produces-unnecessary-joins
List<Competitions> dtoCompetitions;
dtoCompetitions = (from compset in session.Query<FWBCompetitionSet>()
where compset.HeadLine == true
&& compset.A.B.CurrentSeason == true
select (new Competitions
{
CompetitionSetID = compset.CompetitionSetID,
Name = compset.Name,
Description = compset.Description,
Area = compset.Area,
Type = compset.Type,
CurrentSeason = compset.A.B.CurrentSeason,
StartDate = compset.StartDate
}
)).ToList();
Which leads to duplicated join in its generated SQL
SELECT fwbcompeti0_.competitionsetid AS col_0_0_,
fwbcompeti0_.name AS col_1_0_,
fwbcompeti0_.DESCRIPTION AS col_2_0_,
fwbcompeti0_.area AS col_3_0_,
fwbcompeti0_.TYPE AS col_4_0_,
fwbseason3_.currentseason AS col_5_0_,
fwbcompeti0_.startdate AS col_6_0_
FROM fwbcompetitionset fwbcompeti0_
INNER JOIN A fwbcompeti1_
ON fwbcompeti0_.competitionseasonid = fwbcompeti1_.competitionseasonid
INNER JOIN A fwbcompeti2_
ON fwbcompeti0_.competitionseasonid = fwbcompeti2_.competitionseasonid
INNER JOIN B fwbseason3_
ON fwbcompeti2_.seasonid = fwbseason3_.seasonid
WHERE fwbcompeti0_.headline = #p0
AND fwbseason3_.currentseason = #p1
Notice these joins, which are totally duplicated and also affect my SQL Server's performence.
INNER JOIN A fwbcompeti1_
ON fwbcompeti0_.competitionseasonid = fwbcompeti1_.competitionseasonid
INNER JOIN A fwbcompeti2_
ON fwbcompeti0_.competitionseasonid = fwbcompeti2_.competitionseasonid
Update1
In the NHibernate 3.2, this LiNQ bug is still valid, and I could not find a simple and reasonable Linq solution.
So I used QueryOver + JoinAlias + TransformUsing finishing the job, workds perfect to me.
FWBCompetitionSet compset = null;
FWBCompetitionSeason compseason = null;
FWBSeason season = null;
IList<Competitions> dtoCompetitions;
dtoCompetitions = session.QueryOver<FWBCompetitionSet>(() => compset)
.JoinAlias(() => compset.FWBCompetitionSeason, () => compseason)
.JoinAlias(() => compseason.FWBSeason, () => season)
.Where(() => compset.HeadLine == true)
.And(() => season.CurrentSeason == true)
.SelectList(
list => list
.Select(c => c.CompetitionSetID).WithAlias(() => compset.CompetitionSetID)
.Select(c => c.Name).WithAlias(() => compset.Name)
.Select(c => c.Description).WithAlias(() => compset.Description)
.Select(c => c.Area).WithAlias(() => compset.Area)
.Select(c => c.Type).WithAlias(() => compset.Type)
.Select(c => season.CurrentSeason).WithAlias(() => season.CurrentSeason)
.Select(c => c.StartDate).WithAlias(() => compset.StartDate)
)
.TransformUsing(Transformers.AliasToBean<Competitions>())
.List<Competitions>();
Yet Another Edit:
I think I finally found out what's going on. It seems that the LINQ to NHibernate provider has trouble navigating associations from the target to the source table and generates a separate join each time it encounters such an association.
Since you don't provide your mapping, I used the mapping from linq-to-nhibernate-produces-unnecessary-joins. This model has a Document with one Job and many TranslationUnits. Each TranslationUnit has many Translation entities.
When you try to find a Translation based on a Job, you are traversing the associations in the reverse order and the LINQ provider generates multiple joins: one for Translation -> TranslationUnit and one for TranslationUnit to Document.
This query will generate redundant joins:
session.Query<TmTranslation>()
.Where(x => x.TranslationUnit.Document.Job == job)
.OrderBy(x => x.Id)
.ToList();
If you reverse the navigation order to Document -> TranslationUnit -> Translation, you get a query that doesn't produce any redundant joins:
var items=(from doc in session.Query<Document>()
from tu in doc.TranslationUnits
from translation in tu.Translations
where doc.Job ==job
orderby translation.Id
select translation).ToList();
Given this quirkiness, QueryOver seems like a better option.
Previous Edit:
I suspect the culprit is compset.A.B.CurrentSeason. The first joined table (fwbcompeti1_) returns A.B while the next two (fwbcompeti2_ and fwbseason3_) are used to return A.B. The LINQ to NHibernate provider doesn't seem to guess that A is not used anywhere else and fails to remove it from the generated statement.
Try to help the optimizer a little by replacing CurrentSeason = compset.A.B.CurrentSeason with CurrentSeason = true from the select, since your where statement returns only items with CurrentSeason == true.
EDIT: What I mean is to change the query like this:
List<Competitions> dtoCompetitions;
dtoCompetitions = (from compset in session.Query<FWBCompetitionSet>()
where compset.HeadLine == true
&& compset.A.B.CurrentSeason == true
select (new Competitions
{
CompetitionSetID = compset.CompetitionSetID,
Name = compset.Name,
Description = compset.Description,
Area = compset.Area,
Type = compset.Type,
CurrentSeason = true,
StartDate = compset.StartDate
}
)).ToList();
I simply replace the value compset.A.B.CurrentSeason with true

Filtered join with NHibernate QueryOver

Using the Criteria API, I can generate a query that creates a JOIN with an extra condition on the JOIN
var criteria = Session.CreateCriteria<Product>()
.SetReadOnly(true)
.SetMaxResults(1)
.CreateAlias("ProductCategory", "U", JoinType.LeftOuterJoin, Expression.Eq("U.SubType", "Premium"))
.AddOrder(Order.Desc("U.Sequence"));
This generates a JOIN similar to this:
SELECT * FROM dbo.Product w
LEFT JOIN dbo.ProductCategory u
ON u.DefaultProductId = w.Id AND u.SubType = 'Premium'
How do I do the same thing with the QueryOver syntax?
Off the top of my head I think it's like:
ProductCategory category = null;
var result = Session.QueryOver<Product>()
.JoinAlias(x => x.Categories, () => category, JoinType.LeftOuterJoin)
.Where(() => category.SubType == "Premium")
.OrderBy(() => category.Sequence).Desc
.Take(1)
.List();
Edit: Included OrderBy, and gave it a test. Works.
Using the Blog > Posts type example, the generated SQL looks like:
SELECT this_.Id as Id1_1_,
this_.Title as Title1_1_,
post1_.BlogId as BlogId3_,
post1_.Id as Id3_,
post1_.Id as Id3_0_,
post1_.Title as Title3_0_,
post1_.Content as Content3_0_,
post1_.DatePosted as DatePosted3_0_,
post1_.BlogId as BlogId3_0_
FROM [Blog] this_
left outer join [Post] post1_
on this_.Id = post1_.BlogId
WHERE post1_.DatePosted > '2011-11-22T19:43:11.00' /* #p0 */
ORDER BY post1_.DatePosted desc
.JoinAlias has an overload with a withClause
var result = Session.QueryOver<Product>()
.Left.JoinAlias(x => x.Categories, () => category, c => c.SubType == "Premium")
.OrderBy(() => category.Sequence).Desc
.Take(1)
.List();

Using nHibernate and QueryOver how can I join 3 tables

BACKGROUND:
Given 3 tables
results contains 2 columns vId and pId
vTable contains 2 columns vId and data
pTable contains 2 columns pId and data
I want to accomplish this sort of SQL query using QueryOver
SELECT v.data, p.data
from results r
inner join vTable v on r.vId = v.vId
inner join pTable p on r.pId = p.pId
I've tried the following:
var res = GetResults(some parameters)
.Select(x => x.vId
.Select(x => x.pID);
var dataset = session.QueryOver<vTable>()
.WithSubquery.WhereProperty(v => v.vId).In(res)
.Select(v => v.vId)
.Select(v => v.data)
which works just fine to get data from vTable
however, when I add the 2nd table
var dataset = session.QueryOver<vTable>()
.WithSubquery.WhereProperty(v => v.vId).In(res)
.JoinQueryOver<pTable>(p => p.pId)
.WithSubquery.WhereProperty(p => p.pId).In(res)
.Select(v => v.vId)
.Select(v => v.data)
.Select(p => p.pId)
.Select(p => p.data)
I get the error
Delegate 'System.Func<System.Collections.Generic.IEnumerable<pTable>>' does not take 1 arguments
What am I doing wrong?
.JoinQueryOver<pTable>(p => p.pId)
has to point to a mapped entity or collection not it an id, if you can't map it in the hbm. And also the JoinQueryOver will return pTables not vTables, you might want to use JoinAlias instead if you wan to retain the return type to be a list of vTables, but if all you want is that projection make sure you add aliases the QueryOver and JoinQueryOver calls

How to convert HQL with Group By to QueryOver?

I have a HQL query:
select max(l.Num) from SomeTable l group by l.Type, l.Iteration
How can I translate/convert it to QueryOver?
Following one:
var grouped = session.QueryOver<SomeTable>()
.SelectList(l => l
.SelectGroup(x => x.Type)
.SelectGroup(x => x.Iteration)
.SelectMax(x => x.Num));
will generate SQL:
SELECT
MAX(l.Num),
l.Type,
l.Iteration
FROM
SomeTable l
GROUP BY
l.Type,
l.Iteration
which is not what I expect – I don’t want to have Type and Iteration in Select.
I'm using that query as subquery for select z from c where z IN (subquery).
try with this statement, I've used Aliases and UnderlyingCriteria
SomeTable someTb = null;
var grouped = session.QueryOver<SomeTable>(() => someTb)
.SelectList(l => l.SelectMax(() => someTb.lnum))
.UnderlyingCriteria.SetProjection(
Projections.Group(() => someTb.Type)
,Projections.Group(() => someTb.Iteration))
.List();
I hope it's helpful.