Model Entity Indexing Framework

Unless you’re searching for model entities using database queries (not recommended in most cases), each asset in Liferay DXP must be indexed in the search engine. The indexing code is specific to each asset, as the asset’s developers know what fields to index and what filters to apply to the search query. This paradigm applies to Liferay’s own developers and anyone developing model entities for use with Liferay DXP.

In past versions of Liferay DXP, when your asset required indexing, you would implement a new Indexer by extending<T>. Liferay DXP version 7.1 introduced a new pattern that relies on composition instead of inheritance. That said, if you want to use the old approach, feel free to extend BaseIndexer. It’s still supported.

Search and Indexing Overview

Starting with the 7.0 version of Liferay DXP, the Search API has become less tied to Lucene. Elasticsearch support was added (in addition to Solr), and most of the newer searching and indexing APIs aim to leverage/map Elasticsearch APIs. This means that in many cases (for example the Query types) there is a one-to-one mapping between the Liferay and Elasticsearch APIs.

In addition to the Elasticsearch-centered APIs, Liferay’s Search Infrastructure includes additional APIs serving these purposes:

  • Ensure all indexed documents include some required fields (e.g., entryClassName, entryClassPK, assetTagNames, assetCategories, companyId, groupId, staging status).
  • Ensure the scope of returned search results is appropriate by applying the right filters to search requests.
  • Provide permission checking and hit summaries for display in the built-in search application.

Mapping the Composite Search and Indexing Framework to Indexer/BaseIndexer Code

If you’re used to the old way of indexing custom entities (extending BaseIndexer, the abstract implementation of Indexer), the table below provides a quick overview about how the methods of the Indexer interface were decomposed into several new classes and methods.

Indexer/BaseIndexer methodComposite Indexer EquivalentExample
Class ConstructorSearchRegistrarBlogsEntrySearchRegistrar
getSummary/createSummary/doGetSummaryModelSummaryContributorBlogsEntryModelSummaryContributor changeBlogEntriesDisplayContext
Indexer.delete/doDeleteNo changeMBMessageLocalServiceImpl.deleteMessage

In addition, you can index ExpandoBridge attributes. This was previously accomplished in BaseIndexer’s getBaseModelDocument. Now you implement an ExpandoBridgeRetriever. See DLFileEntryExpandoBridgeRetriever for an example implementation.

Permissions-Aware Searching and Indexing

In previous versions of Liferay DXP, search was only permissions aware (indexed with the entity’s permissions and searched with those permissions intact) if the application developer specified this line in the Indexer class’s constructor:


Now, search is permissions aware by default if the new permissions approach, as described in these tutorials, is implemented for an application.

Annotating Service Methods to Trigger Indexing

Having objects translated into database entities and search engine documents means that there’s a possibility for a state mismatch between the database and search engine. For example, when a Blogs Entry is added, updated, or removed from the database, corresponding changes must be made in the search engine. To do this, intervention must be made in the service layer. For Service Builder entities, this occurs in the LocalServiceImpl classes. An annotation simplifies this: @Indexable. It takes a type property that can have two values: REINDEX or DELETE. Commonly, a deleteEntity method in the service layer is annotated like this:

@Indexable(type = IndexableType.DELETE)
@SystemEvent(type = SystemEventConstants.TYPE_DELETE)
public BlogsEntry deleteEntry(BlogsEntry entry) throws PortalException {

The @Indexable annotation is executed by Liferay’s AOP infrastructure, so if you have a method with that annotation, you must call it using a service instance variable injected by your dependency injector, and not using the this keyword. Whether using OSGi’s Declarative Services (DS) or Spring for dependency injection, there’s a protected variable declared in the superclass (*LocalServiceBaseImpl) that can be used in the *LocalServiceImpl, like this.


Since you’re using the injected service variable, that means you must not call


in your *LocalServiceImpl methods. The annotation won’t be executed and you’ll be left with a state mismatch between the search engine document and the database column.

Search and Localization: a Cheat Sheet

Localization is important. Search and localization can play nicely together, if you take some precautions:

  • For each field that should be localized (e.g., content), index a separate field for each of the site’s languages (e.g., content_en_US, content_ja_JP, content_es_ES, …).
  • Search the localized fields. Whatever you index, that’s what you should be querying for.
  • Don’t index content in plain (unlocalized) fields if you expect the content to be present in multiple locales.
  • Don’t index both the plain and the localized field.

The indexing and searching articles included in this section demonstrate how to handle localized fields in the search code properly.

« Statistical AggregationsIndexing Model Entities »