1<?php
2
3namespace Elastica\Suggest;
4
5use Elastica\Suggest\CandidateGenerator\AbstractCandidateGenerator;
6use Elastica\Suggest\CandidateGenerator\DirectGenerator;
7
8/**
9 * Class Phrase.
10 *
11 * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-suggesters-phrase.html
12 */
13class Phrase extends AbstractSuggest
14{
15    public const DEFAULT_REAL_WORD_ERROR_LIKELIHOOD = 0.95;
16    public const DEFAULT_CONFIDENCE = 1.0;
17    public const DEFAULT_MAX_ERRORS = 1.0;
18    public const DEFAULT_STUPID_BACKOFF_DISCOUNT = 0.4;
19    public const DEFAULT_LAPLACE_SMOOTHING_ALPHA = 0.5;
20
21    /**
22     * @return $this
23     */
24    public function setAnalyzer(string $analyzer): self
25    {
26        return $this->setParam('analyzer', $analyzer);
27    }
28
29    /**
30     * Set the max size of the n-grams (shingles) in the field.
31     *
32     * @return $this
33     */
34    public function setGramSize(int $size): self
35    {
36        return $this->setParam('gram_size', $size);
37    }
38
39    /**
40     * Set the likelihood of a term being misspelled even if the term exists in the dictionary.
41     *
42     * @param float $likelihood Defaults to 0.95, meaning 5% of the words are misspelled.
43     *
44     * @return $this
45     */
46    public function setRealWordErrorLikelihood(float $likelihood): self
47    {
48        return $this->setParam('real_word_error_likelihood', $likelihood);
49    }
50
51    /**
52     * Set the factor applied to the input phrases score to be used as a threshold for other suggestion candidates.
53     * Only candidates which score higher than this threshold will be included in the result.
54     *
55     * @param float $confidence Defaults to 1.0.
56     *
57     * @return $this
58     */
59    public function setConfidence(float $confidence): self
60    {
61        return $this->setParam('confidence', $confidence);
62    }
63
64    /**
65     * Set the maximum percentage of the terms considered to be misspellings in order to form a correction.
66     *
67     * @return $this
68     */
69    public function setMaxErrors(float $max): self
70    {
71        return $this->setParam('max_errors', $max);
72    }
73
74    /**
75     * @return $this
76     */
77    public function setSeparator(string $separator): self
78    {
79        return $this->setParam('separator', $separator);
80    }
81
82    /**
83     * Set suggestion highlighting.
84     *
85     * @return $this
86     */
87    public function setHighlight(string $preTag, string $postTag): self
88    {
89        return $this->setParam('highlight', [
90            'pre_tag' => $preTag,
91            'post_tag' => $postTag,
92        ]);
93    }
94
95    /**
96     * @return $this
97     */
98    public function setStupidBackoffSmoothing(float $discount): self
99    {
100        return $this->setSmoothingModel('stupid_backoff', [
101            'discount' => $discount,
102        ]);
103    }
104
105    /**
106     * @return $this
107     */
108    public function setLaplaceSmoothing(float $alpha): self
109    {
110        return $this->setSmoothingModel('laplace', [
111            'alpha' => $alpha,
112        ]);
113    }
114
115    /**
116     * @return $this
117     */
118    public function setLinearInterpolationSmoothing(float $trigramLambda, float $bigramLambda, float $unigramLambda): self
119    {
120        return $this->setSmoothingModel('linear_interpolation', [
121            'trigram_lambda' => $trigramLambda,
122            'bigram_lambda' => $bigramLambda,
123            'unigram_lambda' => $unigramLambda,
124        ]);
125    }
126
127    /**
128     * @param string               $model  the name of the smoothing model
129     * @param array<string, mixed> $params
130     *
131     * @return $this
132     */
133    public function setSmoothingModel(string $model, array $params): self
134    {
135        return $this->setParam('smoothing', [
136            $model => $params,
137        ]);
138    }
139
140    /**
141     * @return $this
142     */
143    public function addDirectGenerator(DirectGenerator $generator): self
144    {
145        return $this->addParam('candidate_generator', $generator);
146    }
147
148    /**
149     * @deprecated since version 7.2.0, use the "addDirectGenerator()" method instead.
150     *
151     * @return $this
152     */
153    public function addCandidateGenerator(AbstractCandidateGenerator $generator): self
154    {
155        \trigger_deprecation('ruflin/elastica', '7.2.0', 'The "%s()" method is deprecated, use the "addDirectGenerator()" method instead. It will be removed in 8.0.', __METHOD__);
156
157        return $this->addParam('candidate_generator', $generator);
158    }
159
160    /**
161     * {@inheritdoc}
162     */
163    public function toArray(): array
164    {
165        $array = parent::toArray();
166        $baseName = $this->_getBaseName();
167
168        if (isset($array[$baseName]['candidate_generator'])) {
169            $generators = $array[$baseName]['candidate_generator'];
170            unset($array[$baseName]['candidate_generator']);
171
172            foreach ($generators as $generator) {
173                $key = \array_key_first($generator);
174                $array[$baseName][$key][] = $generator[$key];
175            }
176        }
177
178        return $array;
179    }
180}
181