Integrating Admin Global Search
Updated at 1772979858000EzyPlatform provides an admin global search feature that allows administrators to quickly search across multiple types of data in the admin panel, such as:
- products
- users
- orders
- articles
- plugin-provided data
The key idea behind this feature is that it is designed to be extensible through plugins. Any module can contribute search results simply by implementing the
AdminSearchService interface.
This guide explains how to integrate your module or plugin into EzyPlatform admin global search.
How Global Search Works
The global search follows a simple workflow:
- The admin enters a keyword
- The endpoint
/api/v1/searchis called - The controller retrieves all
AdminSearchServiceimplementations - Each service performs its own search logic
- The results are merged and returned to the frontend
flowchart TD
A[Admin enters keyword] --> B[GET /api/v1/search]
B --> C[AdminApiSearchController]
C --> D[Load all AdminSearchService implementations]
D --> E[Execute search keyword]
E --> F[Merge results]
F --> G[Return response to frontend]
The controller does not need to know where the data comes from. Each plugin is responsible for providing its own search results.
Global Search Controller
The search endpoint is provided by the following controller:
@Controller("/api/v1") public class AdminApiSearchController { private final EzyLazyInitializer<List<AdminSearchService>> searchServices; public AdminApiSearchController( EzySingletonFactory singletonFactory ) { searchServices = new EzyLazyInitializer<>(() -> { List<AdminSearchService> searchServices = singletonFactory.getSingletonsOf(AdminSearchService.class); searchServices.sort( Comparator.comparingInt(AdminSearchService::priority) ); return searchServices; }); } @DoGet("/search") public List<SearchResultModel> searchGet( @AdminId long adminId, @AdminRoles AdminRolesProxy adminRoles, @RequestParam("keyword") String keyword ) { String trimmedKeyword = trimOrNull(keyword); if (trimmedKeyword == null) { return Collections.emptyList(); } List<SearchResultModel> result = new ArrayList<>(); for (AdminSearchService searchService : searchServices.get()) { try { result.addAll( searchService.search( adminId, adminRoles, trimmedKeyword ) ); } catch (Exception e) { // ignore } } return result; } }
This controller is responsible for:
- retrieving all
AdminSearchServiceimplementations - sorting them by
priority - executing
search()on each service - merging all results into a single list
Interface for Plugin Integration
To integrate with global search, plugins must implement the following interface:
public interface AdminSearchService { default List<SearchResultModel> search( long adminId, AdminRolesProxy rolesProxy, String keyword ) { return this.search(adminId, keyword); } default List<SearchResultModel> search(long adminId, String keyword) { return this.search(keyword); } default List<SearchResultModel> search(String keyword) { return Collections.emptyList(); } default int priority() { return 0; } }
Explanation:
| Method | Purpose |
|---|---|
| `search(keyword)` | simple keyword search |
| `search(adminId, keyword)` | search with admin context |
| `search(adminId, roles, keyword)` | search with role-based filtering |
| `priority()` | defines execution order |
Plugins can override whichever method fits their needs.
Example: Product Search Integration
Below is an example of integrating product search from the ecommerce module.
@Service @AllArgsConstructor public class AdminEcommerceAdminSearchControllerService implements AdminSearchService { private final AdminProductService productService; @Override public List<SearchResultModel> search(String keyword) { if (isBlank(keyword)) { return Collections.emptyList(); } List<ProductModel> products = productService.getProductsByKeywordPrefix( keyword, LIMIT_30_RECORDS ); return newArrayList(products, it -> SearchResultModel.builder() .title(it.getProductCode()) .resultType("product") .content(it.getProductName()) .url("/ecommerce/products/" + it.getId()) .build() ); } }
This service:
- searches products by keyword
- converts
ProductModeltoSearchResultModel - returns the results to the controller
SearchResultModel Structure
Search results must be standardized using
SearchResultModel.Example fields:
| Field | Description |
|---|---|
| `title` | result title |
| `content` | result description |
| `resultType` | data type identifier |
| `url` | link to the admin page |
Because all results share the same model, the frontend can display mixed results from different plugins in a single search dropdown.
Example: Adding Search for a New Plugin
For example, a plugin may add coupon search like this:
@Service public class AdminCouponSearchService implements AdminSearchService { @Override public List<SearchResultModel> search(String keyword) { List<CouponModel> coupons = couponService.search(keyword); return coupons.stream() .map(it -> SearchResultModel.builder() .title(it.getCode()) .content(it.getName()) .resultType("coupon") .url("/coupons/" + it.getId()) .build()) .toList(); } }
No modification to the global search controller is required.
Best Practices
When implementing
AdminSearchService, consider the following:- Limit the number of results (for example 20–30 records)
- Avoid heavy or slow queries
- Use prefix or indexed searches when possible
- Return direct URLs to admin pages
Conclusion
EzyPlatform admin global search is built around a plugin-based search service architecture.
To integrate a plugin into the global search:
- implement
AdminSearchService - perform the search logic
- return results as
SearchResultModel
The global search controller will automatically discover your service and merge its results with others.
This architecture provides:
- easy extensibility
- modular plugin development
- no need to modify core code when adding new search sources