NHibernate QueryOver to select one entity for each group entity - nhibernate

I'm writing part of turist blog so I have two entities: place and photo in relation 1 to many, so every place can have multiple photos, but single photo belongs to one place.
My aim is to create summary: last added 3 places and with every place I want to have one photo (for example photo with the lower id, it doesn't matter). It's important to have not only the id of the photo, but more info (columns) like photo label, path to photo,etc.
Using SQL in MS SQL 2008 it can be done by using rank functions:
select top 3 * from
(
SELECT p.id, p.label, p.VisitDate, ph.id AS photo_id, ph.Label, ph.Path,
RANK() OVER (PARTITION BY p.id ORDER BY ph.id) AS _rank
FROM place AS p INNER JOIN
photo AS ph ON p.id = ph.place_id
) ranked
where _rank = 1
order by VisitDate desc
Or it can be resolved without ranking:
SELECT TOP (3) a.id AS ida, z.id AS idz, z.etykieta, z.sciezka, a.data_odwiedzenia
FROM place AS a INNER JOIN
photo AS z ON a.id = z.id_atrakcji
and z.id in
(
SELECT min(z.id) AS idz
FROM photo z
where z.id_atrakcji in (select top 3 a.id from place a order by a.data_dodania desc)
group by z.id_atrakcji
)
ORDER BY a.data_odwiedzenia DESC
I started writing this query without ranking, but I've stucked.
summaryList =
session.QueryOver(() => placeAlias)
.JoinAlias(o => o.Photos, () => photoAlias)
.SelectList(list => list
.Select(() => placeAlias.Id).WithAlias(() => summaryAlias.PlaceId)
.Select(() => placeAlias.Etykieta).WithAlias(() => summaryAlias.PlaceName)
.Select(() => photoAlias.Etykieta).WithAlias(() => summaryAlias.PhotoLabel)
.Select(() => photoAlias.Id).WithAlias(() => summaryAlias.PhotoId)
)
.OrderByAlias(() => placeAlias.VisitDate).Desc
.WithSubquery.WhereProperty(x => x.Id).In(lastPlaces)
.TransformUsing(Transformers.AliasToBean<PlacesSummary>())
.List<PlacesSummary>();
Can you help me with this? How to write lastPlaces queryover or maybe there's another approach?
Regards,
Macko

var subquery = QueryOver.Of(() => photoAlias)
.Where(photo => photo.Place.Id == placeAlias.Id)
.Select(Projections.Max<Photo>(photo => photo.Id));
var summaryList = session.QueryOver(() => placeAlias)
.OrderBy(() => placeAlias.VisitDate).Desc
.JoinQueryOver(() => placeAlias.Photos, () => photoAlias)
.WithSubquery.WhereProperty(photo => photo.Id).Eq(subquery)
.SelectList(list => list
.Select(() => placeAlias.Id).WithAlias(() => summaryAlias.PlaceId)
.Select(() => placeAlias.Etykieta).WithAlias(() => summaryAlias.PlaceName)
.Select(() => photoAlias.Etykieta).WithAlias(() => summaryAlias.PhotoLabel)
.Select(() => photoAlias.Id).WithAlias(() => summaryAlias.PhotoId)
)
.TransformUsing(Transformers.AliasToBean<PlacesSummary>())
.List<PlacesSummary>();
cant test it right now

it doesn't work as it should but you it is very close:
summaryList =
session.QueryOver(() => placeAlias)
.OrderByAlias(() => placeAlias.VisitDate).Desc
.JoinAlias(o => o.Photos, () => photoAlias)
**.WithSubquery.WhereProperty(()=> photoAlias.Id).Eq(subquery)**
.SelectList(list => list
.Select(() => placeAlias.Id).WithAlias(() => summaryAlias.PlaceId)
.Select(() => placeAlias.Etykieta).WithAlias(() => summaryAlias.PlaceName)
.Select(() => photoAlias.Etykieta).WithAlias(() => summaryAlias.PhotoLabel)
.Select(() => photoAlias.Id).WithAlias(() => summaryAlias.PhotoId))
.TransformUsing(Transformers.AliasToBean<PlacesSummary>())
.Take(3)
.List<PlacesSummary>();
It differs in only one line .WithSubquery where orginally it refers to Place instead it should refer to Photo. Now it's working and it produces following SQL:
SELECT TOP (3) this_.id as y0_, this_.PlaceName as y1_, photoalias1_.PhotoName as y2_, photoalias1_.id as y3_
FROM [place] this_ inner join [photo] photoalias1_ on this_.id=photoalias1_.id_place
WHERE photoalias1_.id = (SELECT min(this_0_.id) as y0_ FROM [photo] this_0_ WHERE this_0_.id_place = this_.id)
ORDER BY this_.DateVisit desc
It's working great but I have another question:
How to rewrite in QueryOver such SQL:
SELECT min(id)
FROM photo
where id in (... it doesn't matter what's here ...)
group by place_id
So I want only list of photo ids (only column) and this query will be used as subquery in another queryover. Can it be done by QueryOver syntax?

Related

Can't build 2 tables JOIN with only certain fields to select in Nhb 4

I try to run query like this with Nhibernate v.4:
Select o.Number, c.Address
From Order o join Client c on o.ClientId = c.Id
Where c.Name = "John"
Tried many ways to do that with JoinQueryOver and JoinAlias but nothing helps, endup with error "could not resolve property: Address of Order"
Session.QueryOver<Order>()
.JoinQueryOver(s => s.Client)
.Where(() => c.Name == "John")
.SelectList(x => .Select(Projections.Property<Order>(o => o.Number)
.SelectList(x => .Select(Projections.Property<Order>(o => o.Client.Address))
or like this
...
.SelectList(x => .Select(Projections.Property<Order>(o => o.Number)
.SelectList(x => .Select(Projections.Property<Client>(c => c.Address))
What is the right way to build the query or something very similar? Problem with 'Select' operator, 'Where' part works fine. I can also build non-join queries by this 2 entities separate from each other and it works well, but no idea how to join them
Finally I've figured out how to make it work:
Client client = null;
OrderClientDto dto = null;
var result = Session.QueryOver<Order>()
.JoinAlias(o => o.Client, () => client)
.Where(() => c.Name == "John")
.SelectList(x => x
.Select(o => o.Number).WithAlias(() => dto.Number)
.Select(() => o.Client.Address).WithAlias(() => dto.Address))
.TransformUsing(Transformers.AliasToBean<OrderClientDto>())
.List<OrderClientDto>();
Nhb is far from being intuitive and convenient. Same query is much easier to write in EF.

NHibernate QueryOver Where with internal select

I have a problem with translating this SQL into QueryOver notation. Can you help me?
SELECT * FROM Alerts a
WHERE a.CreatedOn = (
SELECT MAX(b.CreatedOn) FROM Alerts b WHERE b.UserFk=a.UserFk);
I try to select last alert for every user. I use CreatedOn and cannot use Id.
I have so far:
session.QueryOver(() => alertAlias)
.SelectList(list => list
.Select(() => alertAlias.User.Id)
.SelectSubQuery(
QueryOver.Of<Alerts>()
.Where(x => x.User.Id == alertAlias.User.Id)
.OrderBy(x => x.CreatedOn).Desc
.Select(x => x.CreatedOn)
.Take(1)));
I know it adds user's last alert date to every user's alert row. But I want to have only last alerts.
Your attempt is using subquery inside of a SELECT statement. But we need to move it into WHERE. This should be the way:
// this is a subquery (SELECT ....
var subquery = QueryOver.Of<Alerts>()
.Where(x => x.User.Id == alertAlias.User.Id)
.OrderBy(x => x.CreatedOn).Desc
.Select(x => x.CreatedOn)
.Take(1)));
// main Query could now have or do not have that subquery selected
var query = session.QueryOver(() => alertAlias)
.SelectList(list => list
.Select(() => alertAlias.User.Id)
// could be there
.SelectSubQuery(subquery)
)
// but mostly here we do use WHERE clause
.WithSubquery
.WhereProperty(() => alertAlias.CreatedOn)
.Eq(subquery)
;
// such a query could be returned as a list of arrays
var results = query.List<object[]>();
We can also use some Result Transformer, but this is another story...

NHibernate QueryOver with JoinQueryOver select all columns

I have some QueryOver with several joins and the result I get is OK in terms of the returned objects. This is the code>
var l = session.QueryOver<Discount>(() => discount)
.JoinQueryOver<ConPrdGrp>(r => r.ConPrdGrps)
.JoinQueryOver<PrdGroupTree>(k => k.PrdGroupTree)
.JoinQueryOver<Product>(g => g.Products)
.Where(p => p.ProductID == Product.ProductID)
.And(() => discount.FomDato <= DateTime.Now && discount.TomDato >= DateTime.Now).List();
But if I look at the SQL statement, I can see that the generated query selects ALL columns from all joined tables although the result only returns a list of Discount objects. I can get some properties of Discount using projections and the query will be much smaller. But how I can instruct NHibernate to get just columns from the Discount table and not from all joined tables?
As far as I know there is NOT a simple way like: .Select(Projections.Properties<Discount>()). Check this a bit outdated question (NHIbernate: Shortcut for projecting all properties?)
What you can do is explicitly name the columns you would like to see:
... // the QueryOver you have
.SelectList (l => l
.Select(() => discount.ID).WithAlias(() => discount.ID)
.Select(() => discount.Code).WithAlias(() => discount.Code)
...
)
.TransformUsing(Transformers.AliasToBean<Discount>())
...
The second approach could be to create the Subquery, and then just QueryOver<Discount>() 16.8. Subqueries
QueryOver<Discount> subQuery =
QueryOver.Of<Discount>(() => innerDiscount)
.JoinQueryOver<ConPrdGrp>(r => r.ConPrdGrps)
.JoinQueryOver<PrdGroupTree>(k => k.PrdGroupTree)
.JoinQueryOver<Product>(g => g.Products)
.Where(p => p.ProductID == Product.ProductID)
.And(() => innerDiscount.FomDato <= DateTime.Now
&& innerDiscount.TomDato >= DateTime.Now).List()
.Select( Projections.Property(() => innerDiscount.ID))
;
var query = session.QueryOver<Discount>(() => discount)
.Where(Subqueries.PropertyIn("ID", subQuery.DetachedCriteria))
;

Aggregate query in fluent nhibernate

select T.Id, count(Th.ampount)
from TH, T
where Th.Tid= T.id
group by T.Id
How to write above query using fluent nhibernate. I don't want to use CreateSQLQuery().
session.QueryOver<TH>()
.JoinAlias(th => th.Ts, () => tAlias)
.SelectList(list => list
.SelectGroup(th => th.Id)
.SelectCount(() => tAlias.amount)
// or did you mean
.SelectSum(() => tAlias.amount)
)
.List<object[]>();

QueryOver with Select and OrderBy in NHibernate

I am wondering how do I order a group of results after a select with QueryOver. My query is the following:
CurrentSession.QueryOver<Book>()
.Where(b => b.Author.Name = "SimpleName")
.Select(Projections.Distinct(Projections.Property<Book>(b => b.Genre)))
.OrderBy<Genre>(g => g.Name) // this extension does not exist! How do I order for a Genre?
.List<Genre>()
How can I do?
Your query won't work to begin with. You first of all need to do a join, then you can do your order by and select projections.
Author author = null;
Genre genre = null;
CurrentSession.QueryOver<Book>()
.JoinAlias(b => b.Author, author)
.JoinAlias(b => b.Genre, genre)
.Where(() => author.Name == "SimpleName")
.OrderBy(() => genre.Name)
.Select(Projections.Distinct(Projections.Property<Book>(b => b.Genre)))
.List<Genre>();