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 callback $callback
86 public function map( $callback ) {
87 return array_map( $callback, $this->suggestions
);
91 * Add a new suggestion at the end.
92 * If the score of the new suggestion is greater than the worst one,
93 * the new suggestion score will be updated (worst - 1).
95 * @param SearchSuggestion $suggestion
97 public function append( SearchSuggestion
$suggestion ) {
98 $pageID = $suggestion->getSuggestedTitleID();
99 if ( $pageID && isset( $this->pageMap
[$pageID] ) ) {
102 if ( $this->getSize() > 0 && $suggestion->getScore() >= $this->getWorstScore() ) {
103 $suggestion->setScore( $this->getWorstScore() - 1 );
105 $this->suggestions
[] = $suggestion;
107 $this->pageMap
[$pageID] = true;
112 * Add suggestion set to the end of the current one.
113 * @param SearchSuggestionSet $set
115 public function appendAll( SearchSuggestionSet
$set ) {
116 foreach ( $set->getSuggestions() as $sugg ) {
117 $this->append( $sugg );
122 * Move the suggestion at index $key to the first position
125 public function rescore( $key ) {
126 $removed = array_splice( $this->suggestions
, $key, 1 );
127 unset( $this->pageMap
[$removed[0]->getSuggestedTitleID()] );
128 $this->prepend( $removed[0] );
132 * Add a new suggestion at the top. If the new suggestion score
133 * is lower than the best one its score will be updated (best + 1)
134 * @param SearchSuggestion $suggestion
136 public function prepend( SearchSuggestion
$suggestion ) {
137 $pageID = $suggestion->getSuggestedTitleID();
138 if ( $pageID && isset( $this->pageMap
[$pageID] ) ) {
141 if ( $this->getSize() > 0 && $suggestion->getScore() <= $this->getBestScore() ) {
142 $suggestion->setScore( $this->getBestScore() +
1 );
144 array_unshift( $this->suggestions
, $suggestion );
146 $this->pageMap
[$pageID] = true;
151 * @return float the best score in this suggestion set
153 public function getBestScore() {
154 if ( empty( $this->suggestions
) ) {
157 return $this->suggestions
[0]->getScore();
161 * @return float the worst score in this set
163 public function getWorstScore() {
164 if ( empty( $this->suggestions
) ) {
167 return end( $this->suggestions
)->getScore();
171 * @return int the number of suggestion in this set
173 public function getSize() {
174 return count( $this->suggestions
);
178 * Remove any extra elements in the suggestions set
179 * @param int $limit the max size of this set.
181 public function shrink( $limit ) {
182 if ( count( $this->suggestions
) > $limit ) {
183 $this->suggestions
= array_slice( $this->suggestions
, 0, $limit );
184 $this->hasMoreResults
= true;
189 * Builds a new set of suggestion based on a title array.
190 * Useful when using a backend that supports only Titles.
192 * NOTE: Suggestion scores will be generated.
194 * @param Title[] $titles
195 * @param bool $hasMoreResults Are more results available?
196 * @return SearchSuggestionSet
198 public static function fromTitles( array $titles, $hasMoreResults = false ) {
199 $score = count( $titles );
200 $suggestions = array_map( function ( $title ) use ( &$score ) {
201 return SearchSuggestion
::fromTitle( $score--, $title );
203 return new SearchSuggestionSet( $suggestions, $hasMoreResults );
207 * Builds a new set of suggestion based on a string array.
209 * NOTE: Suggestion scores will be generated.
211 * @param string[] $titles
212 * @param bool $hasMoreResults Are more results available?
213 * @return SearchSuggestionSet
215 public static function fromStrings( array $titles, $hasMoreResults = false ) {
216 $score = count( $titles );
217 $suggestions = array_map( function ( $title ) use ( &$score ) {
218 return SearchSuggestion
::fromText( $score--, $title );
220 return new SearchSuggestionSet( $suggestions, $hasMoreResults );
224 * @return SearchSuggestionSet an empty suggestion set
226 public static function emptySuggestionSet() {
227 return new SearchSuggestionSet( [] );