Grails Self Referencing Criteria - grails-orm

In the project I´m working there is a part of the database that is like the following diagram
The domain classes have a definition similar to the following:
class File{
String name
}
class Document{
File file
}
class LogEntry{
Document document
Date date
}
First I need to get only the latest LogEntry for all Documents; in SQL I do the following (SQL_1):
SELECT t1.* FROM log_entry AS t1
LEFT OUTER JOIN log_entry t2
on t1.document_id = t2.document_id AND t1.date < t2.date
WHERE t2.date IS NULL
Then in my service I have a function like this:
List<LogEntry> logs(){
LogEntry.withSession {Session session ->
def query = session.createSQLQuery(
"""SELECT t1.* FROM log_entry AS t1
LEFT OUTER JOIN log_entry t2
on t1.document_id = t2.document_id AND t1.date < t2.date
WHERE t2.date IS NULL"""
)
def results = query.with {
addEntity(LogEntry)
list()
}
return results
}
}
The SQL query does solve my problem, at least in a way. I need to also paginate, filter and sort my results as well as join the tables LogEntry, Document and File. Altough it is doable in SQL it might get complicated quite quickly.
In other project I´ve used criteriaQuery similar to the following:
Criteria criteria = LogEntry.createCriteria()
criteria.list(params){ //Max, Offset, Sort, Order
fetchMode 'document', FetchMode.JOIN //THE JOIN PART
fetchMode 'document.file', FetchMode.JOIN //THE JOIN PART
createAlias("document","_document") //Alias could be an option but I would need to add transients, since it seems to need an association path, and even then I am not so sure
if(params.filter){ //Filters
if(params.filter.name){
eq('name', filter.name)
}
}
}
In these kinds of criteria I´ve been able to add custom filters, etc. But I have no Idea how to translate my query(SQL_1) into a criteria. Is there a way to accomplish this with criteriaBuilders or should I stick to sql?

Related

UNNEST list of integers in BigQuery query return ```TypeError: Object of type int64 is not JSON serializable```

I am querying a join of tables in BigQuery for a specific list of ids that are of type INT64 and I cannot find what to do, as I constantly have th following error
TypeError: Object of type int64 is not JSON serializable
My query looks like this:
client = bigquery.Client(credentials=credentials)
query = """
SELECT t1.*,
t2.*,
t3.*,
t4.* FROM `<project>.<dataset>.<tabel1>` as t1
join `<project>.<dataset>.<tabel2>` as t2
on t1.label = t2.id
join `<project>.<dataset>.<tabel3>` as t3
on t3.A = t2.A
join `<project>.<dataset>.<tabel4>` as t4
on t4.obj= t2.obj and t4.A = t3.A
where t1.id in unnest(#list)
"""
job_config = bigquery.QueryJobConfig(query_parameters=[
bigquery.ArrayQueryParameter("list", "STRING", list),
])
choices= client.query(query, job_config=job_config).to_dataframe()
where my list is of the type:
list = [3651056, 3651049, 3640195, 3629411, 3627024,3624939]
Now, this method works perfectly whenever the list is of the type:
list = ['3651056', '3651049', '3640195', '3629411', '3627024', '3624939']
I have tried casting the column I want to pick the list items from before querying but it implies I need to cast the entire table, which contain over 4 billion rows. Not efficient at all.
I would be grateful for any insights on how to solve this.
EDIT:
There is one option. Namely to first cast my list to string and then:
client = bigquery.Client(credentials=credentials)
query = """
SELECT t1.*,
t2.*,
t3.*,
t4.* FROM `<project>.<dataset>.<tabel1>` as t1
join `<project>.<dataset>.<tabel2>` as t2
on t1.label = t2.id
join `<project>.<dataset>.<tabel3>` as t3
on t3.A = t2.A
join `<project>.<dataset>.<tabel4>` as t4
on t4.obj= t2.obj and t4.A = t3.A
where cast(t1.id as STRING) in unnest(#list)
"""
job_config = bigquery.QueryJobConfig(query_parameters=[
bigquery.ArrayQueryParameter("list", "STRING", list),
])
choices= client.query(query, job_config=job_config).to_dataframe()
But is there a more direct way to do this?
As #PratikPatil mentioned in comments:
During comparison - both sides needs to be of same data type otherwise, BigQuery will result to an error.
Casting is still best choice for the issue you have with query. But if you want to check if any side of data can be natively converted to int64 (if you are not expecting any strings etc at all). That will avoid extra casting.
Posting the answer as community wiki as this is the best practice and for the benefit of the community that might encounter this use case in the future.
Feel free to edit this answer for additional information.

SQL or django query to find the similar entries

I want to return the user a list of similar tasks. Two tasks A and B are considered similar if all the words in task A exist in task B or vice versa.
I tried the query given below but couldn't get the required result.
SELECT t1.task
FROM todolistapp_todo t1
LEFT JOIN todolistapp_todo t2
ON
t1.task in (t2.task) and t1.id != t2.id;
I'm able to do this by the nested loop. But I want to do it with minimum complexity.
similar = set()
for task in tasks:
for nested_task in tasks
if (task.task in nested_task.task or nested_task.task in task.task) and task.id != nested_task.id:
similar.add(task)
can you try:
[similar.add(similar_task) for similar_task in tasks if tasks.filter(task=similar_task.task).count()>1]
I'm on phone then sorry if not working :)

Force LINQ to SQL to use RowNumber() instead of Top n When Using .Skip(0)

Is there a way to force LINQ to SQL to avoid using TOP X when using Skip(0)? I have a query that runs just fine for every paged result...except for page 1. I've profiled the query and the introduction of a TOP clause just kills it. I'm perplexed on why that is, but it just does. However, using RowNumber Between 1 AND 10 works just fine.
Is there a way to force LINQ to SQL to avoid using TOP X when using Skip(0)? I have a query that runs just fine for every paged result...except for page 1. I've profiled the query and the introduction of a TOP clause just kills it. I'm perplexed on why that is, but it just does. However, using RowNumber Between 1 AND 10 works just fine.
The culprit seems to be an EXISTS condition in my WHERE clause. The produced SQL is below. In SQL Manager, this query runs fine and returns 14 results...however it times out once I add a TOP 10 (as LINQ would do). However, if I comment the EXISTS in my where clause, then the problem goes away.
SELECT
t0.ProtectiveOrderID,
t3.DocketID,
t3.DocketNumber AS CaseNumber,
t3.PartySuffix AS CaseNumberSuffix,
t5.FirstName AS RespondentNameFirst,
t5.MiddleName AS RespondentNameMiddle,
t5.LastName AS RespondentNameLast,
t5.NameSuffix AS RespondentNameSuffix,
t4.FirstName AS ProtectedNameFirst,
t4.MiddleName AS ProtectedNameMiddle,
t4.LastName AS ProtectedNameLast,
t4.NameSuffix AS ProtectedNameSuffix,
t3.ChildNextFriendFirstName AS ChildNextFriendNameFirst,
t3.ChildNextFriendMiddleName AS ChildNextFriendNameMiddle,
t3.ChildNextFriendLastName AS ChildNextFriendNameLast,
t3.ChildNextFriendNameSuffix
FROM dbo.ProtectiveOrder AS t0
INNER JOIN (
SELECT MAX(t1.ProtectiveOrderID) AS value
FROM dbo.ProtectiveOrder AS t1
GROUP BY t1.DocketID
) AS t2 ON t0.ProtectiveOrderID = t2.value
LEFT OUTER JOIN dbo.Docket AS t3 ON t3.DocketID = t0.DocketID
LEFT OUTER JOIN dbo.Directory AS t4 ON t4.DirectoryID = t3.ProtectedPartyID
LEFT OUTER JOIN dbo.Directory AS t5 ON t5.DirectoryID = t3.SubjectID
WHERE
(
((t4.LastName LIKE 'smith%') AND (t4.FirstName LIKE 'jane%'))
OR ((t5.LastName LIKE 'smith%') AND (t5.FirstName LIKE 'jane%'))
OR ((t3.ChildNextFriendLastName LIKE 'smith%') AND (t3.ChildNextFriendFirstName LIKE 'jane%'))
OR (
-- ***************
-- THIS GUY KILLS THE QUERY WHEN A TOP IS INTRODUCED IN THE TOP-LEVEL SELECT
-- ***************
EXISTS(
SELECT NULL AS EMPTY
FROM dbo.Child AS t6
WHERE (t6.LastName LIKE 'smith%') AND (t6.FirstName LIKE 'jane%') AND (t6.DocketID = t3.DocketID)
)
)
)
ORDER BY t3.DocketNumber
Override the Skip method and just check the input for zero. For any value but zero call the original skip method. For zero don't.
so if you modify the Skip provided in dynamic.cs you could do:
public static IQueryable Skip(this IQueryable source, int count)
{
if (count == 0)
{
return source;
}
if (source == null) throw new ArgumentNullException("source");
return source.Provider.CreateQuery(
Expression.Call(
typeof(Queryable), "Skip",
new Type[] { source.ElementType },
source.Expression, Expression.Constant(count)));
}

Nhibernate join filtering

I have a question about joins in NHIBERNATE. We had an issue with our sql query that was generated but nhibernate. Our db developer optimized the raw sql so it works as we need, but we need to change the nhibernate code to make generated sql look like optimized.
the part of the original part of the query is:
FROM PERSON_VISIT this_
inner join PERSON_Basic per2_
on this_.PERSON_ID = per2_.PERSON_ID
left outer join PERSONC_QUESTIONS perint10_
on per2_.PERSON_ID = perint10_.PERSON_ID
left outer join TELEPHONE_QUESTIONS intaudit13_
on perint10_.PP_QUESTIONS_ID = intaudit13_.PP_QUESTIONS_ID
inner join C_QUESTIONS intdef14_
on perint10_.QUESTION_ID = intdef14_.QUESTION_ID
and perint10_.QUESTIONS_CODE = intdef14_.QUESTIONS_CODE
and perint10_.QUESTION_ID = intdef14_.QUESTION_ID
The optimized one is :
FROM PERSON_VISIT this_
inner join PERSON_Basic per2_
on this_.PERSON_ID = per2_.PERSON_ID
left outer join PERSONC_QUESTIONS perint10_
on per2_.PERSON_ID = perint10_.PERSON_ID
left outer join TELEPHONE_QUESTIONS intaudit13_
on perint10_.PP_QUESTIONS_ID = intaudit13_.PP_QUESTIONS_ID
left outer join C_QUESTIONS intdef14_
on perint10_.QUESTION_ID = intdef14_.QUESTION_ID
and perint10_.QUESTIONS_CODE = intdef14_.QUESTIONS_CODE
and perint10_.QUESTION_ID = intdef14_.QUESTION_ID
and intdef14_.DISCIPLINE_CODE = this_.DISCIPLINE_CODE
To change query from inner join to left outer join is easy, i changed only one line of code:
.CreateAlias("PersonInt.QuestionEntity", "IntDef", JoinType.LeftOuterJoin)
But how I can add
and intdef14_.DISCIPLINE_CODE = this_.DISCIPLINE_CODE
using nhibernate code?
There is an option to add reference from PERSON_VISIT definition to C_QUESTIONS, but the problem is that
PERSON_VISIT is used everywhere and I don't want this change to possibly break other queries, I just wnat to add only one line of code to add, how I can do that? Is there any way to have access to the raw join to change it? Or some other way to add this
and intdef14_.DISCIPLINE_CODE = this_.DISCIPLINE_CODE
To the query?
I know that somebody will say that we can add a restriction to the query through criteria.Add, but it is not an option cause db developer optimized our query taking this restriction from WHERE clause to the join.
How I can do that quickly without changing the models definitions? Just changing only this one query without changing the whole model?
It is possible using HQL and the Criteria API's.
This question gives you the answer: Adding conditionals to outer joins with nhibernate
Something like this may solve your issue.
.CreateAlias("PersonInt.QuestionEntity", "IntDef", JoinType.LeftOuterJoin,
Restrictions.EqProperty("DISCIPLINE_CODE", "IntDef.DISCIPLINE_CODE"))
Thanks for answers. We use 2.0 version of NHibernate in our project so we didn't have a chance to use new methods of .CreateAlias with restrictions.
I have fixed an issue using Interceptors:
public class SqlInterceptor : EmptyInterceptor, IInterceptor
{
SqlString IInterceptor.OnPrepareStatement(SqlString sql)
{
//manipulating with the sql
return sql;
}
}
than
var factory = Session.SessionFactory;
var session = factory.OpenSession(new SqlInterceptor());
And use my query without a change.

NHibernate Left Outer Join Unrelated Entities

I have 2 entities Role & Translation.
Role -> Role_ID, Code
Translation -> Code, Language, Name
The idea is to say for a certain role, that it has English name, French name and so on.
For example:
A Role(1, 'Rol_001') can have the relations: Translation('Rol_001', 'English', '') & Translation('Rol_001', 'French', '').
I would like to express the following SQL query in HQL:
select r.Role_ID, t.Name
from Role r left outer join Translation t
on r.Code = t.Code and t.Language = #lang;
In my mapping files I don't have any relation between the 2 entities but the following HQL query works as if it is inner join
IQuery query = session.CreateQuery("select new Lookup(r.Role, t.Name) from Role r, Translation t where r.Code = r.Code and t.Language = :language");
If I change the HQL to left outer join, I get the Path expected for join exception.
Can you help me with the following:
1- Do I need to change my mapping files?
2- If I can keep the mapping files as is, how write such a query in HQL?
3- How does HQL really works? Why such a simple outer join query is not working? I must be missing something here!
Edit:
Now I am using the following code based on the suggetion to use CreateSQL:
ISQLQuery query = session.CreateSQLQuery("select m.MedicineTypeID, t.Name, m.IsDeleted from MedicineType m left outer join Translation t on m.Code = t.Code and t.Language = :language");
query.SetString("language", language);
IList rawLookup = query.List();
IList medicineTypesLookup = new List(rawLookup.Count);
foreach (object[] lookup in rawLookup)
{
medicineTypesLookup.Add(new Lookup((int)lookup[0], (string)lookup[1], (bool)lookup[2]));
}
return medicineTypesLookup;
This is working however I want to use query.List() to get the result directly instead of converting it myself.
I tried to use query.AddEntity(typeof(Lookup)); but I get the exception NHibernate.MappingException: No persister for: DAL.Domain.Lookup.
The Lookup is just a POCO and doesn't map to any database table. Its mapping file is simply <import class="Lookup" />
Finally I found the answer:ISession session = NHibernateHelper.Session;
ISQLQuery query = session.CreateSQLQuery("select m.MedicineTypeID as ID, t.Name, m.IsDeleted from MedicineType m left outer join Translation t on m.Code = t.Code and t.Language = :language");
query.setString("language", language);
IList lookup = query.SetResultTransformer(Transformers.AliasToBean()).List();
return lookup;
And the lookup is a POCO class with a parameterless constructor and 3 properties ID, Name and IsDeleted.
I would like to thank Kelly and Diego Mijelshon for their hints. Although they don't provide the full answer the but using Session.CreateSqlQuery() was a very useful hint.
So the complete solution is Session.CreateSQLQuery and query.SetResultTransformer
Note: Transformers.AliasToBean() is so java.
Edit: http://docs.jboss.org/hibernate/orm/3.2/api/org/hibernate/impl/SQLQueryImpl.html for correct method of setString()
You must to define the relationship in the mappings or do a subquery