2222use Smile \ElasticsuiteThesaurus \Model \Index ;
2323use Smile \ElasticsuiteCore \Api \Search \SpellcheckerInterface ;
2424use Smile \ElasticsuiteCore \Search \Request \QueryInterface ;
25+ use Smile \ElasticsuiteCore \Helper \Text as TextHelper ;
2526
2627/**
2728 * Plugin that handle query rewriting (synonym substitution) during fulltext query building phase.
@@ -47,6 +48,11 @@ class QueryRewrite
4748 */
4849 private $ index ;
4950
51+ /**
52+ * @var Text
53+ */
54+ private $ textHelper ;
55+
5056 /**
5157 * @var array
5258 */
@@ -58,15 +64,18 @@ class QueryRewrite
5864 * @param QueryFactory $queryFactory Search request query factory.
5965 * @param ThesaurusConfigFactory $thesaurusConfigFactory Thesaurus configuration factory.
6066 * @param Index $index Synonym index.
67+ * @param TextHelper $textHelper Text helper.
6168 */
6269 public function __construct (
6370 QueryFactory $ queryFactory ,
6471 ThesaurusConfigFactory $ thesaurusConfigFactory ,
65- Index $ index
72+ Index $ index ,
73+ TextHelper $ textHelper
6674 ) {
6775 $ this ->queryFactory = $ queryFactory ;
6876 $ this ->thesaurusConfigFactory = $ thesaurusConfigFactory ;
6977 $ this ->index = $ index ;
78+ $ this ->textHelper = $ textHelper ;
7079 }
7180
7281 /**
@@ -77,7 +86,7 @@ public function __construct(
7786 * @param QueryBuilder $subject Original query builder.
7887 * @param \Closure $proceed Original create func.
7988 * @param ContainerConfigurationInterface $containerConfig Search request container config.
80- * @param string $queryText Current query text.
89+ * @param string|array $queryText Current query text.
8190 * @param string $spellingType Spelling type of the query.
8291 * @param float $boost Original query boost.
8392 * @param int $depth Call depth of the create method. Can be used to avoid/prevent cycles.
@@ -109,13 +118,16 @@ public function aroundCreate(
109118 if ($ depth === 0 ) {
110119 $ rewrites = $ this ->getWeightedRewrites ($ queryText , $ containerConfig , $ boost );
111120 }
121+ $ originalSpellingType = $ spellingType ;
112122 // Set base query as SPELLING_TYPE_EXACT if synonyms/expansions are found.
123+ // This is to prevent possible fuzzy matches on that original query's terms and doing so, prioritize the rewritten queries.
113124 $ spellingType = empty ($ rewrites ) ? $ spellingType : SpellcheckerInterface::SPELLING_TYPE_EXACT ;
114125 $ query = $ proceed ($ containerConfig , $ queryText , $ spellingType , $ boost , $ depth );
115126
116127 if (!empty ($ rewrites )) {
117128 $ synonymQueries = [$ query ];
118- $ synonymQueriesSpellcheck = SpellcheckerInterface::SPELLING_TYPE_EXACT ;
129+ // Do not enforce SPELLING_TYPE_EXACT systematically for alternative queries.
130+ $ synonymQueriesSpellcheck = $ this ->getRewritesSpellingType ($ queryText , $ originalSpellingType );
119131
120132 foreach ($ rewrites as $ rewrittenQuery => $ weight ) {
121133 $ synonymQueries [] = $ proceed ($ containerConfig , $ rewrittenQuery , $ synonymQueriesSpellcheck , $ weight , $ depth + 1 );
@@ -161,6 +173,40 @@ private function getWeightedRewrites($queryText, $containerConfig, $originalBoos
161173 return $ rewrites ;
162174 }
163175
176+ /**
177+ * Returns the spelling type to use for rewritten queries.
178+ * For multi terms queries, considering that at least one term has been replaced,
179+ * but there could be at least one mistyped term that might not have been replaced.
180+ * So "elevate" the spelling type a bit.
181+ * Ideally a new spellcheck query should be run.
182+ *
183+ * @param string|array $originalQueryText The original query text.
184+ * @param int $originalSpellingType The original spelling type.
185+ *
186+ * @return int
187+ */
188+ private function getRewritesSpellingType ($ originalQueryText , $ originalSpellingType )
189+ {
190+ if (is_array ($ originalQueryText )) {
191+ // Expected to be SPELLING_TYPE_EXACT as enforced by the request builder.
192+ return $ originalSpellingType ;
193+ }
194+
195+ if ($ this ->textHelper ->mbWordCount ($ originalQueryText ) === 1 ) {
196+ return SpellcheckerInterface::SPELLING_TYPE_EXACT ;
197+ }
198+
199+ $ spellingType = $ originalSpellingType ;
200+
201+ if (SpellcheckerInterface::SPELLING_TYPE_FUZZY === $ originalSpellingType ) {
202+ $ spellingType = SpellcheckerInterface::SPELLING_TYPE_MOST_FUZZY ;
203+ } elseif (SpellcheckerInterface::SPELLING_TYPE_PURE_STOPWORDS === $ originalSpellingType ) {
204+ $ spellingType = SpellcheckerInterface::SPELLING_TYPE_MOST_EXACT ;
205+ }
206+
207+ return $ spellingType ;
208+ }
209+
164210 /**
165211 * Return thesaurus/relevance configuration.
166212 *
0 commit comments