Entity Search

Item search is used to find root entities which satisfy arbitrary conditions. Conditions are defined in runtime on root entity fields and sub-entities fields. Search result is a report which contains a list of found root entity ids (see reporting framework for more details) or an array of entity proxies Item search is in fact a generic API to reporting framework and can (and will) replace most of the named reports.

In HPM 5.3 item search implementation has been redesigned and can be safely used to perform complex search queries over large datasets. Search API is defined in com.heiler.ppm.search.core plugin and can be used platform wide.

Search expression

Search conditions are defined using boolean expressions and field operands. Search expression example using pseudo code:

(ArticleType.Ean = 9780321349606) AND ( (ArticleLangType.DescriptionShort not contains "myDescription") OR NOT ( ArticleType.LastModified > "2012.12.12 13:14" ) )

Available conditions and operands can be found in java package com.heiler.ppm.search.core.expression

Search filters

  • ACL filter. By default search result contains only items for which current user has READ ACL permission.

  • Alive only filter. By default search results do not contain soft-deleted items

  • Arbitrary filter. You can provide report result to define subset of entries to search in. This may improve performance if a subset is small (usefull when searching in a predefined assortment)

Examples

HPM 6.0 API

Starting from HPM 6.0 please use SearchService interface instead of ExecuteSearchEx utility. Platform wide search instance can be obtained using EntitySearchComponent singleton. Consider IoC principal and pass the SearchService instance in constructor (or using setter) instead of calling EntitySearchComponent.getSearchService() every time you need a search service instance.

Starting from HPM 6.0 you can use SearchExpressionBuilder to reduce amount of the boiler-plate code used to create search expression. It provides a kind of internal DSL to build search expression. See java doc for more detail.

Search for an article with ean = 9783898426350 in master catalog
//repository fields
FieldPath catalogFieldPath = new FieldPath( "ArticleType.CatalogProxy" );
FieldPath eanFieldPath = new FieldPath( "ArticleType.Ean" );
 
//search expression
EqualExpression equalCatalogExpression = new EqualExpression( new FieldPathExpression( catalogFieldPath ),
new ValueExpression(CatalogProxy.MASTER_CATALOG_PROXY ) );
EqualExpression equalEanExpression = new EqualExpression( new FieldPathExpression( eanFieldPath ),
new ValueExpression( "9783898426350" ) );
Expression rootExpression = equalCatalogExpression.and( equalEanExpression );
 
//search parameters
SearchParameters searchParameters = new SearchParameters( DataSource.MASTER.getIdentifier(), "ArticleType",
rootExpression );
ExecuteSearchEx search = new ExecuteSearchEx( searchParameters );
search.runWithCoreException( new NullProgressMonitor() );
 
//search result
ReportResult searchResult = search.getReportResult();


Find all ExportTemplates - entity search using classifier field
EntityType genericDataEntityType = RepositoryUtils.getRepositoryEntityType( "GenericDataEntityType" );
Field classifierField = RepositoryUtils.getRepositoryField( "GenericDataEntityType.Classifier" );
FieldPath classifierFieldPath = RepositoryUtils.getFieldPath( classifierField );
 
EqualExpression equalClassifierExpression = new EqualExpression( new FieldPathExpression( classifierFieldPath ),
new ValueExpression( "ExportTemplate" ) );
searchParameters = new SearchParameters( DataSource.MAIN.getIdentifier(), "GenericDataEntityType", equalClassifierExpression );
search = new ExecuteSearchEx( searchParameters );
search.runWithCoreException( new NullProgressMonitor() );
searchResult = search.getReportResult();


Search for article revision 100
SearchParameters searchParameters = new SearchParameters( DataSource.MASTER.getIdentifier(), "ArticleType",
rootExpression );
searchParameters.setRevisionFilter( new RevisionSearchFilter( RevisionTokenFactory.create( 100 ) ) );
ExecuteSearchEx search = new ExecuteSearchEx( searchParameters );
search.runWithCoreException( new NullProgressMonitor() );


Search for articles in master catalog which have
//repository fieldpaths
Field shortDescriptionField = RepositoryUtils.getRepositoryField( "ArticleLang.DescriptionShort" );
Field modificationDateField = RepositoryUtils.getRepositoryField( "ArticleType.LastModified" );
FieldPath shortDescriptionFieldPathEN = RepositoryUtils.getFieldPath( shortDescriptionField );
FieldPath lastModifiedFieldPath = RepositoryUtils.getFieldPath( modificationDateField );
 
shortDescriptionFieldPathEN.getEntityPath()
.setLogicalKeyValue( "ArticleLangType.LK.Language", 9L );
 
//expression
Expression containsShortDescENExpression = new ContainsExpression( new FieldPathExpression( shortDescriptionFieldPathEN ),
new ValueExpression( "not available" ) );
 
GreaterExpression lastDayModificationExpression = new GreaterExpression( new FieldPathExpression( lastModifiedFieldPath ),
new ValueExpression( new Timestamp( System.currentTimeMillis() -86400000 ) ) );
 
equalCatalogExpression = new EqualExpression( new FieldPathExpression( catalogFieldPath ),
new ValueExpression( CatalogProxy.MASTER_CATALOG_PROXY ) );
 
Expression expression = equalCatalogExpression.and( containsShortDescENExpression.or( lastDayModificationExpression ) );
 
//search
searchParameters = new SearchParameters( DataSource.MASTER.getIdentifier(), "ArticleType", rootExpression );
search = new ExecuteSearchEx( searchParameters );
search.runWithCoreException( new NullProgressMonitor() );
 
//search result
searchResult = search.getReportResult();


Search for all articles in master catalog filtered by assortment report (improves performance on large catalogs)
ReportResult assortmentReportResult //= ... create assortment report
EntityType articleEntityType = RepositoryUtils.getRepositoryEntityType( "ArticleType" );
equalCatalogExpression = new EqualExpression( new FieldPathExpression( catalogFieldPath ),
new ValueExpression( CatalogProxy.MASTER_CATALOG_PROXY ) );
 
searchParameters = new SearchParameters( DataSource.MASTER.getIdentifier(), "ArticleType", rootExpression );
search = new ExecuteSearchEx( searchParameters, articleEntityType.getReportType(), ReportPurposes.TEMPORARY.toInt(),
assortmentReportResult );
search.runWithCoreException( new NullProgressMonitor() );
 
//search result
searchResult = search.getReportResult();


Search for all articles in master catalog which are NOT in assortment report
assortmentReportResult = null; // = ... create report ...
articleEntityType = RepositoryUtils.getRepositoryEntityType( "ArticleType" );
Field articleInternalIdField = RepositoryUtils.getRepositoryField( "ArticleType.Id" );
FieldPath articleInternalIdFieldPath = RepositoryUtils.getFieldPath( articleInternalIdField );
 
equalCatalogExpression = new EqualExpression( new FieldPathExpression( catalogFieldPath ),
new ValueExpression( CatalogProxy.MASTER_CATALOG_PROXY ) );
 
NotExpression idsNotInAssortmentExpression = new NotExpression( new InExpression( new FieldPathExpression( articleInternalIdFieldPath ),
new ValueExpression( assortmentReportResult ) ) );
expression = equalCatalogExpression.and( idsNotInAssortmentExpression );
searchParameters = new SearchParameters( DataSource.MASTER.getIdentifier(), "ArticleType", rootExpression );
search = new ExecuteSearchEx( searchParameters);
search.runWithCoreException( new NullProgressMonitor() );
 
//search result
searchResult = search.getReportResult();

Limitations

Search has some limitations.

  • Cross datasource search is not supported (i.e. you can't build a single query to search for master and supplier articles simultaneously).

  • Only root entities can be searched

  • Search operates on EntityType repository level and generic search on Entity level is not possible. However if some kind of entity classifier is defined then you can use it in search expression (see ExportTemplate Example).

  • Search is not public API so far. If you are using search api you have to implement junit test to check if results are correct. This is especially true if you are using complex expressions with different logical keys

  • Non-standard logical keys may not work in search expressions or deliver wrong results. Such logical keys are not field based i.e. they do not have 'Field Type' attribute in the repository.

Debug

In case of error or wrong search results you can trace the internal search query and search filter by enabling 'com.heiler.ppm.search.server.internal.service' log category in the server log4j.xml.

Debug search
<category name="com.heiler.ppm.search.server">
<priority value="TRACE"/>
</category>

HSQ - Heiler Search Query language

HSQ is a search expression language which can be used to define entity search queries in a textual form (similar to SQL but more domain model oriented). Since it is currently only used in the context of the Rest Service API, its syntax is described in REST Search Query Language.

You can use the HSQParser class to parse a hsq query and build a SearchExpression which can then be used with the SearchService.

HSQ Example
...
 
ConvertUtils convertUtils = ConvertUtils.getInstance();
EntityProxyFactory entityProxyFactory = EntityManagementComponent.getEntityProxyFactory();
EntityManagerRegistry managerRegistry = EntityManagementComponent.getManagerRegistry();
PermissionService permissionService = EntityManagementComponent.getPermissionService();
EntityProxyParser proxyParser = new EntityProxyParser( convertUtils, entityProxyFactory, managerRegistry );
RepositoryService repositoryService = RepositoryComponent.getRepositoryService();
NavigationService navigationService = RepositoryComponent.getNavigationService();
EnumProviderRegistry enumProviderRegistry = EntityManagementComponent.getEnumProviderRegistry();
FieldParser fieldParser = new FieldParser( repositoryService, navigationService, convertUtils, enumProviderRegistry, proxyParser, permissionService );
 
HSQParser hsqParser = new HSQParser( fieldParser, convertUtils );
 
String hsq = "Article.EAN in (\"123456789\", \"987654321\", \"abcdefghij\") AND not (Article.Identifier = \"abc\" OR Article.Identifier equals \"xyz\")";
 
Expression expression = hsqParser.parseSearchQuery( hsq );
 
SearchParameters searchParameters = new SearchParameters( dataSource.getIdentifier(), entityType.getIdentifier(), expression );
searchParameters.setRevisionFilter( new RevisionSearchFilter( revisionToken ) );
 
SearchService searchService = EntitySearchComponent.getSearchService();
 
ReportResult reportResult = searchService.searchAsReport( progressMonitor, searchParameters, reportResultFilter );