Using the GraphQL Search API to search

  Written by The Jahia Team
   Estimated reading time:

This topic shows you how to set up and use Jahia’s full-text search GraphQL API. The API requires you to have Elasticsearch available to your system. 

Writing search queries

This section shows you how how to create a basic query, and specify sort, basic and advanced filters, and facets options. 

Writing a basic query

The section shows you how to create a basic query. The examples use the Digitall demo website that is bundled with Jahia.

A search query is composed of several parts: 

  • searches wrapper
    The searches wrapper specifies siteKey, language and workspace properties and allows you to have multiple search queries which execute at the same time. Here’s an example of what the start of your query might look like.
    {
      jcr {
        searches(siteKey: "digitall", language: "en", workspace: LIVE) {
          ...
        }
      }
    }
  • search object
    You can use a search object to specify the parameters of your search. The search object must have q which stands for query and is the only mandatory parameter. Here’s an example of using the q parameter.
    {
      jcr {
        searches(siteKey: "digitall", language: "en", workspace: LIVE) {
          search(q:"news") {
            took
            totalHits
            hasMore
            hitsOnPage
            hits {
              node {
                name
              }
              lastModified
              lastPublished
              lastModifiedBy
              lastPublishedBy
              displayableName
            }
          }
        }
      }
    }
  • searchIn parameter
    You can also specify whether you want to search files or content or both by specifying a value for the searchIn parameter as shown below. By default (if you don’t provide a searchIn parameter), the search is performed in both files and content indices.
    searchIn:[CONTENT]
    {
      jcr {
        searches(siteKey: "digitall", language: "en", workspace: LIVE) {
          search(q:"news", searchIn:CONTENT) {
            took
            totalHits
            hasMore
            hitsOnPage
            hits {
              node {
                name
              }
              lastModified
              lastPublished
              lastModifiedBy
              lastPublishedBy
              displayableName
            }
          }
        }
      }
    }
  • search properties
    Search gives you access to the following properties:
    • took
      The time it took to search
    • totalHits
      The total number of available search results
    • hasMore
      Whether there are more results on next page
    • hitsOnPage
      The number of hits returned on the current page
    • hits
      Individual results

      The hits object accepts the following properties:
      • node
        Wrapper for JCR node, which gives you access to any property of the node, children, permissions, and more
      • displayName
        Display name of the returned node
      • lastModified
        Date when content was last modified (includes node and indexed subnodes)
      • lastPublished
        Date when content was last published (includes node and indexed subnodes)
      • lastModifiedBy
        User who made the latest change
      • lastPublishedBy
        User who last published the node
  • Limit and offset parameters
    You can specify limit and offset parameters to further restrict your search. The total offset is calculated using limits. For example, a limit of 5 and an offset of 2 returns results from the 10th entry to the 15th, provided that they exist. In other words, offset is a page number for a page of size limit.
    {
      jcr {
        searches(siteKey: "digitall", language: "en", workspace: LIVE) {
          search(
            q:"news" 
            searchIn:[CONTENT]
            limit:2 
            offset:0) {
            took
            totalHits
            hitsOnPage
            ... 
          }
        }
      }
    }

Now that you know how to write a basic text search query and use constraints, let’s look at some additional functionality which will help you refine your search further.

Sorting

Sort just requires two ingredients: a property to sort on and a sorting direction. Here’s an example of adding sort on jcr:title to a query that sorts in descending order.

{
  jcr {
    searches(siteKey: "digitall", language: "en", workspace: LIVE) {
      search(... 
          sortBy:{property:"jcr:title", orderType:DESC}) {
       ...
      }
    }
  }
}

Filtering

The following types of filters are supported:

  • author
    The last user to publish or modify content
  • published
    Date published
  • modified
    Date modified 
  • created
    Date created 
  • nodeType
    Filter on node type

Let’s look at some filters in detail. The nodeType filter filters out documents that don’t match the specified node type. You can also indicate whether you want to include subnodes or not. The example filters on page nodes and their subnodes. The author filter only takes one parameter value, which is a string representing a user’s name. 

{
  jcr {
    searches(siteKey: "digitall", language: "en", workspace: LIVE) {
      search(..., 
             sortBy:{...},
           filter:{
               nodeType:{nodeType:"jnt:page", includeSubNodes:true}   
               author:{value:"root"}}})  {
       ...
      }
    }
  }
}

A date filter can take a string representation of a date or date math expression. Three constraint types are available:

  • before
    Less than (<)
  • after
    Greater than (>) 
  • equal
    Greater than or equal to (>=) and less than or equal to (<=).
    Note: If you provide an equal constraint, before and after are ignored by your search.

You can use a single filter or combine all four filters together for added flexibility. Here’s an example of a query that uses two filters.

{
  jcr {
    searches(siteKey: "digitall", language: "en", workspace: LIVE) {
      search(..., 
             sortBy:{...},
           filter:{author:{value:"root"}, published:{before:"now-1d/d"}})  {
       ...
      }
    }
  }
}

Advanced filtering

Advanced filtering gives you full control of the property you want to filter on. You use a custom object to specify term, dateRange and numberRange filters. This allows you to have one or more filters of the same type (term, number range, date range), which can be ANDed or ORed together for greater flexibility. Here’s an example of a term filter.

{
  jcr {
    searches(siteKey: "digitall", language: "en", workspace: LIVE) {
      search(..., 
             sortBy:{...},
           filter:{
          custom:{
            term:[{operation:OR, terms:[{field:"jcr:createdBy" value:"root"}]}]
          }
        }
  {
       ...
      }
    }
  }
}

In this case, the result set is filtered to contain entries created by “root”, the use of OR is not really necessary and is here just for demonstration purposes. Note that this is similar to the above example of author filter, the difference is that the author filter is a shortcut designed for convenience, while the term group can be used to filter on any property.

Facets

Facets allow you to see groupings based on similar or exact characteristics of a property. For example, you can see how many documents were created by each user or how many documents fall in a given creation date range. 

Similar to filter, you can use term, numberRange and dateRange objects to specify the type of facet you require. Each facet allows you to specify field, whether it is disjunctive or not, and selections. The selection object varies depending on the type of facet. Here’s how term and date range facets look in a query. Note that date math expressions are fully supported. 

{
  jcr {
    searches(siteKey: "digitall", language: "en", workspace: LIVE) {
      search(..., 
             sortBy:{...},
           filter:{...},
        facets:{
                term:[{field:"jcr:createdBy"}],
                  dateRange: [{field: "jcr:lastModified", 
                    ranges: [{from: "now-1M", to: "now", 
                              name: "last month"}, 
                             {from: "now-1y", to: "now", 
                              name: "last year"}]}]
          })  {
       ...
      }
    }
  }
}

To correctly retrieve values for your facets, you must specify which value you want to resolve: TermValue or DateRangeValue.

facets {
  field
  data {
    ...on TermValue {
      value
      count
    }
    ...on DateRangeValue {
      range {
        from
        to
        name
      }
     count
    }
  }
}

To filter on facets, you need to specify one or more selections in the selections list. For term facets, selections is a list of strings.

term:[{field:"jcr:createdBy" selections:["root"]}]

For range facets, it is an object describing the range. 

dateRange: [{field: "jcr:lastModified", 
              ranges: [{from: "now-1M", to: "now", name: "last month"}, 
                        {from: "now-1y", to: "now", name: "last year"}] 
              selections:[{from: "now-1M", to: "now", name: "last month"}]}]

In this example, the set of results contains only the documents that were created by root and match the modified date criteria.

That’s it, full text search API is that simple.