NHibernate Queries: Find all users that are members of the same blogs as this user
Let us assume that we have the following model:
- User
- n:m -> Blogs
- n:m -> Users
Given a user, how would you find all the users that are members of all the blogs that the user is a member of?
Turn out that NHibernate makes it very easy:
DetachedCriteria usersForSameBlog = DetachedCriteria.For<User>() .Add(Expression.IdEq(userId)) .CreateCriteria("Blogs") .CreateCriteria("Users", "user") .SetProjection(Projections.Id()); session.CreateCriteria(typeof(User)) .Add(Subqueries.PropertyIn("id", usersForSameBlog)) .List();
And the resulting SQL is:
SELECT this_.Id AS Id5_0_,
this_.Password AS Password5_0_,
this_.Username AS Username5_0_,
this_.Email AS Email5_0_,
this_.CreatedAt AS CreatedAt5_0_,
this_.Bio AS Bio5_0_
FROM Users this_
WHERE this_.Id IN (SELECT this_0_.Id AS y0_
FROM Users this_0_
INNER JOIN UsersBlogs blogs4_
ON this_0_.Id = blogs4_.UserId
INNER JOIN Blogs blog1_
ON blogs4_.BlogId = blog1_.Id
INNER JOIN UsersBlogs users6_
ON blog1_.Id = users6_.BlogId
INNER JOIN Users user2_
ON users6_.UserId = user2_.Id
WHERE this_0_.Id = @p0)
Comments
I'm probably being stupid, but doesn't that SQL find the users that are members of any of the same blogs that the specified user is a member of? (Rather than all, as specified in the post.)
And out of interest, am I right in saying we don't actually need the join to Blogs at all? Assuming there are appropriate constraints, the subselect could be:
SELECT this_0_.Id AS y0_
FROM Users this_0_
INNER JOIN UsersBlogs blogs4_
ON this_0_.Id = blogs4_.UserId
INNER JOIN UsersBlogs users6_
ON blogs4_.BlogId = users6_.BlogId
INNER JOIN Users user2_
ON users6_.UserId = user2_.Id
WHERE this_0_.Id = @p0
Hopefully the query optimiser in the DB would figure this out though :)
Jon
Jon,
Yes, you are correct, I wasn't precise enough.
As for skipping the blog's join, it would be valid, but it is likely that using the fully path will allow the DB to utilize the FK indexes.
Maybe I am reading it wrong, but shouldn't the projection be something like
.SetProjection(Property.ForName("user.id")) so we get the related user ids?
No, create criteria means that you are now in a sub criteria, so the entity you now refer to is the user.
If I would have used the CreateAlias(), then I would have to do so.
"user.id" would work as well, btw.
Comment preview