diff --git a/src/Api/Controllers/SearchController.php b/src/Api/Controllers/SearchController.php index 9d5f7e7..893b9d8 100644 --- a/src/Api/Controllers/SearchController.php +++ b/src/Api/Controllers/SearchController.php @@ -37,7 +37,7 @@ use Illuminate\Support\Str; use Psr\Http\Message\ServerRequestInterface; use Psr\Log\LoggerInterface; use Spatie\ElasticsearchQueryBuilder\Builder; -use Spatie\ElasticsearchQueryBuilder\Queries\BoolQuery; +use Blomstra\Search\Elasticsearch\BoolQuery; use Spatie\ElasticsearchQueryBuilder\Queries\TermQuery; use Spatie\ElasticsearchQueryBuilder\Sorts\Sort; use Tobscure\JsonApi\Document; @@ -217,6 +217,10 @@ class SearchController extends ListDiscussionsController $postQuery->add(TermQuery::create('is_hidden', 'false'), 'filter'); } + // Without minimum_should_match, ES default MSM is 0 when a filter clause is present, + // causing has_child to score every non-hidden post instead of only matching ones. + $postQuery->minimumShouldMatch(1); + $textQuery->add( HasChildQuery::create('post', $postQuery)->withInnerHits(), 'should' @@ -235,7 +239,6 @@ class SearchController extends ListDiscussionsController } if ($this->matchWords) { $should->add((new MatchQuery('content', $search))->operator('and')->boost(1.8 * $boost), 'should'); - $should->add((new MatchQuery('content', $search))->operator('or')->boost(0.8 * $boost), 'should'); } return $should; diff --git a/src/Elasticsearch/BoolQuery.php b/src/Elasticsearch/BoolQuery.php new file mode 100644 index 0000000..ac900e5 --- /dev/null +++ b/src/Elasticsearch/BoolQuery.php @@ -0,0 +1,41 @@ +minimumShouldMatch = $minimum; + + return $this; + } + + public function toArray(): array + { + $array = parent::toArray(); + + if ($this->minimumShouldMatch !== null) { + $array['bool']['minimum_should_match'] = $this->minimumShouldMatch; + } + + return $array; + } +} diff --git a/src/Provider.php b/src/Provider.php index dcf5163..2cd5ad4 100644 --- a/src/Provider.php +++ b/src/Provider.php @@ -47,7 +47,13 @@ class Provider extends AbstractServiceProvider $this->container->singleton(Elastic::class, function (Container $container) use ($settings, $config) { $builder = ClientBuilder::create() - ->setHosts([$settings->get('blomstra-search.elastic-endpoint')]); + ->setHosts([$settings->get('blomstra-search.elastic-endpoint')]) + ->setConnectionParams([ + 'client' => [ + 'connect_timeout' => 2, // fail fast if ES is unreachable + 'timeout' => 10, // allow time for complex queries + ], + ]); if ($config->inDebugMode()) { $builder->setLogger($container->make(LoggerInterface::class));