Learn to configure the <subquery>
element for Pull Reports™ Ad Hoc
report and data service software.
Catalog Configuration Java API analog:
SubqueryProvider, TableConfigurationBuilder#setSubqueryProvider
The <subquery>
element specifies a query to represent the
parent <table>
within the Export Report REST API SQL from
clause
instead of the parent <table>
's name
attribute. The <subquery>
value may be dynamically generated through the use
of a
Freemarker template or
factory class.
<subquery>
usageTo demonstrate <subquery>
element usage, take with
this simple XML Catalog file which specifies a base <table>
named order
containing two <column>
s: item_number
and item_name
.
<?xml version="1.0" encoding="UTF-8"?> <catalog xmlns="http://www.pullreports.com/catalog-1.6.1" id="customer" name="Customer Reports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.pullreports.com/catalog-1.6.1 https://www.pullreports.com/docs/xsd/pullreports-catalog-1.6.1.xsd"> <report id="order" name="Customer Orders"> <table id="order" name="cust_order" displayName="Order"> <column id="item_number" name="item_number" displayName="Item Number" paramType="java.lang.Long"/> <column id="item_name" name="item_name" displayName="Item Name"/> </table> </report> </catalog>
In this example, the following HTTP request to the Export Report REST API results in the following SQL query.
/[context]/pullreports/catalog/customer/report/order/export
select t1.item_number,t1.item_name from cust_order t1
Using a <subquery>
permits more flexible query definition than does the <table>
name
attribute.
For example, if another table in the database called canceled_orders
contains those orders which
are canceled, the following catalog configuration uses <subquery>
to exclude the canceled orders from the
export results.
<?xml version="1.0" encoding="UTF-8"?> <catalog xmlns="http://www.pullreports.com/catalog-1.6.1" id="customer" name="Customer Reports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.pullreports.com/catalog-1.6.1 https://www.pullreports.com/docs/xsd/pullreports-catalog-1.6.1.xsd"> <report id="order" name="Customer Orders"> <table id="order" displayName="Order"> <subquery> select o.item_number,o.item_name from cust_order o left outer join order_canceled oc on o.id = oc.id where oc.id is null </subquery> <column id="item_number" name="item_number" displayName="Item Number" paramType="java.lang.Long"/> <column id="item_name" name="item_name" displayName="Item Name"/> </table> </report> </catalog>
The <subquery>
select clause must contain all the <column>
children of the parent <table>
.
The following example is an identical Catalog Configuration Java API configuration.
package com.pullreports.examples.subquery; import com.pullreports.model.CatalogId; import com.pullreports.model.ColumnId; import com.pullreports.model.ParamType; import com.pullreports.model.ReportId; import com.pullreports.model.Subquery; import com.pullreports.model.SubqueryProvider; import com.pullreports.model.TableId; import com.pullreports.model.config.CatalogConfiguration; import com.pullreports.model.config.CatalogConfigurationFactory; import com.pullreports.model.config.ColumnConfiguration; import com.pullreports.model.config.ReportConfiguration; import com.pullreports.model.config.TableConfiguration; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Optional; public class BasicSubqueryCatalogConfigurationFactory implements CatalogConfigurationFactory { @Override public CatalogConfiguration makeCatalog(ServletContext servletContext) { ColumnConfiguration itemNumberColumnConfiguration = new ColumnConfiguration.Builder( new ColumnId("item_number"),"item_number") .setDisplayName("Item Number").setParamType(ParamType.LONG).build(); ColumnConfiguration itemNameColumnConfiguration = new ColumnConfiguration.Builder( new ColumnId("item_name"),"item_name") .setDisplayName("Item Name").build(); List<ColumnConfiguration> columnConfigurations = Arrays.asList( itemNumberColumnConfiguration,itemNameColumnConfiguration); TableConfiguration orderTableConfiguration = new TableConfiguration.Builder( new TableId("order"),"Order",columnConfigurations) .setSubqueryProvider(new OrderSubqueryProvider()).build(); ReportConfiguration orderReportConfiguration = new ReportConfiguration.Builder( new ReportId("order"),"Customer Orders",orderTableConfiguration).build(); List<ReportConfiguration> reportConfigurations = Collections.singletonList(orderReportConfiguration); return new CatalogConfiguration(new CatalogId("customer"),"Customer Reports",reportConfigurations); } private final static class OrderSubqueryProvider implements SubqueryProvider { private static final String QUERY = "select o.item_number,o.item_name " + "from cust_order o left outer join order_canceled oc on o.id = oc.id " + "where oc.id is null"; @Override public Subquery createSubquery(HttpServletRequest hsr) { return new Subquery(){ @Override public Optional<String> getValue(){ return Optional.of(QUERY); } }; } } }
Now, the same HTTP request to the Export Report REST API results in the following SQL query. Note the embedded subquery in the SQL from clause.
/[context]/pullreports/catalog/customer/report/order/export
select t1.item_number,t1.item_name from (select o.item_number,o.item_name from cust_order o left outer join order_canceled oc on o.id = oc.id where oc.id is null) t1
<description>
to a <subquery>
Adding a child <description>
element to a <subquery>
will
output that description within Export Report REST API
if the parent <table>
is included in the
columns
parameter. A child <description>
element
is typically used to communicate the subquery behavior to Export Report REST API users.
See the
format
parameter documentation for how each export format outputs
<subquery>
descriptions.
<?xml version="1.0" encoding="UTF-8"?> <catalog xmlns="http://www.pullreports.com/catalog-1.6.1" id="customer" name="Customer Reports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.pullreports.com/catalog-1.6.1 https://www.pullreports.com/docs/xsd/pullreports-catalog-1.6.1.xsd"> <report id="order" name="Customer Orders"> <table id="order" displayName="Order"> <subquery> <description>Only orders that are not canceled are present in the export result.</description> select o.item_number,o.item_name from cust_order o left outer join order_canceled oc on o.id = oc.id where oc.id is null </subquery> <column id="item_number" name="item_number" displayName="Item Number" paramType="java.lang.Long"/> <column id="item_name" name="item_name" displayName="Item Name"/> </table> </report> </catalog>
The following example is an identical Catalog Configuration Java API configuration.
package com.pullreports.examples.subquery; import com.pullreports.model.CatalogId; import com.pullreports.model.ColumnId; import com.pullreports.model.ParamType; import com.pullreports.model.ReportId; import com.pullreports.model.Subquery; import com.pullreports.model.SubqueryProvider; import com.pullreports.model.TableId; import com.pullreports.model.config.CatalogConfiguration; import com.pullreports.model.config.CatalogConfigurationFactory; import com.pullreports.model.config.ColumnConfiguration; import com.pullreports.model.config.ReportConfiguration; import com.pullreports.model.config.TableConfiguration; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Optional; public class BasicSubqueryWithDescriptionCatalogConfigurationFactory implements CatalogConfigurationFactory { @Override public CatalogConfiguration makeCatalog(ServletContext servletContext) { ColumnConfiguration itemNumberColumnConfiguration = new ColumnConfiguration.Builder( new ColumnId("item_number"),"item_number") .setDisplayName("Item Number").setParamType(ParamType.LONG).build(); ColumnConfiguration itemNameColumnConfiguration = new ColumnConfiguration.Builder( new ColumnId("item_name"),"item_name") .setDisplayName("Item Name").build(); List<ColumnConfiguration> columnConfigurations = Arrays.asList( itemNumberColumnConfiguration,itemNameColumnConfiguration); TableConfiguration orderTableConfiguration = new TableConfiguration.Builder( new TableId("order"),"Order",columnConfigurations) .setSubqueryProvider(new OrderSubqueryProvider()).build(); ReportConfiguration orderReportConfiguration = new ReportConfiguration.Builder( new ReportId("order"),"Customer Orders",orderTableConfiguration).build(); List<ReportConfiguration> reportConfigurations = Collections.singletonList(orderReportConfiguration); return new CatalogConfiguration(new CatalogId("customer"),"Customer Reports",reportConfigurations); } private static final class OrderSubqueryProvider implements SubqueryProvider { private static final String QUERY = "select o.item_number,o.item_name " + "from cust_order o left outer join order_canceled oc on o.id = oc.id " + "where oc.id is null"; private static final String DESCRIPTION = "Only orders that are not canceled are present in the export result."; @Override public Subquery createSubquery(HttpServletRequest hsr) { return new Subquery(){ @Override public Optional<String> getValue(){ return Optional.of(QUERY); } @Override public Optional<String> getDescription(){ return Optional.of(DESCRIPTION); } }; } } }
<subquery>
with a Freemarker Template
Customize the <subquery>
element as a
Freemarker template.
Additionally, use methods provided by
RequestMethodModels
to access methods from the
HttpServletRequest
associated with the Export Report REST API.
In the following <subquery>
example, the subquery uses an
HttpSession
attribute set by external-to-Pull Reports™ configuration. For instance,
HttpSessionListener
s
are commonly used to set HttpSession
attributes.
<?xml version="1.0" encoding="UTF-8"?> <catalog xmlns="http://www.pullreports.com/catalog-1.6.1" id="customer" name="Customer Reports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.pullreports.com/catalog-1.6.1 https://www.pullreports.com/docs/xsd/pullreports-catalog-1.6.1.xsd"> <report id="order" name="Customer Orders"> <table id="order" displayName="Order"> <subquery> select o.item_number,o.item_name from cust_order o where o.user_id = ${getSessionAttribute('user.id')} </subquery> <column id="item_number" name="item_number" displayName="Item Number" paramType="java.lang.Long"/> <column id="item_name" name="item_name" displayName="Item Name"/> </table> </report> </catalog>
Assuming the user.id
session attribute has a value of 100
,
the following request to the Export Report REST API results in the following SQL query.
/[context]/pullreports/catalog/customer/report/order/export
select t1.item_number,t1.item_name
from
(select o.item_number,o.item_name
from cust_order o
where
o.user_id = 100) t1
<subquery>
with a provider class
If a Freemarker template is not expressive enough to create the <subquery>
value,
use the provider
attribute to specify the fully qualified name of
a Java class which implements SubqueryProvider to create the <subquery>
.
In the following <subquery>
example, the <table>
element specifies
a name
attribute that will be used within the Export Report REST API SQL from
clause
if the createSubquery
method of the SubqueryProvider class returns null.
<?xml version="1.0" encoding="UTF-8"?> <catalog xmlns="http://www.pullreports.com/catalog-1.6.1" id="customer" name="Customer Reports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.pullreports.com/catalog-1.6.1 https://www.pullreports.com/docs/xsd/pullreports-catalog-1.6.1.xsd"> <report id="order" name="Customer Orders"> <table id="order" name='cust_order' displayName="Order"> <subquery provider='com.pullreports.examples.subquery.OrderSubqueryProvider'/> <column id="item_number" name="item_number" displayName="Item Number" paramType="java.lang.Long"/> <column id="item_name" name="item_name" displayName="Item Name"/> </table> </report> </catalog>
package com.pullreports.examples.subquery; import com.pullreports.model.Subquery; import com.pullreports.model.SubqueryProvider; import javax.servlet.http.HttpServletRequest; import java.util.Optional; public class OrderSubqueryProvider implements SubqueryProvider { @Override public Subquery createSubquery(HttpServletRequest request){ return new Subquery(){ @Override public Optional<String> getDescription() { if (request.isUserInRole("SUPERUSER")){ return Optional.empty(); } else { return Optional.of("The result is restricted to your orders."); } } @Override public Optional<String> getValue() { if (request.isUserInRole("SUPERUSER")){ // Superusers may see all records. Returning empty will use the // <table>'s name attribute. return Optional.empty(); } else { return Optional.of("select o.item_number,o.item_name " + "from cust_order o " + "where " + "o.user_id = " + request.getSession().getAttribute("user.id")); } } }; } }
<description> ? |
<table> |
The fully qualified name of a Java class which implements SubqueryProvider to create the subquery value.
If the provider
attribute is specified, the <subquery>
element may not have content.