diff --git a/src/XF/Search/Source/AbstractSource.php b/src/XF/Search/Source/AbstractSource.php
index 1021993da28..56373c0b495 100644
--- a/src/XF/Search/Source/AbstractSource.php
+++ b/src/XF/Search/Source/AbstractSource.php
@@ -10,6 +10,8 @@
abstract class AbstractSource
{
+ const DEFAULT_MAX_KEYWORDS = 1024;
+
protected $bulkIndexing = false;
abstract public function isRelevanceSupported();
@@ -78,6 +80,11 @@ public function getWordSplitRange()
return '\x00-\x21\x28\x29\x2C-\x2F\x3A-\x40\x5B-\x5E\x60\x7B\x7D-\x7F';
}
+ public function getMaxKeywords(): int
+ {
+ return self::DEFAULT_MAX_KEYWORDS;
+ }
+
public function parseKeywords($keywords, &$error = null, &$warning = null)
{
$splitRange = $this->getWordSplitRange();
@@ -159,6 +166,14 @@ public function parseKeywords($keywords, &$error = null, &$warning = null)
)->render('raw');
}
+ $keywordCount = count($output);
+ $maxKeywords = $this->getMaxKeywords();
+
+ if ($maxKeywords && $keywordCount > $maxKeywords)
+ {
+ $error = \XF::phrase('search_could_not_be_completed_because_number_of_keywords_exceeds_limit_x', ['maxKeywords' => \XF::language()->numberFormat($maxKeywords)]);
+ }
+
return $this->finalizeParsedKeywords($output);
}
diff --git a/src/addons/XFES/Elasticsearch/Api.php b/src/addons/XFES/Elasticsearch/Api.php
index 36383de524b..e45ccc5b1e8 100644
--- a/src/addons/XFES/Elasticsearch/Api.php
+++ b/src/addons/XFES/Elasticsearch/Api.php
@@ -310,6 +310,11 @@ public function createIndex(array $config = [])
return $body;
}
+ public function getClusterSettings(bool $includeDefaults = true)
+ {
+ return $this->requestFromRoot('get', '_cluster/settings' . ($includeDefaults ? '?include_defaults=true' : ''))->getBody();
+ }
+
public function updateSettings(array $settings)
{
return $this->requestFromIndex('put', '_settings', $settings)->getBody();
@@ -333,6 +338,13 @@ public function requestFromIndex($method, $path, $data = null)
return $this->request($method, "{$index}/{$path}", $data);
}
+ public function requestFromRoot($method, $path, $data = null)
+ {
+ $path = ltrim($path, '/');
+
+ return $this->request($method, $path, $data);
+ }
+
public function requestById($method, $type, $id, $data = null)
{
if ($this->isTypelessIndex())
diff --git a/src/addons/XFES/Search/Source/Elasticsearch.php b/src/addons/XFES/Search/Source/Elasticsearch.php
index bc24b04aa15..6184c95cf73 100644
--- a/src/addons/XFES/Search/Source/Elasticsearch.php
+++ b/src/addons/XFES/Search/Source/Elasticsearch.php
@@ -944,6 +944,41 @@ public function getWordSplitRange()
return '\x00-\x21\x23-\x26\x28\x29\x2B\x2C\x2F\x3A-\x40\x5B-\x5E\x60\x7B-\x7F';
}
+ public function getMaxKeywords(): int
+ {
+ return $this->getMaxClauseCount();
+ }
+
+ protected function getMaxClauseCount(): int
+ {
+ $options = \XF::options();
+ $xfesConfig = $options->xfesConfig;
+
+ if (!isset($xfesConfig['maxClauseCount'])
+ || !isset($xfesConfig['maxClauseCountChecked'])
+ || !$xfesConfig['maxClauseCountChecked']
+ || $xfesConfig['maxClauseCountChecked'] < \XF::$time - 60 * 60 * 6 // 6 hours
+ )
+ {
+ $clusterSettings = $this->es->getClusterSettings();
+
+ $maxClauseCount = $clusterSettings['defaults']['indices']['query']['bool']['max_clause_count']
+ ?? $clusterSettings['defaults']['index']['query']['bool']['max_clause_count']
+ ?? null;
+
+ if ($maxClauseCount === null)
+ {
+ $maxClauseCount = self::DEFAULT_MAX_KEYWORDS;
+ }
+
+ $xfesConfig['maxClauseCount'] = $maxClauseCount;
+ $xfesConfig['maxClauseCountChecked'] = \XF::$time;
+
+ \XF::repository('XF:Option')->updateOption('xfesConfig', $xfesConfig);
+ }
+
+ return intval($xfesConfig['maxClauseCount']);
+ }
+
protected function finalizeParsedKeywords(array $parsed)
{
$query = '';