Skip to content
Global Search CUBA Platform application component that will provide user ability to search many types of objects in one search field.
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.github
config
gradle/wrapper
modules
resources
.gitignore
.travis.yml
LICENSE.txt
README.md
build.gradle
gradlew
gradlew.bat
settings.gradle
studio-settings.xml

README.md

license Build Status

Rich Search Component

Component image example

Overview

The Global Search application component is designed to search for various types of objects using a single field. The component provides an out-of-the-box solution for searching by application menu items. It is also possible to configure the addon to search for entities (including related ones) and their attributes.

Installation

To add the component to your project, the following steps should be taken:

  1. Open your application in CUBA Studio.

  2. Edit Project properties.

  3. Click the plus button in the App components section of the Main tab.

  4. Specify the coordinates of the component in the corresponding field as follows: group:name:version. Click OK to confirm the operation.

    • Artifact group: com.haulmont.addon.search

    • Artifact name: search-global

    • Version: add-on version

      When specifying the component version, you should select the one, which is compatible with the platform version used in your project.

    Platform Version Component Version
    7.0.X 1.4.0
    6.10.X 1.3.0
    6.9.X 1.2.0
    6.8.X 1.0.3

Quick Start

  1. Override the main screen via the Studio interface.
  2. Add the following code to ext-mainwindow.xml:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<window xmlns="http://schemas.haulmont.com/cuba/window.xsd"
        class="com.company.rs.web.screens.ExtAppMainWindow"
        extends="/com/haulmont/cuba/web/app/mainwindow/mainwindow.xml"
        messagesPack="com.company.rs.web.screens"
        xmlns:ext="http://schemas.haulmont.com/cuba/window-ext.xsd"
        xmlns:search="http://schemas.haulmont.com/cuba/search.xsd">
    <dialogMode height="600"
                width="800"/>
    <layout>
        <hbox id="titleBar">
            <search:richSearch id="search"
                               align="MIDDLE_LEFT"
                               ext:index="3"
                               inputPrompt="msg://search"
                               suggestionsLimit="200">
                <search:strategyBean name="search_MainMenuSearchStrategy"/>
            </search:richSearch>
        </hbox>
    </layout>
</window>

Data Model

Search Field

Search Field provides one or several search strategies that are called for each search query.

Search Strategy

Search Strategy defines which objects should be returned as a result of the current search request.

SearchEntry

SearchEntry is an interface designed to work with search result objects. Each SearchEntry has an id, caption and the name of a search strategy that it belongs to specified.

DefaultSearchEntry

DefaultSearchEntry defines the default implementation of SearchEntry.

SearchContext

SearchContext provides the context dependent data for the search mechanism, e.g. a user session, additional params.

HeaderEntry

HeaderEntry implements a header for grouping strategy results.

Usage

Let us consider an example of how the component can be of use. Start by searching for a system user. The component finds the required user and opens User Editor.

The system performs search by login. In order to configure the component, use the following code:

LoadContext<User> lc = LoadContext.create(User.class);
lc.setQueryString("select u from sec$User u where u.loginLowerCase like concat('%',:loginLowerCase,'%')")
        .setParameter("loginLowerCase", query.toLowerCase());

The sections below present several options to define search strategies.

Using A Spring Bean As Search Strategy:

Spring bean:

@Component("search_UsersSearchStrategy")
public class UsersSearchStrategy implements SearchStrategy {
    @Override
    public List<SearchEntry> load(SearchContext context, String query) {
        LoadContext<User> lc = LoadContext.create(User.class);
        lc.setQueryString("select u from sec$User u where u.loginLowerCase like concat('%',:loginLowerCase,'%')")
                .setParameter("loginLowerCase", query.toLowerCase());

        return dataManager.loadList(lc).stream()
                .map(user -> new DefaultSearchEntry(user.getId().toString(), user.getCaption(), name()))
                .collect(Collectors.toList());
    }

    @Override
    public void invoke(SearchContext context, SearchEntry value) {
        LoadContext<User> lc = LoadContext.create(User.class)
                .setId(UuidProvider.fromString(value.getId()));
        User user = dataManager.load(lc);
        AppUI.getCurrent().getTopLevelWindow().openEditor(user, WindowManager.OpenType.NEW_TAB);
    }
 
    
    @Override
    public String name() {
        return "usersSearchStrategy";
    }
}

XML screen config:

<window
    class="com.company.test.web.screens.TestWindow" 
    xmlns:search="http://schemas.haulmont.com/cuba/search.xsd">
    ...
    <layout>
        ...
        <search:richSearch id="search" inputPrompt="msg://search">
            <search:strategyBean name="search_UsersSearchStrategy" />
        </search:richSearch>
        ...
    </layout>
    ...
</window>

Localization

Add the following entry to the main message pack as it is given below:

searchStrategy.{strategyName} = Strategy name

Example:

searchStrategy.usersSearchStrategy = Users

Using Controller Methods**

XML screen config:

<window
    class="com.company.test.web.screens.MyWindowController" 
    xmlns:search="http://schemas.haulmont.com/cuba/search.xsd">
    ...
    <layout>
        ...
        <search:richSearch id="search" inputPrompt="msg://search">
            <search:strategy name="usersSearchStrategy" searchMethod="search" invokeMethod="invoke" />
        </search:richSearch>
        ...
    </layout>
    ...
</window>

Screen controller:

public class MyWindowController extends AbstractWindow {
    
    @Inject
    protected DataManager dataManager;

    public List<SearchEntry> search(SearchContext context, String query) {
        LoadContext<User> lc = LoadContext.create(User.class);
        lc.setQueryString("select u from sec$User u where u.loginLowerCase like concat('%',:loginLowerCase,'%')")
                .setParameter("loginLowerCase", query.toLowerCase());

        return dataManager.loadList(lc).stream()
                .map(user -> new DefaultSearchEntry(user.getId().toString(), user.getCaption(), "usersSearchStrategy"))
                .collect(Collectors.toList());
    }
   
    public void invoke(SearchContext context, SearchEntry searchEntry) {
        LoadContext<User> lc = LoadContext.create(User.class)
                .setId(UuidProvider.fromString(searchEntry.getId()));
        User user = dataManager.load(lc);
        AppUI.getCurrent().getTopLevelWindow().openEditor(user, WindowManager.OpenType.NEW_TAB);
    }
}

Using Programmatic Strategy

Screen controller:

public class MyWindowController extends AbstractWindow {

    @Inject
    protected DataManager dataManager;

    @Inject
    protected RichSearch search;

    @Override
    public void init(Map<String, Object> params) {
        super.init(params);

        search.addStrategy("usersSearchStrategy", query -> {
            LoadContext<User> lc = LoadContext.create(User.class);
            lc.setQueryString("select u from sec$User u where u.loginLowerCase like concat('%',:loginLowerCase,'%')")
                    .setParameter("loginLowerCase", query.toLowerCase());

            return dataManager.loadList(lc).stream()
                    .map(user -> new DefaultSearchEntry(user.getId().toString(), user.getCaption(), "usersSearchStrategy"))
                    .collect(Collectors.toList());
        }, searchEntry -> {
            LoadContext<User> lc = LoadContext.create(User.class)
                    .setId(UuidProvider.fromString(searchEntry.getId()));
            User user = dataManager.load(lc);
            AppUI.getCurrent().getTopLevelWindow().openEditor(user, WindowManager.OpenType.NEW_TAB);
        });
    }
}

The result is:

User search example

Demo

The rich-search-addon-demo project contains usage example.

Known issues

  1. The available amount of input should be accounted for each strategy separately (no task, to be discussed).
  2. It is required to add an XML declaration for specific strategy parameters (no task, to be discussed).
  3. It is required to add keyboard shortcuts for the component and its settings (no task, to be discussed).
You can’t perform that action at this time.