criteria api sub-select with sub-select and custom fields - jpa

I'd like to rewrite following sql query to criteria api:
select * from demands d
where exists (
select * from (
select a.demandid,
min(coalesce(a.securitylevel, a.securitylevel, -1)) themin,
max(coalesce(a.securitylevel, -1)) themax
from allocations a
group by demandid)
where demandid = d.id
and (themin = -1 and themax = -1
or themax >= 5)
);
The problem is that I'm not able to figure out how to write select clause for sub-queries, my current implementation produces CompoundSelection however subQyer.select accepts only Expression (DemandSecLevel is custom POJO/DTO).
public static Specification<Demand> bySecurityLevel(
final Integer securityLevel) {
return (root, query, cb) -> {
final Subquery<DemandSecLevel> subQuery = query.subquery(DemandSecLevel.class);
final Root<AllocationEntry> allocationEntryRoot = subQuery.from(AllocationEntry.class);
subQuery.select(cb.construct(
DemandSecLevel.class,
allocationEntryRoot.get(AllocationEntry_.incidentDemandId),
cb.min(cb.coalesce(allocationEntryRoot.get(AllocationEntry_.securityLevel), -1))
.alias("minSecLevel"),
cb.max(cb.coalesce(allocationEntryRoot.get(AllocationEntry_.securityLevel), -1))
.alias("maxSecLevel")
));

Related

Covert SQL query to TYPO3 query builder?

How to convert SQL query to TYPO3 query builder.
SELECT * FROM tableName ORDER BY (CASE WHEN DATE(dateColumn) < DATE(GETDATE()) THEN 1 ELSE 0
END) DESC, dateColumn ASC
enter link description here
Same functionality i need in typo3 query builder.
To get the sql query you posted, you can do it like following:
// little helper function to debug querybuilder,
// queries with parameter placeholders
function debugQuery(QueryBuilder $builder)
{
$preparedStatement = $builder->getSQL();
$parameters = $builder->getParameters();
$stringParams = [];
foreach ($parameters as $key => $parameter) {
$stringParams[':' . $key] = $parameter;
}
return strtr($preparedStatement, $stringParams);
}
// get querybuilder
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getConnectionByName('Default')
->createQueryBuilder();
// build query
$queryBuilder
->select('*')
->from('table')
->getConcreteQueryBuilder()->orderBy(
'(CASE WHEN DATE('
.$queryBuilder->quoteIdentifier('dateColumn')
.') < DATE(GETDATE()) THEN 1 ELSE 0 END)',
'DESC'
)
;
$queryBuilder->addOrderBy('dateColumn', 'ASC');
// build query
$sql = debugQuery($queryBuilder);
The generates following sql query:
SELECT
FROM `table`
ORDER BY
(CASE WHEN DATE(`dateColumn`) < DATE(GETDATE()) THEN 1 ELSE 0 END) DESC,
`dateColumn` ASC
Some note beside:
To my knowlage GETDATE() is not a valid mysql method, more MSSQL. Eventually you want CURRENT_DATE() instead.
edit #1
Not tested/run .. just created the sql string to match what you provided. So don't blame me if provided sql query is wrong itself.

int4range, NOT IN (VALUES), ON NOT (with LEFT JOIN) in QueryDSL 4

I would like to know, how to make a int4range / NOT IN (VALUES) / ON NOT (with LEFT JOIN) in QueryDSL 4.
I've writte this SQL request :
SELECT DISTINCT ON (numero_semaine, jour_semaine)
numero_semaine, jour_semaine, heure_debut, heure_fin, id_box
FROM (
SELECT po.* FROM (
(SELECT DISTINCT ON (numero_semaine, jour_semaine)
numero_semaine, jour_semaine, heure_debut, heure_fin, id_box
FROM accueil.semaine_type_box
INNER JOIN accueil.plage_ouverture
ON accueil.plage_ouverture.id_semaine_type = accueil.semaine_type_box.id_semaine_type
INNER JOIN accueil.semaine_type
ON accueil.semaine_type.id = accueil.semaine_type_box.id_semaine_type
INNER JOIN accueil.box
ON accueil.box.id = accueil.semaine_type_box.id_box
WHERE semaine_type_box.numero_semaine >= 48
AND semaine_type_box.numero_semaine <= 52
AND (numero_semaine, jour_semaine)
---->NOT IN (VALUES (48,1), (48,2), (48,3), (48,4), (52,6), (52,7))<----
AND semaine_type.site = 'UR130'
AND box.ouvert_cotisant = TRUE
ORDER BY numero_semaine, jour_semaine, heure_debut
)
UNION ALL
(SELECT DISTINCT ON (numero_semaine, jour_semaine)
extract(week from plage_exceptionnelle.date) as numero_semaine,
extract(isodow from plage_exceptionnelle.date) as jour_semaine,
heure_debut, heure_fin, id_box
FROM accueil.plage_exceptionnelle
INNER JOIN accueil.box ON
accueil.box.id= accueil.plage_exceptionnelle.id_box
WHERE plage_exceptionnelle.date >= '2018-11-30'
AND plage_exceptionnelle.date <= '2018-12-28'
AND ouverte = TRUE
AND box.site = 'UR130'
AND box.ouvert_cotisant = TRUE
ORDER BY numero_semaine, jour_semaine, heure_debut
)
) po
LEFT JOIN (
SELECT
extract(week from plage_bloquee.date) as numero_semaine,
extract(isodow from plage_bloquee.date) as jour_semaine,
heure_debut, heure_fin, id_box
FROM accueil.plage_bloquee
INNER JOIN accueil.box ON accueil.box.id = accueil.plage_bloquee.id_box
WHERE box.site = 'UR130'
AND box.ouvert_cotisant = TRUE
) pb
---->ON NOT(po.jour_semaine = pb.jour_semaine<----
---->AND int4range(po.heure_debut, po.heure_fin) && int4range(pb.heure_debut, pb.heure_fin)<----
AND po.id_box = pb.id_box
AND po.numero_semaine = pb.numero_semaine
)
WHERE pb.id_box IS NOT NULL
) end_table
ORDER BY numero_semaine,jour_semaine,heure_debut
What is the way to do this SQL request ?
I've writte this, but I don't find the way to writte a not in, on not with left join, and int4range doesn't exist with QueryDSL 4 :
public List<PlageDisponibleWS> findDayAvailableDao(String organisme, String site, MediaEnum media) {
final LocalDate startDate = LocalDate.now();
final short startWeekOfWeekYear = (short) startDate.getWeekOfWeekyear();
final short startDay = (short) startDate.getDayOfWeek();
final LocalDate endDate = startDate.plusMonths(1);
final short endWeekOfWeekYear = (short) endDate.getWeekOfWeekyear();
final int MONDAY_START_DAY = 1;
final int TUESDAY_START_DAY = 2;
final int WEDNESDAY_START_DAY = 3;
final int THURSDAY_START_DAY = 4;
final int FRIDAY_START_DAY = 5;
final int SATURDAY_START_DAY = 6;
final int SUNDAY_START_DAY = 7;
PostgreSQLQuery<Tuple> plagesOuvertes = queryFactory
.select(qSemaineTypeBox.numeroSemaine, qSemaineTypeBox.idBox, qPlageOuverture.jourSemaine,
qPlageOuverture.heureDebut, qPlageOuverture.heureFin)
.distinctOn(qSemaineTypeBox.numeroSemaine, qPlageOuverture.jourSemaine).from(qSemaineTypeBox)
.innerJoin(qPlageOuverture).on(qPlageOuverture.idSemaineType.eq(qSemaineTypeBox.idSemaineType))
.innerJoin(qSemaineType).on(qSemaineType.id.eq(qSemaineTypeBox.idSemaineType)).innerJoin(qBox)
.on(qBox.id.eq(qSemaineTypeBox.idBox)).where(qSemaineTypeBox.numeroSemaine.goe(startWeekOfWeekYear))
.where(qSemaineTypeBox.numeroSemaine.loe(endWeekOfWeekYear))
// NOT IN (VALUES (startWeekOfWeekYear,MONDAY_START_DAY), (startWeekOfWeekYear,TUESDAY_START_DAY),
// (startWeekOfWeekYear,WEDNESDAY_START_DAY), (startWeekOfWeekYear,THURSDAY_START_DAY),
// (endWeekOfWeekYear,SATURDAY_START_DAY), (endWeekOfWeekYear,SUNDAY_START_DAY)) ??
.where(qSemaineType.site.eq(site)).where(qBox.ouvertCotisant.eq(true))
.orderBy(qSemaineTypeBox.numeroSemaine.asc(), qPlageOuverture.jourSemaine.asc(),
qPlageOuverture.heureDebut.asc());
PostgreSQLQuery<Tuple> plagesExceptionnelles = queryFactory
.select(qPlageExceptionnelle.date.week().as("numero_semaine"),
qPlageExceptionnelle.date.dayOfWeek().as("jour_semaine"), qPlageExceptionnelle.heureDebut,
qPlageExceptionnelle.heureFin, qPlageExceptionnelle.idBox)
.distinctOn(qSemaineTypeBox.numeroSemaine, qPlageOuverture.jourSemaine).from(qPlageExceptionnelle)
.innerJoin(qBox).on(qBox.id.eq(qPlageExceptionnelle.idBox))
.where(qPlageExceptionnelle.date.goe(startDate)).where(qPlageExceptionnelle.date.loe(endDate))
.where(qPlageExceptionnelle.ouverte.eq(true)).where(qBox.site.eq(site))
.where(qBox.ouvertCotisant.eq(true)).orderBy(qSemaineTypeBox.numeroSemaine.asc(),
qPlageOuverture.jourSemaine.asc(), qPlageOuverture.heureDebut.asc());
PostgreSQLQuery<Tuple> plagesBloquees = queryFactory
.select(qPlageBloquee.date.week().as("numero_semaine"),
qPlageBloquee.date.dayOfWeek().as("jour_semaine"), qPlageBloquee.heureDebut,
qPlageBloquee.heureFin, qPlageBloquee.idBox)
.from(qPlageBloquee).innerJoin(qBox).on(qBox.id.eq(qPlageBloquee.idBox)).where(qBox.site.eq(site))
.where(qBox.ouvertCotisant.eq(true));
#SuppressWarnings("unchecked")
Expression<Tuple> unionSubQuery = queryFactory.query().unionAll(plagesOuvertes, plagesExceptionnelles)
.as("po");
final PathBuilder<Object> aliasPb = new PathBuilder<>(Object.class, "pb");
PostgreSQLQuery<Tuple> leftJoinSubQuery = queryFactory.select(unionSubQuery)
.leftJoin(plagesBloquees, aliasPb)
// ON NOT
// INT4RANGE
;
final PathBuilder<Object> aliasEnd = new PathBuilder<>(Object.class, "end_table");
query = queryFactory
.select(Projections.constructor(PlageDisponibleWS.class, qSemaineTypeBox.numeroSemaine,
qSemaineTypeBox.idBox, qPlageOuverture.jourSemaine, qPlageOuverture.heureDebut,
qPlageOuverture.heureFin))
.distinctOn(qSemaineTypeBox.numeroSemaine, qPlageOuverture.jourSemaine)
.from(leftJoinSubQuery, aliasEnd).orderBy(qSemaineTypeBox.numeroSemaine.asc(),
qPlageOuverture.jourSemaine.asc(), qPlageOuverture.heureDebut.asc())
.fetch();
return query;
}
So please have you any idea to solve my problems ?
Thanks a lot in advance.
For "in values" construction with multiple columns you can try this:
.where(list(table.column1, table.column2).
in(list(select(constant("aaa"), constant("bbb")),
select(constant("ccc"), constant("ddd")),
select(constant("eee"), constant("fff")))))
Function "list" and "select comes from these imports:
import static com.querydsl.core.types.dsl.Expressions.*;
import static com.querydsl.sql.SQLExpressions.*;
For "left join + not", try this:
.from(table1)
.leftJoin(table2)
.on(table1.id.ne(table2.id))
Unfortunatly I am not familar with int4range function, hovewer you can try to use template expression for custom database functions:
Expressions.template(Integer.class, "custom_db_function({0}, {1})", 10, 20);

JPA Criteria orderBy: unexpected AST node

I have the following criteria query, which retrieves some fields from Anfrage and Sparte entities and also the translated string for the sparte.i18nKey.
This works as expected if I dont use orderBy.
Now I have the requirement to sort by the translated string for sparte.i18nKey and using the orderBy as shown below, results in QuerySyntaxException: unexpected AST node
So the problem must be the subselect in the orderBy clause!
select distinct new
my.domain.model.dto.AnfrageDTO(
anfrage0.id,
anfrage0.name,
anfrage0.sparte.id,
anfrage0.sparte.i18nKey,
-- retrieve translated string for sparte.i18nKey
(select rb0.value from at.luxbau.mis2.domain.model.ResourceBundleEntity as rb0
where (anfrage0.sparte.i18nKey = rb0.key) and (rb0.language = 'de'))
)
from my.domain.model.impl.Anfrage as anfrage0
left join anfrage0.sparte as sparte
order by (
-- sort by translated string for sparte.i18nKey
select rb1.value
from my.domain.model.ResourceBundleEntity as rb1
where (anfrage0.sparte.i18nKey = rb1.key) and (rb1.language = 'de')
) asc
My Java code looks like this:
private List<AnfrageDTO> getAnfragen() {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<AnfrageDTO> query = cb.createQuery(AnfrageDTO.class);
Root<Anfrage> anfrage = query.from(Anfrage.class);
anfrage.join(Anfrage_.sparte, JoinType.LEFT);
query.select(cb.construct(AnfrageDTO.class,
anfrage.get(Anfrage_.id),
anfrage.get(Anfrage_.name),
anfrage.get(Anfrage_.sparte).get(Sparte_.id),
anfrage.get(Anfrage_.sparte).get(Sparte_.i18nKey),
// create subquery for translated sparte.i18nKey
createResourceBundleSubQuery(cb, query, anfrage.get(Anfrage_.sparte).get(Sparte_.i18nKey)).getSelection()));
TypedQuery<AnfrageDTO> tq = entityManager
.createQuery(query)
// use subquery to sort by translated sparte.i18nKey
.orderBy(cb.asc(createResourceBundleSubQuery(cb, query, anfrage.get(Anfrage_.sparte).get(Sparte_.i18nKey))));
tq.setMaxResults(10);
List<AnfrageDTO> anfragen = tq.getResultList();
return anfragen;
}
public Subquery<String> createResourceBundleSubQuery(CriteriaBuilder cb, CriteriaQuery<?> query, <String> expr) {
Subquery<String> subquery = query.subquery(String.class);
Root<ResourceBundleEntity> rb = subquery.from(ResourceBundleEntity.class);
subquery
.select(rb.get(ResourceBundleEntity_.value))
.where(cb.and(
cb.equal(expr, rb.get(ResourceBundleEntity_.key)),
cb.equal(rb.get(ResourceBundleEntity_.language), "de")));
return subquery;
}
Using a native SQL query with subselect in orderBy works also as expected.
select distinct
anfrage0_.id,
anfrage0_.name,
anfrage0_.sparte_id,
sparte4_.i18n_key,
(select rb3.i18n_value from resource_bundle rb3 where rb3.language_code = 'de' and rb3.i18n_key = sparte4_.i18n_key) as sparte_i18n_value
from
mis2.anfrage anfrage0_
left outer join mis2.sparte sparte4_ on anfrage0_.sparte_id = sparte4_.id
order by (
select rb.i18n_value
from mis2.resource_bundle rb
where sparte4_.i18n_key = rb.i18n_key and rb.language_code = 'de'
) asc
Also using an alias in the native SQL query works also as expected.
select distinct
anfrage0_.id,
anfrage0_.name,
anfrage0_.sparte_id,
sparte4_.i18n_key,
(select rb3.i18n_value from resource_bundle rb3 where rb3.language_code = 'de' and rb3.i18n_key = sparte4_.i18n_key) as sparte_i18n_value
from
mis2.anfrage anfrage0_
left outer join mis2.sparte sparte4_ on anfrage0_.sparte_id = sparte4_.id
order by sparte_i18n_value
asc
It would be great if JPA Criteria API would support using an alias in orderBy clause!
Any hints welcome - Thank you!
My environment is WildFly 11 and PostgreSQL 9.6.
JPA doesn't support passing parameter in order by clause, your problem has been asked before: Hibernate Named Query Order By parameter

Kentico document query API always returns empty result

I have the following query:
var newsItems = tree.SelectNodes()
.Types(pageTypesArray)
.Path(path)
.OrderBy(orderBy)
.CombineWithDefaultCulture(false)
.Page(page, count)
.OnCurrentSite()
.NestingLevel(-1)
.Culture(CurrentDocument.DocumentCulture)
.InCategories(categories)
.TopN(topN)
.Where("ListableDocumentImage is not null AND ListableDocumentImage != ''")
.Columns(columns);
Which translates like so:
WITH AllData AS
(
SELECT TOP 6 * for brevity, ROW_NUMBER() OVER (ORDER BY [NewsOccurrenceDate] DESC) AS [CMS_RN]
FROM View_CMS_Tree_Joined AS V WITH (NOLOCK, NOEXPAND) INNER JOIN SOS_News AS C WITH (NOLOCK) ON [V].[DocumentForeignKeyValue] =
[C].[NewsID] AND V.ClassName = N'News' LEFT OUTER JOIN COM_SKU AS S WITH (NOLOCK) ON [V].[NodeSKUID] = [S].[SKUID]
WHERE [NodeSiteID] = #NodeSiteID AND (([DocumentCanBePublished] = 1 AND ([DocumentPublishFrom] IS NULL OR [DocumentPublishFrom] <= #Now)
AND ([DocumentPublishTo] IS NULL OR [DocumentPublishTo] >= #Now))
AND [NodeAliasPath] LIKE #NodeAliasPath AND [DocumentCulture] = #DocumentCulture
**AND 0 = 1)** <<<------------------- WHERE DID THIS COME FROM?????
)
SELECT *, (SELECT COUNT(*) FROM AllData) AS [CMS_TOT]
FROM AllData
WHERE CMS_RN BETWEEN 4 AND 6
ORDER BY CMS_RN
Has anyone ever come across anything like this before? I can't figure out why they're sticking in the AND 0=1 in my where clause.
Properly structuring your document API call will help with this. In your query results, you can see your WHERE condition is not even being added so it does make a difference the order of the methods being called.
For instance:
var newsItems = tree.SelectNodes()
.Types(pageTypesArray)
.Path(path)
.Where("ListableDocumentImage is not null AND ListableDocumentImage != ''")
.TopN(topN)
.OrderBy(orderBy)
.CombineWithDefaultCulture(false)
.Page(page, count)
.NestingLevel(-1)
.Culture(CurrentDocument.DocumentCulture)
.InCategories(categories)
.OnCurrentSite()
.Columns(columns);

Orientdb - How do Filter a Match with an Edge Property

Look the Match
SELECT PSQ_psq_nome AS nome, INS_ins_nome AS instituicao, COUNT(PUB_pub_id) AS qtdpub, * FROM (
MATCH
{class:Pais, as:PAI, where:(pai_id=1)} <-NASCEU- {class:Pesquisador, as:PSQ} -PUBLICOU-> {class:Publicacao, as:PUB, where: (pub_data_publicacao_int > 20141231)},
{as:PSQ} -ATUOU-> {class:Instituicao, as:INS}
RETURN PSQ.psq_nome AS nome, INS.ins_nome AS instituicao, PUB.pub_id, PUBLICOU.ordem )
GROUP BY PSQ_psq_nome, INS_ins_nome
ORDER BY qtdpub DESC, nome
I need use the property ordem, type integer, for the edge PUBLICOU. Is it possible?
something like (see PUBLICOU)
SELECT PSQ_psq_nome AS nome, INS_ins_nome AS instituicao, COUNT(PUB_pub_id) AS qtdpub, * FROM (
MATCH
{class:Pais, as:PAI, where:(pai_id=1)} <-NASCEU- {class:Pesquisador, as:PSQ} -PUBLICOU { where: (ordem = 1) -> {class:Publicacao, as:PUB, where: (pub_data_publicacao_int > 20141231)},
{as:PSQ} -ATUOU-> {class:Instituicao, as:INS}
RETURN PSQ.psq_nome AS nome, INS.ins_nome AS instituicao, PUB.pub_id, PUBLICOU.ordem )
GROUP BY PSQ_psq_nome, INS_ins_nome
ORDER BY qtdpub DESC, nome
Sure, but you cannot use arrow notation, eg. you have to replace
{class:Pesquisador, as:PSQ} -PUBLICOU-> {class:Publicacao ...}
with
{class:Pesquisador, as:PSQ} .outE("PUBLICOU"){where:(ordem = 1)}.inV() {class:Publicacao ...}
You can also assign an alias to the edge and return it in the result set if you wish