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
com.liferay.portal.kernel.search.BaseIndexer<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 method | Composite Indexer Equivalent | Example |
---|---|---|
Class Constructor | SearchRegistrar | BlogsEntrySearchRegistrar |
setDefaultSelectedFieldNames | SearchRegistrar.activate | BlogsEntrySearchRegistrar |
setDefaultSelectedLocalizedFieldNames | SearchRegistrar.activate | BlogsEntrySearchRegistrar |
setPermissionAware | ModelResourcePermissionRegistrar | DLFileEntryModelResourcePermissionRegistrar |
setFilterSearch | ModelResourcePermissionRegistrar | DLFileEntryModelResourcePermissionRegistrar |
getDocument /doGetDocument | ModelDocumentContributor | BlogsEntryModelDocumentContributor |
reindex /doReindex | ModelIndexerWriterContributor | BlogsEntryModelIndexerWriterContributor |
addRelatedEntryFields | RelatedEntryIndexer | DLFileEntryRelatedEntryIndexer |
postProcessContextBooleanFilter /PostProcessContextQuery | ModelPreFilterContributor | BlogsEntryModelPreFilterContributor |
postProcessSearchQuery | KeywordQueryContributor | BlogsEntryKeywordQueryContributor |
getFullQuery | SearchContextContributor | DLFileEntryModelSearchContextContributor |
isVisible /isVisibleRelatedEntry | ModelVisibilityContributor | BlogsEntryModelVisibilityContributor |
getSummary /createSummary /doGetSummary | ModelSummaryContributor | BlogsEntryModelSummaryContributor |
Indexer.search /searchCount | No change | BlogEntriesDisplayContext |
Indexer.delete /doDelete | No change | MBMessageLocalServiceImpl.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:
setPermissionAware(true);
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)
@Override
@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.
blogsEntryLocalService.deleteEntry(entry);
Since you’re using the injected service variable, that means you must not call
this.deleteEntry(...)
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.