**Use case**

I want to estimate better parameters for fulltext (`ngrambf_v1`) index.

**Describe the solution you'd like**

Implement it as SQL function:

`ngrams(string, N)`

that returns array of FixedString.

**Describe alternatives you've considered**

It can be done with less efficient and more cumbersome way:

```
WITH 3 AS n, extractAll(text, '.') AS chars 
SELECT 
  arrayMap(x -> arrayStringConcat(x), 
    arrayFilter(x -> length(x) = n, 
      arrayMap(x, i -> arraySlice(chars, i, n), 
        chars, arrayEnumerate(chars)))) AS ngrams
```

**Additional context**

`ngramsUTF8` can also be implemented to extract ngrams of Unicode code points.
{
public:
    MergeTreeIndexFullText(
        const IndexDescription & index_,
        const BloomFilterParameters & params_,
        std::unique_ptr<ITokenExtractor> && token_extractor_)
        : IMergeTreeIndex(index_)
        , params(params_)
        , token_extractor(std::move(token_extractor_)) {}
    ~MergeTreeIndexFullText() override = default;
    MergeTreeIndexGranulePtr createIndexGranule() const override;
    MergeTreeIndexAggregatorPtr createIndexAggregator() const override;
    MergeTreeIndexConditionPtr createIndexCondition(
            const SelectQueryInfo & query, ContextPtr context) const override;
    bool mayBenefitFromIndexForIn(const ASTPtr & node) const override;
    BloomFilterParameters params;
    
    std::unique_ptr<ITokenExtractor> token_extractor;
};
}