I couldn't get this to work previously, but after upgrading to Drupal 9.4 it now works. What I did was this:
- I added a new field "autocomplete_tokens" as an "aggregated_field". I put all the text fields in there that were part of the search, especially the rendered HTML and the file contents.
- As a field type I chose "Fulltext Tokens", which is defined as "Full text field without any processing like stemming or stop word filters, just unmodified tokens from the text separated by white spaces". This way, all the words in the input stay exactly as they are. No stemming, no breaking up ito ngrams.
- In the "autocomplete" tab of my index, I clicked on "Edit" and chose the suggester "Retrieve from server". Under "Configure suggester Retrieve from server > Override used fields" I chose the new field "autocomplete_tokens". This way, the autocomplete feature got its suggestions only from my new field, not from all the other stemmed and ngrammed fields.
And that's it, this time it worked like a charm. Reindex all contents (since a new field was added and needs to be present in the index), and the autocomplete now behaves as expected. I can only guess that I did something stupidly wrong or that something changed with Drupal 9.4.
This solution has some caveats, of course. First, when I add a new field to be part of the search (e.g. for a new content type), I have to remember to add it to the autocomplete token concatenation field as well. This can easily be forgotten. On the other hand, I also have to add the new field to the view and maybe other places, so this needs to be tested anyway.
I also found that this datatype does not remove punctuation from the tokens. So if my content contains e.g. "unbowed, unbent, unbroken", the autocomplete suggestion for "unb" is "unbowed," and "unbent," along with "unbroken" (or "unbroken.", if it ends as a sentence). This can maybe dealed with using a different field type or adding a special field type to the schema.xml. Since I wanted to close this issue fast, I added a filter instead to my local search module:
function my_module_search_api_autocomplete_suggestions_alter(array &$suggestions, array $alter_params) {
$allNewSuggestions = [];
foreach ($suggestions as $index => $suggestion) {
// Remove punctuation from the end of autocomplete suggestions.
$newSuggestionSuffix = rtrim($suggestion->getSuggestionSuffix(), ".!?,-'\"");
$suggestion->setSuggestionSuffix($newSuggestionSuffix);
// Now remove duplicates that occur if two different input suggestions are
// reduced to the same token when removing the punctuation.
$newSuggestion = $suggestion->getSuggestionPrefix() . $newSuggestionSuffix;
if (in_array($newSuggestion, $allNewSuggestions)) {
unset($suggestions[$index]);
continue;
}
$allNewSuggestions[] = $newSuggestion;
}
}
This removes all punctuation from the end of the suggestions and removes duplicates (if a word was suggested one with a trailing comma and once with a trailing full stop, for example). Now we might have fewer than the configured number of sugestions, but I can live with that.
I also notice that all the suggestions are in lower-case, which looks a bit strange for German nouns. But for now I can also live with that.