The Periscope module provides back-end functionality used by the Find Bar. It can be used out-of-the-box to search all content apps or extended by activating search in external data sources and by implementing operations commonly used by your users. The Find Bar brings the back-end search functionality of the Periscope module into the Magnolia UI. It provides the UI display of the Find Bar search box, search results grid and the drop-down filters to refine results.

The EE Pro Periscope Result Ranker module works with the periscope modules to provide neural network-based ranking for your search results.

Module structure

magnolia-periscope-parent Parent reactor.


Provides an API to plug in search result suppliers to the search result set, as well as to create your own query sniffers (for example for vocal commands).


Provides the default search result supplier and periscope operation configuration files as well as a sample REST integration (to search the Magnolia documentation).


Optional submodule.

Provides the speech recognition module and service to enable voice data processing.


Maven is the easiest way to install the modules. Add the following dependencies to your webapp. All the other necessary dependencies will be brought in automatically:



Pre-built jars are also available for download. See Installing a module for help.


There's no additional configuration required for standard use because the default configuration provides a number of search result suppliers and periscope operations. All content apps are detected upon startup and corresponding search result suppliers for their workspaces are generated by the system on-the-fly. 

You may configure additional search result suppliers and periscope operations to suit your specific needs. A sample REST integration is also provided, which you can use as a basis to implement your own REST integration.

For general usage information, see the Find Bar page. 

Search result suppliers

Search result suppliers are responsible for providing results to the Find Bar.

By default, all the Magnolia JCR workspaces are searchable. Content apps are detected when you start Magnolia and the system generates corresponding SearchResultSupplier s  for their workspaces automatically. For performance reasons, search results are limited to 10 items per search result supplier and 100 items in total by default.

For example, this is the definition for the Pages app:

workspace: website
  - mgnl:page
fullTextSearch: true
titleProperty: title
icon: webpages-app

If you want to search non-JCR databases or external datasets, you must create your own corresponding SearchResultSupplier.

For normal content apps you don't need to create your own SearchResultSupplier Java class or YAML definition. Search result suppliers for existing JCR workspaces (at start up) are generated automatically.

Creating a custom search result supplier

Use the SearchResultSupplierDefinition interface provided by the magnolia-periscope-api submodule to implement a search result supplier. For example, you could implement a search result supplier to search through images stored on S3 or Flickr. 

public interface SearchResultSupplier {
    String getName();
    Stream<SearchResult> search(SearchQuery query) throws SearchException;
Each SearchResultSupplier is responsible for supplying results. The results of suppliers are represented by SearchResult objects which contain text, metadata and a periscope operation request, which specifies what should be done when a user selects that result.

public class SearchResult {

    private final String text;
    private final String excerpt;
    private final Object operationRequest;

    private final String type;
    private final String lastModifiedBy;
    private final ZonedDateTime lastModified;

For example, the built-in JcrSearchResultSupplierDefinition creates an InternalNavigationRequest to open the result selected by the user in Magnolia. 

For JCR workspaces

Content apps are detected when you start Magnolia and the system generates corresponding SearchResultSuppliers for their JCR workspaces automatically. You only need to define a search result supplier using YAML to search JCR workspaces if you want to customize the supplier. Magnolia provides a registry with a specific JcrSearchResultSupplierDefinition class that searches over defined JCR workspaces using specific node types and with the option of performing full-text search.

In the example below, the JcrSearchResultSupplierDefinition class, <my-custom-workspace> workspace and node type <my-node-type> are specified. Full-text search is activated within the workspace defined.

workspace: <my-custom-workspace>
- <my-node-type>
fullTextSearch: true
titleProperty: caption




Fully qualified class name of the definition. Must extend SearchResultSupplierDefinition.


optionaldefault is true 

Enable or disable the search provider.



The workspace to be searched.



The node type to be searched.



JCR property to be considered the title of a node. It is always searched and used for the result title.


optionaldefault is true 

When set to false, only node names are searched.



The name of your app. Specifies which app is opened when a result is clicked.

For REST integrations

You can define a search result supplier using YAML to integrate an external dataset via REST. Magnolia provides a specific RestSearchResultSupplierDefinition class to facilitate REST integrations. The sample described below shows how it can be used. 

Sample REST integration 

The magnolia-periscope-core module provides a sample REST integration illustrating how to deliver search results from outside of Magnolia. When enabled, this sample integrates the Magnolia documentation and the Find Bar displays search results from the documentation.

You can define your own REST integrations using the class.

This example shows the definition for searching the Magnolia documentation:

requestParameters: "?cql=(type=page AND space=DOCS60 AND (title~\"/.*${query}.*/\" OR text~\"/.*${query}.*/\") ${editorClause} ${dateClause})&limit=10&expand=history"
editorField: contributor
dateField: lastmodified
navigationBaseURL: ""
titleJsonPath: $..results[?(@.title =~ /^(?!_).*/i)].title
navigationURLJsonPath: $..results[?(@.title =~ /^(?!_).*/i)]..webui
editorJsonPath: $..results[?(@.title =~ /^(?!_).*/i)]..displayName
dateJsonPath: $..results[?(@.title =~ /^(?!_).*/i)]..createdDate
enabled: false

XXX Ilgun testing

As another example, this definition shows how to link to Wikipedia:

requestParameters: ?format=json&action=query&titles=%s&redirects
navigationBaseURL: ""
titleJsonPath: $..title
navigationURLJsonPath: $..pageid






The base request URL for the content to be searched.



Parameters of the request URL.

The following variables are replaced when a request is executed:

  • ${query} – replaced by the search query text.
  • ${editorClause} – replaced by a clause representing the desired editor, defaulting* to [editorField] = editor1 OR [editorField] = editor2 ...
  • ${dateClause} – replaced by a clause representing the date filter, defaulting* to [dateField] >= [yyyy-MM-dd] AND [dateField] <= [yyyy-MM-dd] ...

*The structure of these clauses can be customized by subclassing RestSearchResultSupplier and overriding the corresponding methods. Default patterns comply with CQL.



Base URL for navigation when selecting a result. May contain placeholders (%s) for inserting result-dependent parts (based on naviagationURLJsonPath), otherwise they are appended.



Path to the result-dependent part of the navigation target URL of a result. To be combined with navigationBaseURL.



Path within returned JSON after a search. Should point to an item representing the result's title.



Field in a query representing an editor property. Used in ${editorClause}(see requestParameters above).



Path within returned JSON after a search. Should point to an item denoting the editor associated with a result.



Field in a query representing a date property. Used in ${dateClause}(see requestParameters above).



Path within returned JSON after a search. Should point to an item denoting the last-edited date of a result.



Set to true or false to enable or disable the integration.

Disabling search result suppliers 

To change which suppliers are enabled, you must provide your own Find Bar configuration through component mapping.

For example, add a custom configuration provider to your module descriptor :


Changing the order of search result supplier results 

Results are presented asynchronously by search result supplier, from all workspaces and in the following default order:

  • Apps
  • Pages
  • Stories
  • Assets
  • Tours
  • Any other search result suppliers in alphabetical order.

This order is configurable. To change it, you must provide your own Find Bar configuration through component mapping (see example above)

Configuring the default number of search results per supplier

For performance reasons, search results are limited to 10 items per search result supplier by default. 

This limit is configurable. To change it, you must provide your own Find Bar configuration through component mapping (see example above)

Periscope operations

Periscope operations are actions that are executed based on a corresponding operation request, usually triggered by a search result when a user clicks it in the Find Bar. 

By default, Magnolia provides the following Operations out-of-the-box:

  • Internal navigation – shows an item inside a workspace by opening the corresponding app.
  • External navigation – opens a URL in a new browser tab.
  • App navigation – opens an app.

For example, this is the Operation definition provided for opening an app, for example, when a user clicks the "pages app" result or types "open pages app":

requestClass: info.magnolia.periscope.operation.request.AppNavigationRequest
operationClass: info.magnolia.admincentral.findbar.operation.AppNavigationOperation

Configuring a Operation

Developers can define additional custom operations to be used by result suppliers.

This is the generic interface that should be implemented in order to introduce a new Operation:

public interface Operation<R> {
    OperationResult execute(R request);

In order for Periscope to register the operation, create a definition complying with OperationDefinition, for example by creating a YAML file under src/main/resources/<module-name>/operations as follows: 

requestClass: com.example.mypackage.MyCustomRequest
operationClass: com.example.mypackage.MyCustomOperation

In this example, a MyCustomOperation is executed whenever a SearchResult's getOperationRequest() returns an instance of MyCustomRequest (after the result has been clicked).

Operation definitions work like any other definition in Magnolia, meaning they support features like decorations or hot-swapping.

Query sniffers

Query sniffers are listeners for changes in the Find Bar. When a user enters a search query into the Find Bar (by typing or speaking), Periscope notifies all registered sniffers. The sniffers then decide whether to take action or not based on the same query.

All built-in query sniffers have the form of commands: they execute an action based on certain text patterns. The default ones are:

  • Find – searches and opens the first result:
    • node (node name or title), triggered by queries like "find mountain tour".
    • content (full-text), triggered by queries like "find pages about biking".
  • Open – command to open apps, triggered by, for example, "open assets app".

Defining query sniffers

Custom query sniffers can be introduced by implementing the QuerySniffer interface:

public interface QuerySniffer {
    Optional<OperationResult> sniff(String query);

For sniffers intended to take action based on a regex pattern, extend PatternCommandSniffer, which abstracts away the matching part. For example, a command for creating contacts can be implemented as follows:

public abstract class CreateContactCommandSniffer extends PatternCommandSniffer {

    private static final Pattern COMMAND_PATTERN = Pattern.compile("create contact (?<contactName>([^\\s]+))");

    protected abstract Pattern getPattern() {

    protected abstract OperationResult execute(Matcher matcher) {
        String contactName ="contactName");
        // ... (create contact)
        return new OperationResult(true, "Successfully created new contact named: " + contactName);

To be recognized by Periscope, each sniffer must be registered through a definition. New query sniffer definitions are automatically detected by the running system. To register a sniffer, create a YAML definition file under src/main/resources/<module-name>/querySniffers as follows:

snifferClass: com.example.periscope.sniff.CreateContactCommandSniffer

Just like other definitions, sniffer definitions are listed in the Definitions app. If something is not working as expected, this is a good place to start troubleshooting.

Speech recognition service

The speech-recognition module provides the speech recognition service to enable voice data processing for users searching using the Find Bar. Typically users speak to Magnolia via a microphone and the speech recognition service translates their voice data into text. The periscope executes that text like a normal text query from the user.

The default Speech recognition implementation uses Web Speech API.

Check which browsers are supported by the Web Speech API here. At the time of publication, only Google Chrome was supported.

#trackbackRdf ($trackbackUtils.getContentIdentifier($page) $page.title $trackbackUtils.getPingUrl($page))