Skip to content

Commit

Permalink
EVENT_BEFORE_SCORE_RESULTS etc.
Browse files Browse the repository at this point in the history
Resolves #11882
  • Loading branch information
brandonkelly committed Sep 7, 2022
1 parent 385905f commit d26f4ae
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 13 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG-WIP.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- Added `craft\events\CreateTwigEvent`.
- Added `craft\events\DefineAddressFieldLabelEvent`.
- Added `craft\events\DefineAddressFieldsEvent`.
- Added `craft\events\SearchEvent::$scores`. ([#11882](https://github.com/craftcms/cms/discussions/11882))
- Added `craft\helpers\DateTimeHelper::today()`.
- Added `craft\helpers\DateTimeHelper::tomorrow()`.
- Added `craft\helpers\DateTimeHelper::yesterday()`.
Expand All @@ -40,6 +41,7 @@
- Added `craft\services\Elements::canDuplicate()`.
- Added `craft\services\Elements::canSave()`.
- Added `craft\services\Elements::canView()`.
- Added `craft\services\Search::EVENT_BEFORE_SCORE_RESULTS`. ([#11882](https://github.com/craftcms/cms/discussions/11882))
- Added `craft\web\Controller::getCurrentUser()`. ([#11754](https://github.com/craftcms/cms/pull/11754))
- Added `craft\web\View::EVENT_AFTER_CREATE_TWIG`. ([#11774](https://github.com/craftcms/cms/pull/11774))
- Added the `Craft.useMobileStyles()` JavaScript method. ([#11636](https://github.com/craftcms/cms/pull/11636))
Expand All @@ -55,6 +57,7 @@
- `{% cache %}` tags now store any HTML registered with `{% html %}` tags. ([#11811](https://github.com/craftcms/cms/discussions/11811))
- Control panel `.twig` templates are now prioritized over `.html`. ([#11809](https://github.com/craftcms/cms/discussions/11809), [#11840](https://github.com/craftcms/cms/pull/11840))
- `craft\helpers\Component::iconSvg()` now namespaces the SVG contents, and adds `aria-hidden="true"`. ([#11703](https://github.com/craftcms/cms/pull/11703))
- `craft\services\Search::EVENT_AFTER_SEARCH` now includes the computed search result scores, set to `craft\events\SearchEvent::$scores`, and any changes made to it will be returned by `searchElements()`. ([#11882](https://github.com/craftcms/cms/discussions/11882))
- `craft\services\Search::EVENT_BEFORE_INDEX_KEYWORDS` is now cancellable by setting `$event->isValid` to `false`. ([#11705](https://github.com/craftcms/cms/discussions/11705))
- `checkboxSelect` inputs without `showAllOption: true` now post an empty value if no options were selected. ([#11748](https://github.com/craftcms/cms/issues/11748))
- Updated Yii to 2.0.46.
Expand Down
20 changes: 20 additions & 0 deletions src/events/SearchEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,27 @@ class SearchEvent extends BaseEvent

/**
* @var array|null The raw search result data
*
* This will only be set ahead of time for [[\craft\services\Search::EVENT_BEFORE_SCORE_RESULTS]] and
* [[\craft\services\Search::EVENT_AFTER_SEARCH]].
*
* If an event handler modifies this from [[\craft\services\Search::EVENT_BEFORE_SCORE_RESULTS]], then
* [[\craft\services\Search::searchElements()]] will score the results set on the event rather than the original results.
*
* @since 3.6.0
*/
public ?array $results = null;

/**
* @var array|null The result scores, indexed by element ID
*
* This will only be set ahead of time for [[\craft\services\Search::EVENT_AFTER_SEARCH]].
*
* If an event handler sets this from [[\craft\services\Search::EVENT_BEFORE_SCORE_RESULTS]] or modifies it from
* [[\craft\services\Search::EVENT_AFTER_SEARCH]], then [[\craft\services\Search::searchElements()]] will return its
* value rather than calculate the result scores itself.
*
* @since 4.3.0
*/
public ?array $scores = null;
}
69 changes: 56 additions & 13 deletions src/services/Search.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,22 @@ class Search extends Component

/**
* @event SearchEvent The event that is triggered after a search is performed.
*
* Any modifications to [[SearchEvent::$scores]] will be respected.
*/
public const EVENT_AFTER_SEARCH = 'afterSearch';

/**
* @event SearchEvent The event that is triggered before the results are scored.
*
* Any modifications to [[SearchEvent::$results]] will be respected when results are scored.
*
* Event handlers can set [[SearchEvent::$scores]] to override the resulting element scores returned by [[searchElements()]].
*
* @since 4.3.0
*/
public const EVENT_BEFORE_SCORE_RESULTS = 'beforeScoreResults';

/**
* @var bool Whether fulltext searches should be used ever. (MySQL only.)
* @since 3.4.10
Expand Down Expand Up @@ -264,33 +277,63 @@ public function searchElements(ElementQuery $elementQuery): array
$results = $query->all();

// Score the results
$scoresByElementId = [];
$scores = null;

// Loop through results and calculate score per element
foreach ($results as $row) {
$elementId = $row['elementId'];
$score = $this->_scoreRow($row, $elementQuery->siteId);
// Fire a 'beforeScoreResults' event
if ($this->hasEventHandlers(self::EVENT_BEFORE_SCORE_RESULTS)) {
$event = new SearchEvent([
'elementQuery' => $elementQuery,
'query' => $searchQuery,
'siteId' => $elementQuery->siteId,
'results' => $results,
]);
$this->trigger(self::EVENT_BEFORE_SCORE_RESULTS, $event);

if (!isset($scoresByElementId[$elementId])) {
$scoresByElementId[$elementId] = $score;
} else {
$scoresByElementId[$elementId] += $score;
// Use whatever changes may have been made to the results (unless it was set to null for some reason)
if ($event->results !== null) {
$results = $event->results;
}

// If a handler set the scores, use that instead of figuring it out for ourselves.
$scores = $event->scores;
}

if ($scores === null) {
$scores = [];

// Loop through results and calculate score per element
foreach ($results as $row) {
$elementId = $row['elementId'];
$score = $this->_scoreRow($row, $elementQuery->siteId);

if (!isset($scores[$elementId])) {
$scores[$elementId] = $score;
} else {
$scores[$elementId] += $score;
}
}
}

arsort($scoresByElementId);
arsort($scores);

// Fire an 'afterSearch' event
if ($this->hasEventHandlers(self::EVENT_AFTER_SEARCH)) {
$this->trigger(self::EVENT_AFTER_SEARCH, new SearchEvent([
$event = new SearchEvent([
'elementQuery' => $elementQuery,
'query' => $searchQuery,
'siteId' => $elementQuery->siteId,
'results' => $results,
]));
'scores' => $scores,
]);
$this->trigger(self::EVENT_AFTER_SEARCH, $event);

// Return the scores from the event, in case any last minute changes were made to it
if ($event->scores !== null) {
return $event->scores;
}
}

return $scoresByElementId;
return $scores;
}

/**
Expand Down

0 comments on commit d26f4ae

Please sign in to comment.