--- myst: html_meta: "description": "" "property=og:description": "" "property=og:title": "" "keywords": "" --- # More Features Next we will cover some more advanced topics which need configuration on Plone and Solr side. Features like autocomplete and suggest ("did you mean ...") are often requested when it comes to search. They are perfectly doable with the Plone / Solr combination. At the end of this chapter we will build a full search page with autocomplete, suggest, term highlighting and faceting turned on. Let's see how and start with autocomplete: ## Autocomplete For autocomplete we need a special Solr handler because we don't search full terms but only part of terms. With the additional Solr configuration, autocomplete can be called via URL directly: ``` http://localhost:8080/Plone/@@solr-autocomplete?term=Pl ``` Which gives the response ``` [ { "value": "Willkommen bei Plone", "label": "Willkommen bei Plone" } ] ``` {file}`solr.cfg` ```ini [solr-instance] recipe = collective.recipe.solrinstance ... name:title_autocomplete type:text_auto indexed:true stored:true name:description_autocomplete type:text_desc indexed:true stored:true additional-solrconfig = explicit edismax 10 description_autocomplete,title_autocomplete,score title_autocomplete^30 description_autocomplete^50.0 title_autocomplete^30 description_autocomplete^50.0 true title_autocomplete description_autocomplete score desc score desc extra-field-types = additional-schema-config = ``` For the search template we utilize the HTML5 datalist element to populate the search input field. {file}`search.pt`: ```html ``` ## Suggest The suggest (did you mean ...) feature is well known from popular search engines. It is integrated into Solr as a component which needs to be enabled and configured. Here is an example configuration which works with {py:mod}`collective.solr`. If you change it, stick to the names of the parameters and handlers. The JSON view of Plone can be called with this URL: `http://localhost:8080/Plone/@@search?format=json&SearchableText=Plane` And from JavaScript ```http GET http://localhost:8080/Plone/@@search?SearchableText=Plane HTTP/1.1 Accept: application/json ``` We get a response like this ```json { "data": [], "suggestions": { "plane": { "endOffset": 87, "numFound": 1, "startOffset": 82, "suggestion": ["plone"] } } } ``` The configuration in buildout is as follows: ```ini [solr-instance] recipe = collective.recipe.solrinstance ... additional-solrconfig = SearchableText default SearchableText solr.DirectSolrSpellChecker internal 0.5 2 1 5 4 0.01 wordbreak solr.WordBreakSolrSpellChecker SearchableText true true 10 suggest org.apache.solr.spelling.suggest.Suggester org.apache.solr.spelling.suggest.fst.WFSTLookupFactory SearchableText 0.0005 true default wordbreak suggest on true 10 5 5 true true 10 5 spellcheck ``` A simple integration in our training-search is here: ```html ``` ## Facetting Facetting is tightly integrated in :py:mod::`collective.solr` and works out of the box. We will now create a full search page with faceting, autocomplete, search term highlighting and suggest enabled. The HTML of the page is mainly taken from the standard page. To reduce complexity some of the standard features like syndication, i18n and view actions have been removed: ```html

Search results 234 items matching your search terms

No results were found. Did you mean: Plone

``` Let's analyze the important parts. The head includes a reference to the {file}`showmore.js` JavaScript, which is included in :py:mod::`collective.solr` and used to reduce long lists of facets. Additionally the left column is removed on the search page. The right column is kept. No portlets will be displayed, it is used for the facets. The first thing we do in our search is getting the results for the search query, if there is one: ```python def search(self): if not self.request.get('SearchableText'): return [] catalog = api.portal.get_tool('portal_catalog') results = IContentListing(catalog(hl='true', **self.request.form)) self.has_results = bool(len(results)) b_start = self.request.get('b_start', 0) batch = Batch(results, size=20, start=b_start) return batch ``` We can use the standard Plone catalog API for getting the results. ```{note} Don't use {py:meth}`plone.api.content.find` because it "fixes" the query to match the indexes defined in ZCatalog and will strip all Solr-related query parameters. We don't want that. ``` After we have the results, we wrap it with `IContentListing` to have unified access to them. Finally we create a Batch, to make sure long result sets are batched on our search view. The next thing we have in our search view is the form itself ```python
``` We have a input field for used input. For the autocompletion we reference the datalist with the `list` attribute. For the facets we need to render the `hiddenfields` snippet, which is constructed by the `search-facets` view of `collective.solr`. This snippet will add the necessary query parameters like **facet=true&facet.field=portal_type&facet.field=review_state**. We use the `h1` element for displaying the number of elements. The next section is reserved for the *suggest* snippet: ```html ``` If no results are found with the query, a term is suggested. This term is fetched from the `collective.solr` AJAX view **suggest-terms**. The code in our view class is here ```python def suggest(self): self.request.form['term'] = self.request.get('SearchableText') suggest_view = getMultiAdapter((self.context, self.request), name='suggest-terms') suggestions = json.loads(suggest_view()) if suggestions: word = suggestions[0]['value']['word'] query = self.request.form.copy() query['SearchableText'] = word return {'word': word, 'url': '{0}?{1}'.format(self.request.getURL(), urlencode(query, doseq=1))} return '' ``` We get suggestions from the Solr handler and construct an URL for a new search with query parameters preserved. The next thing we have is the result list. There is nothing fancy in it. We show the title, which is linked to the article found and the cropped description. Finally we have the snippet for the facets in the right slot: ```
``` We call the facet view of :py:mod::`collective.solr` with our resultset and get the facets fully rendered as HTML. ```{note} We need to pass the `real` Solr response to the facet view. That's why we have to escape the batch (\_sequence) and the contentlisting (\_basesequence) ``` Now we have a fully functional Plone search with faceting, autocompletion, suggestion and term highlighting. You can find the complete example on [GitHub](https://github.com/collective/plonetraining.solr_example). ## Exercise Have a custom search page with autocomplete, suggest, highlighting and faceting working.