From 88b3e57d8fe934825b5f805027bf2e73a2ef55fe Mon Sep 17 00:00:00 2001 From: Daniel Klabbers Date: Mon, 3 Jan 2022 15:13:13 +0100 Subject: [PATCH] added weighting properly, also different for titles vs posts --- src/Api/Controllers/SearchController.php | 66 ++++++++++++++++++++++-- src/Elasticsearch/SimpleSearchQuery.php | 39 ++++++++++++++ src/Elasticsearch/WildcardQuery.php | 46 +++++++++++++++++ 3 files changed, 148 insertions(+), 3 deletions(-) create mode 100644 src/Elasticsearch/SimpleSearchQuery.php create mode 100644 src/Elasticsearch/WildcardQuery.php diff --git a/src/Api/Controllers/SearchController.php b/src/Api/Controllers/SearchController.php index 9bd9ae7..1697d85 100644 --- a/src/Api/Controllers/SearchController.php +++ b/src/Api/Controllers/SearchController.php @@ -4,6 +4,8 @@ namespace Blomstra\Search\Api\Controllers; use Blomstra\Search\Elasticsearch\MatchPhraseQuery; use Blomstra\Search\Elasticsearch\MatchQuery; +use Blomstra\Search\Elasticsearch\SimpleSearchQuery; +use Blomstra\Search\Elasticsearch\WildcardQuery; use Blomstra\Search\Save\Document as ElasticDocument; use Blomstra\Search\Elasticsearch\TermsQuery; use Elasticsearch\Client; @@ -57,7 +59,9 @@ class SearchController extends ListDiscussionsController if (! empty($search)) { $filterQuery ->add($this->sentenceMatch($search)) - ->add($this->wordMatch($search)); + ->add($this->wordMatch($search)) +// ->add($this->partialMatch($search)) + ; } $builder = (new Builder($this->elastic)) @@ -98,10 +102,12 @@ class SearchController extends ListDiscussionsController if ($type === 'posts') { return [ 'most_relevant_post_id' => $id, + 'weight' => Arr::get($hit, 'sort.0') ]; } else { return [ 'discussion_id' => $id, + 'weight' => Arr::get($hit, 'sort.0') ]; } }); @@ -115,12 +121,15 @@ class SearchController extends ListDiscussionsController ->each(function (Discussion $discussion) use ($results) { if (in_array($discussion->id, $results->pluck('discussion_id')->toArray())) { $discussion->most_relevant_post_id = $discussion->first_post_id; + $discussion->weight = $results->firstWhere('discussion_id', $discussion->id)['weight'] ?? 0; } else { $post = $discussion->posts()->whereIn('id', $results->pluck('most_relevant_post_id'))->first(); $discussion->most_relevant_post_id = $post?->id ?? $discussion->first_post_id; + $discussion->weight = $results->firstWhere('most_relevant_post_id', $post?->id)['weight'] ?? 0; } }) ->keyBy('id') + ->sortByDesc('weight') ->unique(); $this->loadRelations($discussions, $include); @@ -193,12 +202,63 @@ class SearchController extends ListDiscussionsController protected function sentenceMatch(string $q): Query { - return new MatchPhraseQuery('content', $q); + return BoolQuery::create() + // Discussion titles + ->add( + BoolQuery::create() + ->add(TermQuery::create('type', 'discussions'), 'filter') + ->add((new MatchPhraseQuery('content', $q))->boost(1)), + 'should' + ) + // Post bodies + ->add( + BoolQuery::create() + ->add(TermQuery::create('type', 'posts'), 'filter') + ->add((new MatchPhraseQuery('content', $q))->boost(.9)), + 'should' + ); } protected function wordMatch(string $q) { - return (new MatchQuery('content', $q))->boost(.3); + return BoolQuery::create() + // Discussion titles + ->add( + BoolQuery::create() + ->add(TermQuery::create('type', 'discussions'), 'filter') + ->add((new MatchQuery('content', $q))->boost(.6)), + 'should' + ) + // Post bodies + ->add( + BoolQuery::create() + ->add(TermQuery::create('type', 'posts'), 'filter') + ->add((new MatchQuery('content', $q))->boost(.5)), + 'should' + ); + } + + protected function partialMatch(string $q) + { + $wildcard = (new WildcardQuery('content', "*$q*")) + ->caseSensitivity(false) + ->rewrite('constant_score'); + + return BoolQuery::create() + // Discussion titles + ->add( + BoolQuery::create() + ->add(TermQuery::create('type', 'discussions'), 'filter') + ->add($wildcard->boost(.3)), + 'should' + ) + // Post bodies + ->add( + BoolQuery::create() + ->add(TermQuery::create('type', 'posts'), 'filter') + ->add($wildcard->boost(.5)), + 'should' + ); } diff --git a/src/Elasticsearch/SimpleSearchQuery.php b/src/Elasticsearch/SimpleSearchQuery.php new file mode 100644 index 0000000..9bb6341 --- /dev/null +++ b/src/Elasticsearch/SimpleSearchQuery.php @@ -0,0 +1,39 @@ +boost = $boost; + + return $this; + } + + public function __construct( + protected array $fields, + protected string $value + ) { + } + + public function toArray(): array + { + return [ + 'simple_query_string' => [ + 'query' => $this->value, + 'fields' => $this->fields, + 'boost' => $this->boost + ] + ]; + } +} diff --git a/src/Elasticsearch/WildcardQuery.php b/src/Elasticsearch/WildcardQuery.php new file mode 100644 index 0000000..320f2bb --- /dev/null +++ b/src/Elasticsearch/WildcardQuery.php @@ -0,0 +1,46 @@ +boost = $boost; + + return $this; + } + + public function caseSensitivity(bool $sensitivity = true) + { + $this->sensitivity = $sensitivity; + + return $this; + } + + public function rewrite(string $rewrite = null) + { + $this->rewrite = $rewrite; + + return $this; + } + + public function toArray(): array + { + $query = parent::toArray(); + + $query['wildcard'][$this->field]['boost'] = $this->boost; + $query['wildcard'][$this->field]['case_insensitive'] = ! $this->sensitivity; + + if ($this->rewrite) { + $query['wildcard'][$this->field]['rewrite'] = $this->rewrite; + + } + + return $query; + } +}