Page providers in Nuxeo often use aggregates as user-friendly filters. For example, if aggregates are used, then when filtering a query, users will be able to see before clicking how many documents match that filter. However, having a lot of aggregates takes a toll on page load performance since all this data has to be retrieved from Elasticsearch during page load. In order to reduce this upfront load while still providing this user-friendly functionality, we can replace the default aggregate functionality with custom functionality that only loads the data once the user has clicked to see the options for a specific filter.
From analysis, we know that the default behavior is for the aggregates’ buckets to be transferred along with page providers’ data and then be assigned to the Nuxeo aggregate elements’ data property (nuxeo-checkbox-aggregation, nuxeo-dropdown-aggregation). This occurs when page is first loaded, but we know users will not immediately select all filters. That means that, from a performance and user experience perspective, all these aggregations do not need to load when the page first loads.
So, our solution is to remove these aggregation buckets from the response data in order to reduce the amount of data requested upon page load, and only load the data when a user clicks a filter (fetching that filter’s aggregation at that point in time).
There are some steps we need to complete before we’ll be able to achieve these performance improvements.
Many areas to optimize
Nuxeo has a lot of moving parts-the front-end, the database, Elastic Search, caching, and so on-and the exact setup can be unique to each company that uses it. And, because developers typically introduce custom functionality that is specific to the company’s needs, such as custom views, we need to make sure that we are aware of the impact of these customizations on page-load speed and that we optimize the page load process where necessary.
In short, there are many places where we can optimize performance. This blog entry won’t try to cover all of them but will instead look at one specific example scenario where we discovered some issues using the Network Monitor tool in Chrome and investigated to find the issue and the solution.
Part 1: Customize the aggregation elements
We need to define a customized element that fetches aggregates when a user clicks a filter but which otherwise looks and operates the same as a standard Nuxeo aggregate.
Below is the template content of our customized dropdown aggregation element based on the nuxeo-dropdown-aggregation
element:
We use a nuxeo-page-provider element to fetch aggregation; the page-size
should be 1, and schemas
should contain “dublincore” to get the items’ label.
In the element, we use a customized element, iss-selectivity
, to execute the query. The iss-selectivity
element is derived from the nuxeo-selectivity
element. We changed its default query in its callback function; if we set its page provider, then it will query aggregation. A code snippet is below:
// debounce requests
this._debouncer = Polymer.Debouncer.debounce(
this._debouncer,
Polymer.Async.timeOut.after(this.frequency), () => {
// if set PageProvider, then use it to query aggregations
if (this.pageProvider) {
// if first time set quickFilters, need to fetch once
if (this.quickFilters && !this.pageProvider.quickFilters) {
this.pageProvider.fetch().then(resp => {
this._fetchAggregations(query);
});
} else {
this._fetchAggregations(query);
}
} else { // the default query
this._query(query);
}
},
);
The nuxeo-dropdown-aggregation
and nuxeo-selectivity
elements can be found in the Nuxeo WebUI official repository on GitHub.
Part 2: Modify page providers and search form and result files
Once we have customized aggregation elements, we can apply them to multi-doc view pages and search forms. We will explain here, in three steps, how to use it in a search form or results page.
Step 1: Modify main page provider
We need to modify the original main page provider. First, we need to change aggregates to predicates. All predicate operations should use the Operator “IN”. If the field is a single value type, check the list checkbox; this is necessary to ensure aggregate’s multi-value search function:
In the example of modified page provider below, we can see that all aggregates are changed to predicates:
Step 2: Define separate aggregation page provider
In order to achieve lazy loading, we use the main page provider to fetch the entries’ data when we are in the search tab/section. Then we need to define a separate aggregation page provider to fetch the aggregates when users click the specific filter.
In the aggregation page provider, we set predicates like above, except for the fields that we need to aggregate. For example, if we need to fetch desc:assetTypes aggregation, the page provider is set as shown below:
If the main page provider contains some necessary aggregates, quick filters, or other settings, the aggregation page provider should also keep the same settings with the main page provider. This ensures that the aggregate filter will request the same parameters as the main page provider. An example definition of all page providers for an example area called Chapter Reference is below (including the main page provider, “chapterReference_pp”):
- chapterReference_Description_assetTypes_agg
- chapterReference_Description_character_agg
- chapterReference_Description_chapterRef_chapterRefVolumeBook_agg
- chapterReference_Relations_series_agg
- chapterReference_Relations_storyline_agg
- chapterReference_pp
- chapterReference_technical_mediaTyes_agg
- chapterReference_video_production_season_agg
In the above page providers, the chapterReference_pp
is the main page provider and the others are aggregation page providers.
Step 3: Modify the search form/results file
We need to modify the search form file to replace the Nuxeo aggregate element with our customized element.
A simple example is below (the element name is iss-dropdown-aggregation
):
The parameters “value”, “page-params”, “provider”, and “multiple” are necessary. When “value” changes, the “params” will also change and will invoke the “params-changed” event to fetch data. The “provider” is the page provider id that we defined to fetch aggregations; when users click the filter, it will execute the query and pop up the dropdown list with results.
If page provider contains other aggregates, then we also need to set “aggregation” parameter to identify the aggregates that we need:
If the page provider contains any quick filters, then we need to set the “quick-filters” parameter:
The variable quickFilters
is defined in the search form file:
When users click quick filter buttons, the variable quickFilters
must know its changes, so we need to listen for the quick-filters-changed
event on the search result file:
The application in multi-doc view file also needs to be changed slightly. We should listen for the “params-changed” event:
listeners: {
‘params-changed’: ‘_paramsChanged’
},
_paramsChanged: function () {
if (this.document && this.$.results.view) {
this.$.results.view.fetch();
}
}
Conclusion
This solution can significantly improve page performance on pages with a lot of aggregates. However, we need customized aggregation elements and to define separate aggregation page providers to work with the main page provider to support this feature; this makes code modification more complex.
Want more information?
If you would like to learn more information, please contact us today.