Page providers allow retrieving items with pagination facilities, they can be used in a non-UI context like event listeners or core services.
Standard Page Providers
Page provider offers many advantages comparing to hard coded NXQL queries:
- Pagination logics do not have to be coded again, the
AbstractPageProvider
implementation already offers corresponding logics. - Code duplication can be prevented, by re-using registered page providers.
- Overriding an existing page provider definition is easy.
- The default implementation (
CoreQueryDocumentPageProvider
) that handles Nuxeo documents can be switched from VCS (database) to Elasticsearch by just setting a configuration in thenuxeo.conf
file.
- When using Elasticsearch, page providers support aggregation features.
Here is a sample page provider definition:
<extension target="org.nuxeo.ecm.platform.query.api.PageProviderService"
point="providers">
<coreQueryPageProvider name="TREE_CHILDREN_PP">
<pattern>
SELECT * FROM Document WHERE ecm:parentId = ? AND ecm:isProxy = 0 AND
ecm:mixinType = 'Folderish' AND ecm:mixinType != 'HiddenInNavigation'
AND ecm:isVersion = 0 AND ecm:isTrashed = 0
</pattern>
<sort column="dc:title" ascending="true" />
<pageSize>50</pageSize>
</coreQueryPageProvider>
</extension>
A typical usage of this page provider would be:
PageProviderService ppService = Framework.getService(PageProviderService.class);
Map<String, Serializable> props = new HashMap<>();
props.put(CoreQueryDocumentPageProvider.CORE_SESSION_PROPERTY, (Serializable) coreSession);
PageProvider<DocumentModel> pp = (PageProvider<DocumentModel>) ppService.getPageProvider(
"TREE_CHILDREN_PP", null, null, null, props,
new Object[] { myDoc.getId() });
List<DocumentModel> documents = pp.getCurrentPage();
Here you can see that the page provider properties (needed for the query to be executed) and its parameters (needed for the query to be built) cannot be resolved from EL expressions: they need to be given explicitly to the page provider service.
There is also a syntax to reference "named parameters" in the page provider fixed part. This is mostly useful when working with page providers from the Search Endpoints. You can also find extensive test cases in the code.
Available Parameters
parameter and property Elements
The coreQueryPageProvider
element accepts any number of property elements, defining needed context variables for the page provider to perform its work. The coreSession
property is mandatory for a core query to be processed and is bound to the core session proxy named documentManager
available in a default Nuxeo application.
It also accepts any number of parameter
elements, where order of definition matters: this EL expression will be resolved when performing the query, replacing the ?
characters it holds.
The main difference between properties
and parameters
is that properties will not be recomputed when refreshing the provider, whereas parameters will be. Properties will only be recomputed when resetting the provider.
sort Element
The sort
element defines the default sort, that can be changed later through the interface. There can be any number of sort
elements. The sortInfosBinding
element can also be defined: it can resolve an EL expression in case the sort infos are held by a third party instance (document, Seam component...) and will be used instead of the default sort information if not null or empty. The EL expression can either resolve to a list of org.nuxeo.ecm.core.api.SortInfo
instances, or a list of map items using keys sortColumn
(with a String value) and sortAscending
(with a boolean value).
quickFilter Element
Since version 8.10, the quickFilter
element enables to refine the results obtained by a search. There can be any number of quickFilter
elements. Each quick filter is composed of a clause
element which enables to extend the query, and additional sort
elements.
The quick filters appears in the interface as buttons where each action on a button enables or disables its associated quick filter.
In the previous example, activating "myQuickFilter" will display the children of the current document with the titles "Title1" or "Title2". The search results will be ordered by creator.
pageSize Element
The pageSize
element defines the default page size, it can also be changed later. The pageSizeBinding
element can also be defined: it can resolve an EL expression in case the page size is held by a third party instance (document, Seam component...), and will be used instead of the default page size if not null.
The optional maxPageSize
element can be placed at the same level than pageSize
. It makes it possible to define the maximum page size so that the content view does not overload the server when retrieving a large number of items. When not set, the default value "1000" will be used: even when asking for all the results with a page size with value "0" (when exporting the content view in CSV format for instance), only 1000 items will be returned. This is configurable by contributing the property nuxeo.pageprovider.default-max-page-size
to the Configuration service.
maxResults Element
To set this limit you need to add a maxResults
parameter to coreQueryPageProvider
, either using an integer value or one of the following keywords:
DEFAULT_NAVIGATION_RESULTS
: Used by most of the navigation page provider. The default is 200 and it can be overridden by contributing the propertyorg.nuxeo.ecm.platform.query.nxql.defaultNavigationResults
to the Configuration service.PAGE_SIZE
: this is useful when you are interested in a single page or if you don't need a total count.
maxResults
limit is not taken in account because the total number of results is always available.
For performance reason by default Elasticsearch does not allow you to do unlimited deep scrolling on results.
Only the index.max_result_window
results (which defaults to 10000) are accessible. Content view will not allow you to access pages out of this window to prevent errors.
If you change the Elasticsearch configuration you can adapt the Nuxeo limit by contributing the property org.nuxeo.elasticsearch.provider.maxResultWindow
to the Configuration service.
whereClause Element
This kind of core query can also perform a more complex form of query, using a document model to store query parameters. Using a document model makes it easy to:
- use a layout to display the form that will define query parameters;
- save this document in the repository, so that the same query can be replayed when viewing this document.
Here is an example of such a registration:
<coreQueryPageProvider>
<property name="coreSession">#{documentManager}</property>
<property name="maxResults">DEFAULT_NAVIGATION_RESULTS</property>
<whereClause docType="AdvancedSearch">
<predicate parameter="dc:title" operator="FULLTEXT">
<field schema="advanced_search" name="title" />
</predicate>
<predicate parameter="dc:created" operator="BETWEEN">
<field schema="advanced_search" name="created_min" />
<field schema="advanced_search" name="created_max" />
</predicate>
<predicate parameter="dc:modified" operator="BETWEEN">
<field schema="advanced_search" name="modified_min" />
<field schema="advanced_search" name="modified_max" />
</predicate>
<predicate parameter="dc:language" operator="LIKE">
<field schema="advanced_search" name="language" />
</predicate>
<predicate parameter="ecm:currentLifeCycleState" operator="IN">
<field schema="advanced_search" name="currentLifeCycleStates" />
</predicate>
<fixedPart>
ecm:parentId = ? AND ecm:isVersion = 0 AND ecm:mixinType !=
'HiddenInNavigation' AND ecm:isTrashed = 0
</fixedPart>
</whereClause>
<parameter>#{currentDocument.id}</parameter>
<sort column="dc:title" ascending="true" />
<pageSize>20</pageSize>
</coreQueryPageProvider>
The above definition holds a whereClause
element, stating the search document type and predicates explaining how the document model properties will be translated into a NXQL query. It can also state a fixedPart
element that will added as to the query string. This fixed part can also take parameters using the '?' character and parameter
elements. It can also accept "named parameters", e.g. parameters prefixed by a comma, that can also match the search document model properties (for instance: SELECT * FROM Document WHERE [dc:title](http://dctitle) = :[searchdoc:title](http://searchdoctitle)
).
The fixedPart
element also accepts attributes to better control its behavior:
statement
makes is possible to declare the select statement to use (as contrary to thepattern
element, thefixedPart
element is not supposed to hold the select statement). This optional parameter default to "Select * from document" for default core page providers.escapeParameters
is a boolean value that allows to avoid escaping parameters when building the fixed part (defaults to true).quoteParameters
is a boolean value that allows to avoid adding quotes around the parameter (defaults to true). This is useful when parameters actually hold a complete predicate, for instance (and in this case, theescapeParameters
element must also be set to true.
Note that when using an Elasticsearch page provider you can use NXQL hints inside predicates like this:
<predicate hint="ES: INDEX(dc:title,dc:description)" parameter="dc:title" operator="FULLTEXT">
<field schema="advanced_search" name="fulltext_all"/>
</predicate>
Attributes escapeParameters
and quoteParameters
are also accepted on the pattern
element.
It might be useful to add the ContentViewDisplay facet (this facet includes the content_view_display
schema, using the cvd
prefix to the definition of the AdvandedSearch
document type, when configuring one of the elements described below: pageSizeBinding
, sortInfosBinding
, resultColumns
or resultLayout
.
searchDocumentType Element
The searchDocumentType element is an alternative way to define the search document type to use on the whereClause element. It's been made available to make it possible to define such a search document model, even if no whereClause is defined. This can be useful when defining aggregates without any other filtering, for instance.
Sample usage:
<coreQueryPageProvider>
<property name="coreSession">#{documentManager}</property>
<searchDocumentType>AdvancedSearch</searchDocumentType>
</coreQueryPageProvider>
Custom Page Providers
The <coreQueryPageProvider>
element makes it possible to answer to most common use cases. If you would like to use another kind of query, you can use an alternate element and specify the PageProvider
class to use.
Here is a sample example of a custom page provider configuration:
<extension point="providers" target="org.nuxeo.ecm.platform.query.api.PageProviderService">
<genericPageProvider name="CUSTOM_PAGE_PROVIDER"
class="org.nuxeo.ecm.platform.query.nxql.CoreQueryAndFetchPageProvider">
<property name="coreSession">#{documentManager}</property>
<pattern>
SELECT dc:title FROM Document WHERE ecm:parentId = ? AND
ecm:isVersion = 0 AND ecm:mixinType != 'HiddenInNavigation'
AND ecm:isTrashed = 0
</pattern>
<parameter>#{currentDocument.id}</parameter>
<sort column="dc:title" ascending="true" />
<pageSize>2</pageSize>
</genericPageProvider>
...
</extension>
The <genericPageProvider>
element takes an additional class
attribute stating the page provider class. This class has to follow the org.nuxeo.ecm.core.api.PageProvider
interface and does not need to list document models. The abstract class org.nuxeo.ecm.core.api.AbstractPageProvider
makes it easier to define a new page provider as it implements most of the interface methods in a generic way.
As result layouts can apply to other objects than document models, their definition can be adapted to fit to the kind of results provided by the custom page provider.
In the given example, another kind of query will be performed on a core session, and will return a list of maps, each map holding the dc:title
key and corresponding value on the matching documents.
The <genericPageProvider>
element accepts all the other configurations present on the <coreQueryPageProvider>
element: it is up to the PageProvider
implementation to use them to build its query or not. It can also perform its own caching.
The properties can be defined as EL expressions and make it possible for the query provider to have access to contextual information. In the above example, the core session to the Nuxeo repository is taken from the Seam context and passed as the property with name coreSession
.
Use Cases
Using Query Parameters and 'IN' Operator
By passing query String list parameters:
list.add("\"Art/Architecture\", \"Art/Culture\"");
And setting quoteParameters to false:
<genericPageProvider class="org.nuxeo.ecm.platform.query.nxql.CoreQueryAndFetchPageProvider"
name="searchWithInOperatorAndQueryParams">
<property name="searchAllRepositories">true</property>
<pattern quoteParameters="false">
SELECT * FROM Document WHERE ecm:mixinType != 'HiddenInNavigation'
AND ecm:isVersion = 0
AND ecm:isTrashed = 0
AND dc:subjects IN (?)
</pattern>
<pageSize>50</pageSize>
</genericPageProvider>
Result will be the following query:
SELECT * FROM Document WHERE ecm:mixinType != 'HiddenInNavigation' AND ecm:isVersion = 0 AND ecm:isTrashed = 0 AND dc:subjects IN ("Art/Architecture", "Art/Culture")
Using Named Parameters and Dates with Automation
By setting quoteParameters to false:
<genericPageProvider class="org.nuxeo.ecm.platform.query.nxql.CoreQueryAndFetchPageProvider" name="testPP">
<property name="searchAllRepositories">true</property>
<pattern quoteParameters="false">
SELECT * FROM Document WHERE dc:created > :mydate AND dc:title = "hello"
</pattern>
<pageSize>50</pageSize>
</genericPageProvider>
And defining a named parameter date:
<operation id="Repository.PageProvider">
<param type="string" name="language">NXQL</param>
<param type="properties" name="namedParameters">expr:mydate=@{CurrentDate}</param>
<param type="string" name="providerName">testPP</param>
<param type="string" name="sortOrder">ASC</param>
</operation>
Result will be the following query:
SELECT * FROM Document WHERE dc:created > TIMESTAMP '2015-04-04 00:00:00.000'