NHibernate Multiple Criteria Difficulties - nhibernate

I'm having difficulties with multiple joins in the NHibernate Criteria search. Say I had a pet table, and I wanted to return all pets where the pet category was Dog, and the owner gender was female, ordered by the pet birthday. I've tried a bunch of permutations for how I can get this but haven't been able to figure it out. My latest iteration is as follows:
var recentPets = session.CreateCriteria(typeof(Pet))
.AddOrder(Order.Desc("PetBirthday"))
.CreateCriteria("PetType", "pt", JoinType.InnerJoin)
.CreateCriteria("PetOwnerId", "po", JoinType.InnerJoin)
.Add(Expression.Eq("pt.PetTypeName", petType))
.Add(Expression.Eq("po.PersonGender", gender))
.List<Pet>();
Thanks so much for the help!

Is there a reason you are not using the Hibernate/Java persistence query language to perform the query?
select p from Pet p
join p.owner o
where o.gender = :gender
and p.type.name = :petType
order by p.birthday

Related

Cypher - Add multiple connections

I have 2 nodes:
Students and Subjects.
I want to be able to add multiple student names to multiple subjects at the same time using cypher query.
So far I have done it by iterating through the list of names of students and subjects and executing the query for each. but is there a way to do the same in the query itself?
This is the query I use for adding 1 student to 1 subject:
MATCH
(s:Student)-[:STUDENT_BELONGS_TO]->(c:Classroom),
(u:Subjects)-[:SUBJECTS_TAUGHT_IN]->(c:Classroom)
WHERE
s.id = ${"$"}studentId
AND c.id = ${"$"}classroomId
AND u.name = ${"$"}subjectNames
AND NOT (s)-[:IN_SUBJECT]->(u)
CREATE (s)-[:IN_SUBJECT]->(u)
So I want to be able to receive multiple subjectNames and studentIds at once to create these connections. Any guidance for multi relationships in cypher ?
I think what you are looking for is UNWIND. If you have an array as parameter to your query:
studentList :
[
studentId: "sid1", classroomId: "cid1", subjectNames: ['s1','s2'] },
studentId: "sid2", classroomId: "cid2", subjectNames: ['s1','s3'] },
...
]
You can UNWIND that parameter in the beginning of your query:
UNWIND $studentList as student
MATCH
(s:Student)-[:STUDENT_BELONGS_TO]->(c:Classroom),
(u:Subjects)-[:SUBJECTS_TAUGHT_IN]->(c:Classroom)
WHERE
s.id = student.studentId
AND c.id = student.classroomId
AND u.name = in student.subjectNames
AND NOT (s)-[:IN_SUBJECT]->(u)
CREATE (s)-[:IN_SUBJECT]->(u)
You probably need to use UNWIND.
I haven't tested the code, but something like this might work:
MATCH
(s:Student)-[:STUDENT_BELONGS_TO]->(c:Classroom),
(u:Subjects)-[:SUBJECTS_TAUGHT_IN]->(c:Classroom)
WITH
s AS student, COLLECT(u) AS subjects
UNWIND subjects AS subject
CREATE (student)-[:IN_SUBJECT]->(subject)

Return results from more than one database table in Django

Suppose I have 3 hypothetical models;
class State(models.Model):
name = models.CharField(max_length=20)
class Company(models.Model):
name = models.CharField(max_length=60)
state = models.ForeignField(State)
class Person(models.Model):
name = models.CharField(max_length=60)
state = models.ForeignField(State)
I want to be able to return results in a Django app, where the results, if using SQL directly, would be based on a query such as this:
SELECT a.name as 'personName',b.name as 'companyName', b.state as 'State'
FROM Person a, Company b
WHERE a.state=b.state
I have tried using the select_related() method as suggested here, but I don't think this is quite what I am after, since I am trying to join two tables that have a common foreign-key, but have no key-relationships amongst themselves.
Any suggestions?
Since a Person can have multiple Companys in the same state. It is not a good idea to do the JOIN at the database level. That would mean that the database will (likely) return the same Company multiple times, making the output quite large.
We can prefetch the related companies, with:
qs = Person.objects.select_related('state').prefetch_related('state__company')
Then we can query the Companys in the same state with:
for person in qs:
print(person.state.company_set.all())
You can use a Prefetch-object [Django-doc] to prefetch the list of related companies in an attribute of the Person, for example:
from django.db.models import Prefetch
qs = Person.objects.prefetch_related(
Prefetch('state__company', Company.objects.all(), to_attr='same_state_companies')
)
Then you can print the companies with:
for person in qs:
print(person.same_state_companies)

Write query to find parents with no children

I have a situation where a patient record will/wont have equipment history records. I am needing to find all patient records that don't have a equipment records or all patients with equipment records that has a field that is not null. The way I am querying below does not work. I don't find any patients without equipment history at all. Any suggestions? The Mappings are correct since I can directly access the records, update, etc. I can't pull back all patients and do a child count since I have a database consisting of over 40,000 records. It would be slow and use too much memory.
Any help would be greatly appreciated. Thanks in advance.
public IList<Patients> GetAllPatientsWithoutDevice(int facilityID, string search)
{
ICriteria query = FluentSessionManager.GetSession()
.CreateCriteria<Patients>()
.Add(Expression.Eq("IsDeleted", false))
.CreateAlias("Facilities", "f")
.Add(Expression.Eq("f.ID", facilityID))
.CreateAlias("EquipmentHistory", "eh") // Tried, inner, left and right joins...
.Add(Expression.Or(
Expression.IsNull("EquipmentHistory"),
Expression.IsNotNull("eh.DateOffPatient")
));
query.AddOrder(new Order("FirstName", true))
.AddOrder(new Order("LastName", true));
return query.List<Patients>();
}
I figured it out... I had to use "IsEmpty" instead of "IsNull".
ICriteria query = FluentSessionManager.GetSession()
.CreateCriteria<Patients>()
.Add(Expression.Eq("IsDeleted", false))
.CreateAlias("Facilities", "f")
.Add(Expression.Eq("f.ID", facilityID))
.CreateAlias("EquipmentHistory", "eh", NHibernate.SqlCommand.JoinType.LeftOuterJoin)
.Add(Expression.Or(
Expression.IsEmpty("EquipmentHistory"),
Expression.IsNotNull("eh.DateOffPatient")
));

What's the ActiveRecord way to search for nulls in a join?

Suppose I have three tables in my Rails app: cats, dogs, and owners. I want to find all the cats whose owners do not also have dogs.
With SQL, I could do the following:
SELECT
`cats`.*
FROM
`cats`
LEFT JOIN `dogs` ON `cats`.`owner_id` = `dogs`.`owner_id`
WHERE
`dogs`.`id` IS NULL;
However, I'd like to do this as a chainable scope on Cat. The closest I've gotten so far is Cat.connection.select_all(query_string), but that's not chainable.
What's the ActiveRecord way of doing this?
Cat.joins("LEFT JOIN `dogs` ON `cats`.`owner_id` = `dogs`.`owner_id`").where("`dogs`.`id` IS NULL")
Or if you want it as a scope:
scope :cats_without_dogs, joins("LEFT JOIN `dogs` ON `cats`.`owner_id` = `dogs`.`owner_id`").where("`dogs`.`id` IS NULL")

nHibernate collections and alias criteria

I have a simple test object model in which there are schools, and a school has a collection of students.
I would like to retrieve a school and all its students who are above a certain age.
I carry out the following query, which obtains a given school and the children which are above a certain age:
public School GetSchoolAndStudentsWithDOBAbove(int schoolid, DateTime dob)
{
var school = this.Session.CreateCriteria(typeof(School))
.CreateAlias("Students", "students")
.Add(Expression.And(Expression.Eq("SchoolId", schoolid), Expression.Gt("students.DOB", dob)))
.UniqueResult<School>();
return school;
}
This all works fine and I can see the query going to the database and returning the expected number of rows.
However, when I carry out either of the following, it gives me the total number of students in the given school (regardless of the preceding request) by running another query:
foreach (Student st in s.Students)
{
Console.WriteLine(st.FirstName);
}
Assert.AreEqual(s.Students.Count, 3);
Can anyone explain why?
You made your query on the School class and you restricted your results on it, not on the mapped related objects.
Now there are many ways to do this.
You can make a static filter as IanL said, however its not really flexible.
You can just iterate the collection like mxmissile but that is ugly and slow (especially considering lazy loading considerations)
I would provide 2 different solutions:
In the first you maintain the query you have and you fire a dynamic filter on the collection (maintaining a lazy-loaded collection) and doing a round-trip to the database:
var school = GetSchoolAndStudentsWithDOBAbove(5, dob);
IQuery qDob = nhSession.CreateFilter(school.Students, "where DOB > :dob").SetDateTime("dob", dob);
IList<Student> dobedSchoolStudents = qDob.List<Student>();
In the second solution just fetch both the school and the students in one shot:
object result = nhSession.CreateQuery(
"select ss, st from School ss, Student st
where ss.Id = st.School.Id and ss.Id = :schId and st.DOB > :dob")
.SetInt32("schId", 5).SetDateTime("dob", dob).List();
ss is a School object and st is a Student collection.
And this can definitely be done using the criteria query you use now (using Projections)
Unfortunately s.Students will not contain your "queried" results. You will have to create a separate query for Students to reach your goal.
foreach(var st in s.Students.Where(x => x.DOB > dob))
Console.WriteLine(st.FirstName);
Warning: That will still make second trip to the db depending on your mapping, and it will still retrieve all students.
I'm not sure but you could possibly use Projections to do all this in one query, but I am by no means an expert on that.
You do have the option of filtering data. If it there is a single instance of the query mxmissle option would be the better choice.
Nhibernate Filter Documentation
Filters do have there uses, but depending on the version you are using there can be issues where filtered collections are not cached correctly.