Search with Solr

Querying Content

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

Implementing a Type-ahead Service

There are a couple of options for creating a type-ahead or suggestions for your search:

Solr Suggester
Can leverage a dictionary or the content in your index. More details
Solr Facets
Leverages the content in your index
Solr Query
Leverages the content in your index

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

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

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// /scripts/rest/suggestions.get.groovy

import org.craftercms.sites.editorial.SuggestionHelper

// Obtain the text from the request parameters
def term = params.term

def helper = new SuggestionHelper(searchService)

// Execute the query and process the results
return helper.getSuggestions(term)

You will also need to create the helper classs in the scripts forlder

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// /scripts/classes/org/craftercms/sites/editorial/SuggestionHelper.groovy

package org.craftercms.sites.editorial

import org.craftercms.search.service.SearchService

class SuggestionHelper {

  static final String DEFAULT_CONTENT_TYPE_QUERY = "content-type:\"/page/article\""
  static final String DEFAULT_SEARCH_FIELD = "subject"

  SearchService searchService

  String contentTypeQuery = DEFAULT_CONTENT_TYPE_QUERY
  String searchField = DEFAULT_SEARCH_FIELD

  SuggestionHelper(SearchService searchService) {
    this.searchService = searchService
  }

  def getSuggestions(String term) {
    // Query documents matching a content-type and having similar words to the term
    def queryStr = "${contentTypeQuery} AND ${searchField}:*${term}*"
    def query = searchService.createQuery()
    query.setQuery(queryStr)
    def result = searchService.search(query)
    return process(result)
  }

  def process(result) {
    // Extracts only a specific field from each matched document
    def processed = result.response.documents.collect { doc ->
      doc[searchField]
    }
    return processed
  }

}

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
[
  "Men Styles For Winter",
  "Women Styles for Winter",
  "Top Books For Young Women",
  "5 Popular Diets for Women"
]

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