4 * Search suggestion sets
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 * http://www.gnu.org/copyleft/gpl.html
23 * A set of search suggestions.
24 * The set is always ordered by score, with the best match first.
26 class SearchSuggestionSet
{
28 * @var SearchSuggestion[]
30 private $suggestions = [];
36 private $pageMap = [];
39 * @var bool Are more results available?
41 private $hasMoreResults;
44 * Builds a new set of suggestions.
46 * NOTE: the array should be sorted by score (higher is better),
47 * in descending order.
48 * SearchSuggestionSet will not try to re-order this input array.
49 * Providing an unsorted input array is a mistake and will lead to
50 * unexpected behaviors.
52 * @param SearchSuggestion[] $suggestions (must be sorted by score)
53 * @param bool $hasMoreResults Are more results available?
55 public function __construct( array $suggestions, $hasMoreResults = false ) {
56 $this->hasMoreResults
= $hasMoreResults;
57 foreach ( $suggestions as $suggestion ) {
58 $pageID = $suggestion->getSuggestedTitleID();
59 if ( $pageID && empty( $this->pageMap
[$pageID] ) ) {
60 $this->pageMap
[$pageID] = true;
62 $this->suggestions
[] = $suggestion;
67 * @return bool Are more results available?
69 public function hasMoreResults() {
70 return $this->hasMoreResults
;
74 * Get the list of suggestions.
75 * @return SearchSuggestion[]
77 public function getSuggestions() {
78 return $this->suggestions
;
82 * Call array_map on the suggestions array
83 * @param callable $callback
86 public function map( $callback ) {
87 return array_map( $callback, $this->suggestions
);
91 * Filter the suggestions array
92 * @param callable $callback Callable accepting single SearchSuggestion
93 * instance returning bool false to remove the item.
94 * @return int The number of suggestions removed
96 public function filter( $callback ) {
97 $before = count( $this->suggestions
);
98 $this->suggestions
= array_values( array_filter( $this->suggestions
, $callback ) );
99 return $before - count( $this->suggestions
);
103 * Add a new suggestion at the end.
104 * If the score of the new suggestion is greater than the worst one,
105 * the new suggestion score will be updated (worst - 1).
107 * @param SearchSuggestion $suggestion
109 public function append( SearchSuggestion
$suggestion ) {
110 $pageID = $suggestion->getSuggestedTitleID();
111 if ( $pageID && isset( $this->pageMap
[$pageID] ) ) {
114 if ( $this->getSize() > 0 && $suggestion->getScore() >= $this->getWorstScore() ) {
115 $suggestion->setScore( $this->getWorstScore() - 1 );
117 $this->suggestions
[] = $suggestion;
119 $this->pageMap
[$pageID] = true;
124 * Add suggestion set to the end of the current one.
125 * @param SearchSuggestionSet $set
127 public function appendAll( SearchSuggestionSet
$set ) {
128 foreach ( $set->getSuggestions() as $sugg ) {
129 $this->append( $sugg );
134 * Move the suggestion at index $key to the first position
137 public function rescore( $key ) {
138 $removed = array_splice( $this->suggestions
, $key, 1 );
139 unset( $this->pageMap
[$removed[0]->getSuggestedTitleID()] );
140 $this->prepend( $removed[0] );
144 * Add a new suggestion at the top. If the new suggestion score
145 * is lower than the best one its score will be updated (best + 1)
146 * @param SearchSuggestion $suggestion
148 public function prepend( SearchSuggestion
$suggestion ) {
149 $pageID = $suggestion->getSuggestedTitleID();
150 if ( $pageID && isset( $this->pageMap
[$pageID] ) ) {
153 if ( $this->getSize() > 0 && $suggestion->getScore() <= $this->getBestScore() ) {
154 $suggestion->setScore( $this->getBestScore() +
1 );
156 array_unshift( $this->suggestions
, $suggestion );
158 $this->pageMap
[$pageID] = true;
163 * @return float the best score in this suggestion set
165 public function getBestScore() {
166 if ( empty( $this->suggestions
) ) {
169 return $this->suggestions
[0]->getScore();
173 * @return float the worst score in this set
175 public function getWorstScore() {
176 if ( empty( $this->suggestions
) ) {
179 return end( $this->suggestions
)->getScore();
183 * @return int the number of suggestion in this set
185 public function getSize() {
186 return count( $this->suggestions
);
190 * Remove any extra elements in the suggestions set
191 * @param int $limit the max size of this set.
193 public function shrink( $limit ) {
194 if ( count( $this->suggestions
) > $limit ) {
195 $this->suggestions
= array_slice( $this->suggestions
, 0, $limit );
196 $this->hasMoreResults
= true;
201 * Builds a new set of suggestion based on a title array.
202 * Useful when using a backend that supports only Titles.
204 * NOTE: Suggestion scores will be generated.
206 * @param Title[] $titles
207 * @param bool $hasMoreResults Are more results available?
208 * @return SearchSuggestionSet
210 public static function fromTitles( array $titles, $hasMoreResults = false ) {
211 $score = count( $titles );
212 $suggestions = array_map( function ( $title ) use ( &$score ) {
213 return SearchSuggestion
::fromTitle( $score--, $title );
215 return new SearchSuggestionSet( $suggestions, $hasMoreResults );
219 * Builds a new set of suggestion based on a string array.
221 * NOTE: Suggestion scores will be generated.
223 * @param string[] $titles
224 * @param bool $hasMoreResults Are more results available?
225 * @return SearchSuggestionSet
227 public static function fromStrings( array $titles, $hasMoreResults = false ) {
228 $score = count( $titles );
229 $suggestions = array_map( function ( $title ) use ( &$score ) {
230 return SearchSuggestion
::fromText( $score--, $title );
232 return new SearchSuggestionSet( $suggestions, $hasMoreResults );
236 * @return SearchSuggestionSet an empty suggestion set
238 public static function emptySuggestionSet() {
239 return new SearchSuggestionSet( [] );