Expand my Community achievements bar.

Applications for the 2024-2025 Adobe Experience Manager Champion Program are open!
SOLVED

Optimized Query Builder query for getting search result count for each facet.

Avatar

Level 4

Hi All,

I want to show search result count beside the each search facets which acts as filter on the search result returned by a free text search. For example I search "car" and my facets (filters) are Audi, Mercedes, BMW. So when end user clicks on these facets they only see pages which are tagged with that particular tag. I am able to fetch result by writing simple query builder query and logic is working perfectly fine.  

Now I want to enhance my functionality and show number of search result beside each tag like Audi (10), Mercedes (23), BMW(18).

1. Simplest approach which comes to my mind is to fire separate query for each facet and get the search result count for them (i.e. 4 querys for this case and goes bad a number of search facet increases). This will add huge load on my server and would create performance issues.

2. Get all the result back and while processing them in JSP, I read all the pages and keep count of all the tags I encounter. Problem with this approach is as I am implementing pagination for search results (i.e I show only 10 results per page).  Thus I have used predicates like p.offset and p.limit which will never give me whole search result, but rather only required 10 search result according to query parameter found in my search URL. I don't changing this to return all the results would be good solution as I have to start skipping result on the basis of pagination.

3. Third approach which I believe would be the best solution is to use Bucket API provided by CQ, which I assume would be able to get context of my search tags and thus automatically give me back counts without firing any extra query. But I am not able to totally understand how to use this com.day.cq.search.facets and com.day.cq.search.facets.buckets. I do see that OOTB search component does uses bucket API effectively and also get the required count for search result as required. It would be great if any one can tell me some pointers around this buckets API and how does the OOTB code works?

Thanks for your help in advance.

OOTB code http://localhost:4502/crx/de/index.jsp#/libs/foundation/components/search/search.jsp 

<c:if test="${result.facets.tags.containsHit}">
              <p><%=i18n.get("Tags")%></p>
              <c:forEach var="bucket" items="${result.facets.tags.buckets}">
                  <c:set var="bucketValue" value="${bucket.value}"/>
                  <c:set var="tag" value="<%= tm.resolve((String) pageContext.getAttribute("bucketValue")) %>"/>
                  <c:if test="${tag != null}">
                      <c:set var="label" value="${tag.title}"/>
                      <c:choose>
                          <c:when test="<%= request.getParameter("tag") != null && java.util.Arrays.asList(request.getParameterValues("tag")).contains(pageContext.getAttribute("bucketValue")) %>">
                            ${label} (${bucket.count}) - <a title="filter results" href="<c:url value="${currentPage.path}.html"><c:param name="q" value="${escapedQueryForHref}"/></c:url>">remove filter</a>
                          </c:when>
                          <c:otherwise>
                              <a title="filter results" href="
                            <c:url value="${currentPage.path}.html">
                                <c:param name="q" value="${escapedQueryForHref}"/>
                                <c:param name="tag" value="${bucket.value}"/>
                            </c:url>">
                                      ${label} (${bucket.count})
                              </a>
                          </c:otherwise>
                      </c:choose><br/>
                  </c:if>
              </c:forEach>
          </c:if>

1 Accepted Solution

Avatar

Correct answer by
Level 4

Hi All,

I have made is working and now I think I got some understanding how it can be done. Earlier for forming my Query Builder query I was creating a hashmap and then was using

  query = queryBuilder.createQuery(PredicateGroup.create(PredicateParamMap), session); This somehow was not able to detect tags as facets. Now I have changed my code and using predicate group as below:-

SearchResult resultNew = null; // PredicateGroup group = new PredicateGroup(); // group.add(new Predicate("tags", "tagid").set("property", "jcr:content/cq:tags")); // tagPredicate.set("tagid", request.getParameter("tag")); //group.add(new Predicate("mytype", "type").set("type", "nt:file")); final QueryBuilder queryBuilder = getResourceResolver().adaptTo(QueryBuilder.class); final Session session = (Session) getResourceResolver().adaptTo(Session.class); PredicateGroup group = new PredicateGroup(); Predicate tagPredicate = new Predicate("tags", "tagid"); tagPredicate.set("property", "jcr:content/cq:tags"); tagPredicate.set("tagid", "properties:style"); group.add(tagPredicate); Query queryNew = queryBuilder.createQuery(group, session); resultNew = queryNew.getResult(); Map<String, Facet> facets = resultNew.getFacets(); for (String key : facets.keySet()) { System.out.println("Facet Key " + key); Facet facet = facets.get(key); System.out.println("Facet " + facet.toString()); if (facet.getContainsHit()) { for (Bucket bucket : facet.getBuckets()) { String value = bucket.getValue(); long count = bucket.getCount(); System.out.println("Value == "+ value + " count == "+ count); Map<String, String> params = bucket.getPredicate().getParameters(); System.out.println("\n Entering inside the search resuts \n"); for (String k : params.keySet()) { System.out.println("k == " + k); } } } }

Now I see tags getting detected as facets and buckets are getting generated for each associated tag

View solution in original post

3 Replies

Avatar

Level 9

I think you found the best way to implement the solution using option 3.  Here are some info on facets and buckets

Facets are set of possible values found in current results,  they provide options for  more specific query

Facet are set of buckets,  Buckets can be product, business, marketing in your case it is Audi, Mercedes and BMW

Buckets can be custom ranges too like 

Facet = date range

Bucket = yesterday, last week, last year, last century and soon

You may find more information from the following link

docs.adobe.com/docs/en/cq/5-3/dam/customizing_and_extendingcq5dam/query_builder.html

As per the API here are the definitions of Facets and Buckets

         
BucketBucket represents a single element of a search result categorization.
FacetFacet represents a single category that splits up a search result into multiple buckets.

Avatar

Level 4

Hi Shaji,

Thanks for the quick reply, I have already gone through the documents. I need some help in usage of API. :)

Avatar

Correct answer by
Level 4

Hi All,

I have made is working and now I think I got some understanding how it can be done. Earlier for forming my Query Builder query I was creating a hashmap and then was using

  query = queryBuilder.createQuery(PredicateGroup.create(PredicateParamMap), session); This somehow was not able to detect tags as facets. Now I have changed my code and using predicate group as below:-

SearchResult resultNew = null; // PredicateGroup group = new PredicateGroup(); // group.add(new Predicate("tags", "tagid").set("property", "jcr:content/cq:tags")); // tagPredicate.set("tagid", request.getParameter("tag")); //group.add(new Predicate("mytype", "type").set("type", "nt:file")); final QueryBuilder queryBuilder = getResourceResolver().adaptTo(QueryBuilder.class); final Session session = (Session) getResourceResolver().adaptTo(Session.class); PredicateGroup group = new PredicateGroup(); Predicate tagPredicate = new Predicate("tags", "tagid"); tagPredicate.set("property", "jcr:content/cq:tags"); tagPredicate.set("tagid", "properties:style"); group.add(tagPredicate); Query queryNew = queryBuilder.createQuery(group, session); resultNew = queryNew.getResult(); Map<String, Facet> facets = resultNew.getFacets(); for (String key : facets.keySet()) { System.out.println("Facet Key " + key); Facet facet = facets.get(key); System.out.println("Facet " + facet.toString()); if (facet.getContainsHit()) { for (Bucket bucket : facet.getBuckets()) { String value = bucket.getValue(); long count = bucket.getCount(); System.out.println("Value == "+ value + " count == "+ count); Map<String, String> params = bucket.getPredicate().getParameters(); System.out.println("\n Entering inside the search resuts \n"); for (String k : params.keySet()) { System.out.println("k == " + k); } } } }

Now I see tags getting detected as facets and buckets are getting generated for each associated tag