• Document Up to Date
  • Updated On 4.0.0

Search with Elasticsearch

Querying Content

To see the types of content queries you can make in CrafterCMS, please see Basic Query Mechanics

Multi-index Query

CrafterCMS supports querying more than one Elasticsearch index in a single query.

To search your site and other indexes, simply send a search query with a comma separated list of indexes/aliases (ES pointer to an index). It will then search your site and the other indexes

../../../../_images/craftercms-multi-index-query.svg

Remember that all other Elasticsearch indexes/aliases to be searched need to be prefixed with the site name like this: SITENAME_{external-index-name}. When sending the query, remove the prefix SITENAME_ from the other indexes/aliases.

Here’s how the query will look like for the above image of a multi-index query for the site acme (the SITENAME), and the CD database index acme_cd-database:

Search multiple indexes - Groovy example
1def result = elasticsearch.search(new SearchRequest('cd-database').source(builder))

Search multiple indexes - REST example
1curl -s -X POST "localhost:8080/api/1/site/elasticsearch/search?index=cd-database" -d '
2{
3  "query" : {
4    "match_all" : {}
5  }
6}
7'

See here for more information on the Crafter Engine API search.

CrafterCMS supports the following search query parameters:

  • indices_boost
  • search_type
  • allow_no_indices
  • expand_wildcards
  • ignore_throttled
  • ignore_unavailable

See the official docs for more information on the above parameters.

For more information on indices_boost, see here

Implementing a Type-ahead Service

In this section, we will be looking at how to use a query to provide suggestions as the user types.

../../../../_images/search-typeahead-box.webp ../../../../_images/search-typeahead-suggestions.webp

Build the Service

Create a REST service that returns suggestions based on the content in your site.

Requirements

  • The service will take the user’s current search term and find similar content.
  • The service will return the results as a list of strings

To create the REST endpoint, place the following Groovy file in your scripts folder

/scripts/rest/suggestions.get.groovy
1  import org.craftercms.sites.editorial.SuggestionHelper
2
3  // Obtain the text from the request parameters
4  def term = params.term
5
6  def helper = new SuggestionHelper(elasticsearchClient)
7
8  // Execute the query and process the results
9  return helper.getSuggestions(term)

You will also need to create the helper class in the scripts folder

/scripts/classes/org/craftercms/sites/editorial/SuggestionHelper.groovy
 1  package org.craftercms.sites.editorial
 2
 3  import co.elastic.clients.elasticsearch.core.SearchRequest
 4  import org.craftercms.search.elasticsearch.client.ElasticsearchClientWrapper
 5
 6  class SuggestionHelper {
 7
 8      static final String DEFAULT_CONTENT_TYPE_QUERY = "content-type:\"/page/article\""
 9      static final String DEFAULT_SEARCH_FIELD = "subject_t"
10
11      ElasticsearchClientWrapper elasticsearchClient
12
13      String contentTypeQuery = DEFAULT_CONTENT_TYPE_QUERY
14      String searchField = DEFAULT_SEARCH_FIELD
15
16      SuggestionHelper(elasticsearchClient) {
17              this.elasticsearchClient = elasticsearchClient
18      }
19
20      def getSuggestions(String term) {
21                def queryStr = "${contentTypeQuery} AND ${searchField}:*${term}*"
22                def result = elasticsearchClient.search(SearchRequest.of(r -> r
23                      .query(q -> q
24                              .queryString(s -> s
25                                      .query(queryStr)
26                              )
27                      )
28                ), Map)
29
30                return process(result)
31          }
32
33      def process(result) {
34              def processed = result.hits.hits*.getSourceAsMap().collect { doc ->
35                      doc[searchField]
36              }
37              return processed
38      }
39
40  }

Once those files are created and the site context is reloaded you should be able to test the REST endpoint from a browser and get a result similar to this:

http://localhost:8080/api/1/services/suggestions.json?term=men
1[
2  "Men Styles For Winter",
3  "Women Styles for Winter",
4  "Top Books For Young Women",
5  "5 Popular Diets for Women"
6]

Build the UI

The front end experience is built with HTML, Javascript and specifically AJAX.

Requirements

  • When the user types a value send a request to the server to get instant results
  • Display the results and show suggestions about what the user might be looking for
  • Do not fire a query for every keystroke. This can lead to more load than necessary, instead, batch user keystrokes and send when batch size is hit or when the user stops typing.

You can also integrate any existing library or framework that provides a type-ahead component, for example to use the jQuery UI Autocomplete component you only need to provide the REST endpoint in the configuration:

1$('#search').autocomplete({
2  // Wait for at least this many characters to send the request
3  minLength: 2,
4  source: '/api/1/services/suggestions.json',
5  // Once the user selects a suggestion from the list, redirect to the results page
6  select: function(evt, ui) {
7    window.location.replace("/search-results?q=" + ui.item.value);
8  }
9});