NHibernate Polymorphic Query on a Collection - nhibernate

I'm trying to write a query in NHibernate. I don't really care if I use the Criteria API or HQL, I just can't figure out how to write the query.
Here's my model:
public class LogEntry { public DateTime TimeCreated { get; set; } }
public class Note : LogEntry { public string Content { get; set; } }
public class Workflow { public IList<LogEntry> Log { get; set; } }
I want the query to return all Workflows that which contain a Note with specific words in the Content of the note.
In pseudo-SQL, I'd write this like:
select w.*
from Workflow w
join w.Log l where l is class:Note
where (Note)l.Content like '%keyword%'

I'm not sure about the Criteria API, but HQL seems to handle polymorphic queries quite well, even when searching on a property that only exists in a specific sub-class. I would expect the following to work:
from Workflow w join w.Log l where l.class = Note and l.Content like '%keyword%'

I don't know if there is a better way, but I use subqueries for this:
from Workflow w
join w.Log l
where l in (
select n
from Note n
where n.Content like '%keyword%'
)
(if this doesn't work, write l.id in (select n.id...)
In criteria, you can directly filter properties that are only available on a subclass, but you shouldn't, because it only filters for the first subtype it finds where this property is defined.
I use subqueries as well:
DetachedCriteria subquery = DetachedCriteria.For<Note>("n")
.Add(Expression.Like("n.Content", "%keyword%"))
.SetProjection(Projections.Property("n.id"));
IList<Workflow> workflows = session.CreateCriteria<Workflow>("w")
.CreateCriteria("w.Log", "l")
.Add(Subqueries.PropertyIn("l.id", subquery))
.List<Workflow>();

Related

Subquery - JPA - Include Subquery as part of the main selection

I have an issue to add the result of a subquery as part of the selection.
This is how my data model looks like:
#Entity
#Table(name = "flow")
public class Flow {
#Id
Long id;
#OneToMany(mappedBy="flow", fetch = FetchType.LAZY)
Set<Subscription> subscribers;
... other internal fields
//extra fields that come from other tables
#Transient //as it is not part of the model sometimes must be null
Long numberOfActiveSubscribers;
}
#Entity
#Table(name = "subscriptions")
public class Subscription {
#Id
Long id;
#ManyToOne
#JoinColumn(name = "idflow")
Flow flow;
#OneToMany(name = "user")
User user;
#Column(name = "active")
boolean active;
}
I need to implement a query using jpa specifications that will include in its definition the number of user that have active subscriptions to the flow. So that I will be able to use pagination over that, including sort by this field that is not part of the original flow table. The SQL query I came out with look like this:
SELECT f.*,
(SELECT count(sf.id)
FROM subscription sf
WHERE sf.active = true
AND sf.idf = f.id) as numberofactivesubscribers,
FROM flow f;
I would like to us it in a findAll method like so:
this.repository.findAll(new Specification<Flow>() {
#Override
public Predicate toPredicate(Root<Flow> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
Subquery<Long> sq = query.subquery(Long.class);
Root<Subscription> subsRoot = sq.from(Subscription.class);
sq.select(cb.count(subsRoot.get("id")))
.where(cb.equal(subsRoot.get("flow"), flowRoot),
cb.equal(subsRoot.get("active"), true));
//I DONT KNOW HOW TO INCLUDE THIS AS A PART OF THE MAIN SELECT OF THIS QUERY
}
}, pagination);
But as you can see I don't know how to include the subquery as part of the select method as I did in my SQL query.

Breeze Query: compare two columns on Related entity

Using Breeze, how can I compare two columns on a related property of an entity?
public class TableA{
public ICollection<TableB> TableBEntity {get; set;}
}
public class TableB{
public TableC TableCEntity {get; set;}
}
public class TableC {
public string columnA { get; set;}
public string columnB { get; set;}
}
var subpredicate = Predicate.create('TableCEntity.columnA', FilterQueryOp.Equals, 'TableCEntity.columnB');
var predicate = Predicate.create('TableBEntity', FilterQueryOp.Any, subpredicate);
var query1 = EntityQuery.from('TableB')
.where(subpredicate);
var query2 = EntityQuery.from('TableA')
.where(predicate );
query1 above executes without error. However query2 gives me the error:
The query specified in the URI is not valid. Could not find a property named 'TableCEntity' on type 'TableA'.
It seems as the subpredicate is not properly evaluated and it searches for the property TableCEntity on TableA instead of on TableB. If I change the subpredicate to
var subpredicate = Predicate.create('TableCEntity.columnA', FilterQueryOp.Equals, 'asamplevalue');
then, query2 works find. It just does not work the right side of the predicate is a record's column.
Am i doing something wrong or is this a bug?
The breeze 'any' and 'all' operators require that the property returned by the first expression ( "orders" in the example below) be a nonscalar navigation property. i.e. a navigation property that returns a collection. The subpredicate is then is just a simple predicate against the type returned by the first expression.
var query = EntityQuery.from("Employees")
.where("orders", "any", "freight", ">", 950);
with predicates this would be expressed as follows
var p2 = Predicate.create("freight", ">", 950);
var p1 = Predicate.create("orders", "any", p2);
var query = EntityQuery.from("Employees").where(p1);
In your case, i'm not sure what you are trying to do with your 'query2'. 'query2' appears to be a query over a collection of 'TableB' entities returned from 'TableA', but the 'subpredicate' deals with 'TableC' entities.
If this doesn't help, it might be easier to assist if you could restate your TableA, B and C names with table names that are a bit more intuitive and pluralize the collection property names.
Hope this helps.

Fluent Nhibernate join on non foreign key property

I can't find this anywhere, but it seems pretty trivial. So, please excuse if this is a duplicate.
I have something like:
public class Doctor : Entity
{
...some other properties here...
public virtual string Email { get; set; }
}
public class Lawyer : Entity
{
...some other properties here...
public virtual string Email { get; set; }
}
I want to return all doctors where there is no email match in the Lawyers table like:
select * from Doctors d
where d.Email not in
(select l.Email from Lawyers l where l.Email is not null)
or using a join:
select d.* from Doctors d
left join Lawyers l on l.Email = d.Email
where l.Email is null
The problem is that the Email is of course not set up as a foreign key. I have no mapped property on the Doctor entity that maps to Lawyer.
What I've tried so far:
ICriteria criteria = Session.CreateCriteria(typeof(Doctor))
.CreateAlias("Lawyers.Email", "LawyerEmail", JoinType.LeftOuterJoin)
.Add(Restrictions.IsNull("LawyerEmail"));
return criteria.List<Doctor>();
But, I get a "cannot resolve property Lawyer of MyPlatform.MyNamespace.Doctor" error. Any ideas how to set up my DoctorMap and adjust the criteria tomfoolery to achieve this?
NHibernate for the loss........Entity Framework for the win....
We can achieve that with a feature called subquery:
// a inner SELECT to return all EMAILs from Lawyer table
var subQuery = DetachedCriteria.For<Lawyer>()
.SetProjection(Projections.Property("Email"));
// the root SELECT to get only these Doctors
var criteria = session.CreateCriteria<Doctor>();
// whos email is not in the sub SELECT
criteria.Add(Subqueries.PropertyNotIn("Email", subQuery));
// get first 10
var result = criteria
.SetMaxResults(10)
.SetFirstResult(0) // paging
.List<Doctor>();

How to use ExecuteStoreQuery for fetching data from Multiple tables

My application is in Asp.Net MVC3 coded in C#.Net. My issue is i want to get data from database using SQL query, for that i'm aware that i can use the below technique
Code to get data using ExecuteStoreQuery
var Complete_Data = db.ExecuteStoreQuery<Mytable>("select * from Mytable").ToList();
I have two issues
How to get the data in var Complete_Data when the data is coming from Multiple table (i.e the query has multiple joins).
I will be generating the selecting columns dynamically. The select query columns will generating dynamically.
Below is the sample example
string Field_Formation=string.Empty;
foreach (var item in My_Parameter_Collection_Logic_Variable)
{
Field_Formation+= item.Field_Name + ",";
}
Here My_Parameter_Collection_Logic_Variable is a variable declared in my code that will have a certain collection.
var Complete_Data = db.ExecuteStoreQuery<What_Class_To_Be_Taken_Here>("select" + Field_Formation + " from My_Tables_With_Multiple_Joins").ToList();
Need suggestion, whether it is possible to do such a stuff.
var Complete_Data = db.ExecuteStoreQuery<**What_Class_To_Be_Taken_Here**>
<What_Class_To_Be_Taken_Here> - this class must have properties like your Field_Formation.
For example if your select is:
"SELECT e.idExpense AS ExpenseID, e.idVehicle as VehicleID, d.Date AS Date1 .... join ... where ..."
create class like this:
public class ItemExample
{
private int VehicleID{ get; set; }
private int ExpenseID{ get; set; }
private DateTime? Date1 { get; set; }
}
You can write query using join and get your data from multiple tables.
for eg:-
var Complete_Data = db.ExecuteStoreQuery("select * from Table1 as T1 inner join Table2 as T2 on T1.id=T2.Id").ToList();
You will get all column of all table and then you can access using A.ColumnName or B.ColumnName.

NHibernate: Return a referenced property without lazy loading using SetProjection

I have the following two classes mapped:
public class Foo
{
public virtual Guid Id { get; set; }
public virtual Bar Bar { get; set; }
}
public class Bar
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
}
I have the following Criteria:
return Session.CreateCriteria<Foo>("f")
.CreateAlias("f.Bar", "b")
.SetProjection(Projections.Property("f.Bar"))
.List<Bar>();
This generates the following SQL:
select b.Id from Foo f
inner join Bar on f.BarId = b.Id
Notice how only the Id of Bar is returned, rather than the entire class. How do I get all the columns of Bar instead?
The solution depends on your needs.
First of all if you need to return entity Bar, then your initial Criteria must be of type Bar so you just:
session.CreateCriteria<Bar>().
.List<Bar();
If you need to add some restrictions based on Foo then there are two ways.
Use HQL
session.CreateQuery(
"select b " +
"from Foo f " +
"inner join f.Bar b " +
"where f.Id = 9 ")
.List();
Use query only property. Do this by adding access="noop" in your Bar mapping file.
<many-to-one name="foo" access="noop" class="Foo" column="FooFk"/>
Note that your domain model doesn't have to change! You don't need to add that "foo" property/field in the Bar class.
Now you can use that property in your queries like:
session.CreateCriteria<Bar>()
.CreateAlias("foo", "f")
.Add(Restrictions.Eq("f.Id", 9))
.List<Bar>();
If it doesn't fit into a single criteria, use DetachedCriteria:
var subquery = DetachedCriteria.For<Foo>("f")
.SetProjection(Projections.Property("f.Bar"))
// more filter criteria ...
return session.CreateCriteria<Bar>
.SetProjection(Subqueries.PropertyIn("id", subquery));
which creates sql like this:
select Bar.*
from Bar
where Bar.id in (Select b.id from Foo f inner join Bar b on ...)
Is obviously only make sense if you have some filter criteria base on Foo in the subquery.
If the above detached criteria query doesnt work out here is a simple HQL query that should do the trick.
var hqlQuery="select b from Foo as f inner join f.Bar as b";
Now run this query as follows:
Session.CreateQuery(hqlQuery).List<Boo>();
you can now add where condition to your query too if you want.
Hope this helps.. I can tell you how to do it with Criteria but I think this is a little easier for you to ustand as u seem to be comfortable with SQL.