Querying and Searching
Queries and searches are two very different concepts, but both will follow the same flow: provide parameters and iterate on results. This tutorial will provide practical usage examples of both concepts as well as references to relevant documentation entries.
Before you begin
Jahia modules rely on Java and Maven. The key requirements are as follows:
- Access to a local Jahia system or the free cloud trial from Jahia
- Oracle JDK 11 or OpenJDK 11
- Maven 3.3+
- Completion of the following tutorials:
- The IDE of your choice
- Access to the Jahia administration console
What you will learn
Query
- A query is a question asked in a structured format (in Jahia's case, the JCR SQL2 syntax), expecting results strictly matching the request's parameters. Queries are executed by asking the JCR SQL API available from JSPs, Java code and GraphQL endpoints.Queries are most often used to aggregate content and build lists and menus.
Search
- Search is designed to gather inputs from a human (or any other entity capable of typing on a keyboard!), and aimed at returning results ordered by relevance. Parameters are not strictly enforced to allow for typos and gathering results not matching all terms at once. Search results include fancy features, such as keyword highlighting and 'did you mean...' recommendation.
Queries
- This query tutorial will guide you through the querying and displaying of a list of pages on a specific site in a JSP. This query can be adapted for any other type of objects.
Query syntax
- This tutorial will not cover the syntax of the JCR SQL2 grammar. Instead, you can refer to the JCR SQL2 cheat sheet that provides plenty of examples.
Querying in a JSP
- Jahia provides the JSTL functions to run queries, using the JCR SQL2 syntax. Here is an example of using the
<jcr:sql
library:
<jcr:sql var="pages" sql="select * from [jnt:page] as page where isdescendantnode(page,['${renderContext.site.path}'])"/>
The query will select all the objects of type jnt:page
under the current site, and store an iterator (a list) of results in the variable pages
.
Iterating on results
Results stored by jcr:sql
are stored in an iterator who will return a list with the accessor .nodes
<c:forEach items="${pages.nodes}" var="node">
<template:module node="${node}" view="link" />
</c:forEach>
In the previous piece of code, we are storing each page in a variable node
, and displaying its view link
, which is a simple HTML link to the page.
Querying best practices
Queries can be resource intensive when not optimized. Some easy optimizations can make a big difference:
- Restrict the path under which the query is made using
isdescendantnode
- Query exactly the required nodetype. Instead of gathering all nodes of type jnt:content, and then filtering on the type of each result afterwards, create a mixin applied to all types of nodes you want to search for, and query this mixin.
Searches
Searching is an experience composed of two steps: gathering inputs, and displaying the results. Since searching is targeted at humans, the search experience is a key aspect to be taken into consideration.
Search form
The following code displays a vanilla HTML form to perform searches with the following parameters:
- The search is going to be performed on the current site only
- The search is going to be performed on the current user locale (language) only
- Only results (hits) matching all words of the input will be returned
<c:url value="${currentNode.properties.result.node.url}" var="searchUrl"/>
<s:form method="post" action="${searchUrl}" role="search">
<div class="search_form">
<s:term match="all_words" id="searchTerm" searchIn="siteContent,tags" class="form-control" placeholder="Search term"/>
<s:site value="${renderContext.site.name}" includeReferencesFrom="systemsite" display="false"/>
<s:language value="${renderContext.mainResource.locale}" display="false"/>
<span class="input-group-btn">
<button type="button" onclick="document.searchForm.submit()" value="Search">
</button>
</span>
</div>
</s:form>
The variable ${currentNode.properties.result}
is expected to contain a reference to the search result's page. The search form content type should contain a property defined as follows:
[jnt:customSearchForm] > jnt:content, jmix:structuredContent
- result (weakreference, type=['picker=page'])
When putting this content on a page via composer, the following field should be displayed:
The search results page is automatically created under the home page and can be picked as follows:
For more information about the taglib options, please refer to the Search and Queries documentation.
Search results
Search results are extracted by the function s:results
and put on a Map object.
<s:results var="resultsHits" approxCountVar="listApproxSize">
<c:set target="${moduleMap}" property="listTotalSize" value="${count}" />
<c:set target="${moduleMap}" property="resultsHits" value="${resultsHits}" />
<c:set target="${moduleMap}" property="listApproxSize" value="${listApproxSize}" />
</s:results>
<s:resultIterator begin="${moduleMap.begin}" end="${moduleMap.end}" varStatus="status" hits="${moduleMap['resultsHits']}">
<li>
<%--<span>${status.index+1}.</span>--%>
<%@ include file="searchHit.jspf" %>
</li>
</s:resultIterator>
The code above will store the search results as well as some metadata on the moduleMap, and iterate through each result. The moduleMap is a Hashmap used by various Jahia modules to store and fetch data between views. In this instance, the s:result taglib stores the search results in the moduleMap and the current view passes it to s:resultIterator to display each result individually.
The searchHit.jspf's source code can be found here, and forked according to your needs. This file needs to be stored in the same folder as your search result view file.