Site search filter by category and highlight results

Silverstripe Version: 4*

Question:
Is it possible to refine the default search? I would like to include only the results from certain categories.
I would also like to highlight the the searched keyword and trim the text on both sides. So I would get ~ 100 characters before and ~100 characters after the highlighted keyword. I tried extending the ContentControllerSearchExtension but didn’t get it to work. Is there a simple way to filter the results and add <span class="highlight-keyword">keyword</span> or should I create the search from scratch?

If anyone is interested - I extended the search form
class MySearchForm extends SearchForm
and edited the $result

public function getResults() {
...
    foreach ($results as $result) {
       $result->Content = str_replace($keywords, '<span class="highlight-search-result">' . $keywords . '</span>', $result->Content);
    }
...
}

Yes, it is possible to refine the default search in SilverStripe to include only results from certain categories and highlight the searched keyword in the results.

To filter the search results by category, you can use the addFilter() method of the FulltextSearchable class. This method allows you to specify a filter function that will be applied to the search results before they are returned.

Here is an example of how you might use the addFilter() method to filter the search results by category:

use SilverStripe\CMS\Search\SearchForm;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\FulltextSearchable;

class MySearchForm extends SearchForm
{
  public function getResults($data, $form)
  {
    $results = parent::getResults($data, $form);

    $category = $data['Category'];
    $results->addFilter(function ($item) use ($category) {
      if ($item instanceof DataObject && $item->hasField('Category')) {
        return $item->Category == $category;
      }

      return true;
    });

    return $results;
  }
}

To highlight the searched keyword in the results, you can use a regular expression to search for the keyword and replace it with a highlighted version of the keyword. Here is an example of how you might do this:

use SilverStripe\View\ArrayData;
use SilverStripe\Control\Controller;

class MyController extends Controller
{
  public function search($request)
  {
    $keyword = $request->getVar('q');
    $results = MySearchForm::get_search_results($keyword);

    $highlightedResults = new ArrayList();
    foreach ($results as $result) {
      $result->Content = preg_replace(
        '/' . preg_quote($keyword, '/') . '/i',
        '<span class="highlight-keyword">$0</span>',
        $result->Content
      );

      $highlightedResults->push($result);
    }

    return $this->customise([
      'Results' => $highlightedResults,
    ])->renderWith(['MySearchResults']);
  }
}

To trim the text on both sides of the highlighted keyword, you can use the substr() function to extract a portion of the content that includes the highlighted keyword and a certain number of characters before and after it. Here is an example of how you might do this:

use SilverStripe\View\ArrayData;
use SilverStripe\Control\Controller;

class MyController extends Controller
{
  public function search($request)
  {
    $keyword = $request->getVar('q');
    $results = MySearchForm::get_search_results($keyword);

    $highlightedResults = new ArrayList();
    foreach ($results as $result) {
      $content = $result->Content;
      $highlightedContent = preg_replace(
        '/' . preg_quote($keyword, '/') . '/i',
        '<span class="highlight-keyword">$0</span>',