1 HstQuery bootstrapping
Getting hold of a HstQueryManager
The HstQueryManager can be retrieved from the current request context.
-
In a HstComponent
HstComponent extending BaseHstComponent:
public class LatestItems extends BaseComponent { @Override public void doBeforeRender(HstRequest request, HstResponse response) { final HstRequestContext context = request.getRequestContext(); final HstQueryManager hstQueryManager = context.getQueryManager(); [...] } }
-
In a JAXRS Service Resource extending from org.hippoecm.hst.jaxrs.services.AbstractResource
ProductPlainResource extending AbstractResource:
@Path("/products/") public class ProductPlainResource extends AbstractResource { @GET @Path("/{productType}/") public List<ProductRepresentation> getProductResources(....) { final HstRequestContext requestContext = RequestContextProvider.get(); final HstQueryManager hstQueryManager = context.getQueryManager(); [...] } }
In cases where you don't have a HstRequestContext (for example for during some background process or for external application integration that does not involve a http request), you can get hold of the HstQueryManager to create an HstQuery directly through the Spring Component Manager:
Repository repo = HstServices.getComponentManager().getComponent(Repository.class.getName()); Credentials creds = HstServices.getComponentManager().getComponent(Credentials.class.getName() + ".default"); ContentBeansTool cbt = HstServices.getComponentManager().getComponent(ContentBeansTool.class.getName()); Session session = null; try { session = repo.login(creds); HstQueryManager queryManager = cbt.createQueryManager(session); } finally { if (session != null) session.logout(); }
Creating a new HstQuery
Once you have the HstQueryManager, there are multiple ways to create a new HstQuery. Below, we explain the usual way to do this. Other ways are very similar, also see the Javadocs from org.hippoecm.hst.content.beans.query.HstQueryManager.
HstQueryManager#createQuery:
/** * Creates a query, with scope HippoBean and Filter for types of filterBean. If * includeSubTypes is <code>true</code>, the result may also contain * HippoBean's whose primarytype is a subtype of the filterBean type. * * @param scope * @param filterBean * @param includeSubTypes * @return a new <code>{@link HstQuery}</code> with scope & filter * @throws QueryException */ HstQuery createQuery(HippoBean scope, Class<? extends HippoBean> filterBean, boolean includeSubTypes) throws QueryException;
In the code above the createQuery method takes three arguments:
-
HippoBean scope : The scope to search below in the repository. A HippoBean is backed by a JCR Node, which represents a location in the content tree of the repository. The scope identifies the sub-tree in the repository for searching documents. Note that instead of argument HippoBean, there are also methods in the HstQueryManager that directly take the JCR Node as scope, for example HstQuery createQuery(Node node, Class<? extends HippoBean> filterBean, boolean includeSubTypes);
-
Class<? extends HippoBean> filterBean : This is a filter for the possible hits. Only hits of type filterBean will be part of the result. Depending on the last argument, includeSubTypes, also subtypes are inluded in the result.
-
includeSubTypes : when true, also subtypes of the filterBean can be part of the result.
Here's an example code snippet for creating a new HstQuery in an HstComponent extending from BaseHstComponent:
HippoBean scope = request.getRequestContext().getSiteContentBaseBean(); try { // create the query to search below 'scope', return beans that are of type // BaseDocument bean or a subclass/sub-jcr-types HstQuery hstQuery = request.getRequestContext().getQueryManager().createQuery(scope, BaseDocument.class, true); [...] } catch (QueryException e) { throw new HstComponentException("Exception occured during creation of HstQuery.", e); }
Setting a limit and offset
In general it is always best to set a limit. If you do not specify a limit, the HST by default will set a limit of 1000. The best way is to set the limit equal to the number of items you want to show, for example pageSize. If you combine this with offset, you can easily always fetch the correct items.
For example, if pageSize = 10, and you want to show the 3rd page ( currentPage = 3), use:
hstQuery.setLimit(pageSize); hstQuery.setOffset(pageSize * (currentPage - 1));
Now, there is one more catch. If you have set a limit of, say, 10, then, HstQueryResult#getSize() will return at most 10. So, what if you need to know how many pages there are? For this, you can use getTotalSize(). This method returns the total number of hits.
Always set a limit, preferably to just the number you need to show
If you don't set a limit, HST sets a limit of 1000 for safety. This is already large, and might result in slower searches. For optimal performance, set a limit (and optionally an offset).
Sorted results
The HstQuery can "order by" / "sort by" multiple properties, ascending or descending. The sorting is done according to the order in which the properties to sort on are added. Sorting can only be done on properties (meta-data) which are directly stored on the document. Properties which are part of a Compound in a document can not be used for sorting.
For example:
Sorting / Ordering:
// first sort the results on the "example:date" descending (newest first) hstQuery.addOrderByDescending("example:date"); // if there are hits with the same "example:date", we then sort ascending by // "example:title" hstQuery.addOrderByAscendingCaseInsensitive("example:title"); // if there are hits with the same date and title, we sort descending by // "hippostdpubwf:publicationDate" hstQuery.addOrderByDescending("hippostdpubwf:publicationDate"); // etc
Sorting can only be done on properties directly on the documents
Also see Sorting search results in a query can only be done on direct properties of Documents
Including or excluding extra scopes
An HstQuery can search in multiple scopes, and also, some scopes can be excluded. This can be done by addScope and excludeScope :
Including extra or excluding scopes:
/** * add scopes to search in. * If the exact scope is already added to exclude from the search, it is * removed from the excluded list. * @param scopes */ void addScopes(List<HippoBean> scopes); /** * add scopes to search in. * If the exact scope is already added to exclude from the search, it is * removed from the excluded list. * @param scopes */ void addScopes(Node[] scopes); /** * add scopes to exclude from search. * If the exact scope is already added as a scope to search in, it is * removed from there * @param scopes */ void excludeScopes(List<HippoBean> scopes); /** * add scopes to exclude from search. * If the exact scope is already added as a scope to search in, it is * removed from there * @param scopes */ void excludeScopes(Node[] scopes);
Assuming that I want to search through the entire content of the current site and through the assets I can use something like:
HippoBean siteScope = getSiteContentBaseBean(request); // because we will also search in assets, make sure the filter is not project // specific but HippoDocument.class or if you do not want to use // HippoDocument.class ,set explicitly all bean classes you want to search for HstQuery hstQuery = queryManager.createQuery(siteScope, HippoDocument.class, true); // extra scopes to search in List<HippoBean> extraScopes = new ArrayList<HippoBean>(); // also search in assets extraScopes.add(getAssetBaseBean(request)); hstQuery.addScopes(extraScopes); // scopes to exclude, for example 'comments' List<HippoBean> excludeScopes = new ArrayList<HippoBean>(); HippoBean commentsScope = siteScope.getBean("comments", HippoFolderBean.class); excludeScopes.add(commentsScope); hstQuery.excludeScopes(excludeScopes);