4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 * http://www.gnu.org/copyleft/gpl.html
23 namespace MediaWiki\Api
;
26 use MediaWiki\Context\IContextSource
;
28 use SearchEngineConfig
;
29 use SearchEngineFactory
;
30 use Wikimedia\ParamValidator\ParamValidator
;
31 use Wikimedia\ParamValidator\TypeDef\IntegerDef
;
34 * Traits for API components that use a SearchEngine.
39 private ?SearchEngineConfig
$searchEngineConfig = null;
40 private ?SearchEngineFactory
$searchEngineFactory = null;
42 private function checkDependenciesSet() {
43 // Since this is a trait, we can't have a constructor where the services
44 // that we need are injected. Instead, the api modules that use this trait
45 // are responsible for setting them (since api modules *can* have services
46 // injected). Double check that the api module did indeed set them
47 if ( $this->searchEngineConfig
=== null ||
$this->searchEngineFactory
=== null ) {
48 throw new LogicException(
49 'SearchApi requires both a SearchEngineConfig and SearchEngineFactory to be set'
55 * When $wgSearchType is null, $wgSearchAlternatives[0] is null. Null isn't
56 * a valid option for an array for PARAM_TYPE, so we'll use a fake name
57 * that can't possibly be a class name and describes what the null behavior
61 private static $BACKEND_NULL_PARAM = 'database-backed';
64 * The set of api parameters that are shared between api calls that
65 * call the SearchEngine. Primarily this defines parameters that
66 * are utilized by self::buildSearchEngine().
68 * @param bool $isScrollable True if the api offers scrolling
71 public function buildCommonApiParams( $isScrollable = true ) {
72 $this->checkDependenciesSet();
76 ParamValidator
::PARAM_TYPE
=> 'string',
77 ParamValidator
::PARAM_REQUIRED
=> true,
80 ParamValidator
::PARAM_DEFAULT
=> NS_MAIN
,
81 ParamValidator
::PARAM_TYPE
=> 'namespace',
82 ParamValidator
::PARAM_ISMULTI
=> true,
85 ParamValidator
::PARAM_DEFAULT
=> 10,
86 ParamValidator
::PARAM_TYPE
=> 'limit',
87 IntegerDef
::PARAM_MIN
=> 1,
88 IntegerDef
::PARAM_MAX
=> ApiBase
::LIMIT_BIG1
,
89 IntegerDef
::PARAM_MAX2
=> ApiBase
::LIMIT_BIG2
,
92 if ( $isScrollable ) {
94 ParamValidator
::PARAM_DEFAULT
=> 0,
95 IntegerDef
::PARAM_MIN
=> 0,
96 ParamValidator
::PARAM_TYPE
=> 'integer',
97 ApiBase
::PARAM_HELP_MSG
=> 'api-help-param-continue',
101 $alternatives = $this->searchEngineConfig
->getSearchTypes();
102 if ( count( $alternatives ) > 1 ) {
103 $alternatives[0] ??
= self
::$BACKEND_NULL_PARAM;
104 $params['backend'] = [
105 ParamValidator
::PARAM_DEFAULT
=> $this->searchEngineConfig
->getSearchType(),
106 ParamValidator
::PARAM_TYPE
=> $alternatives,
108 // @todo: support profile selection when multiple
109 // backends are available. The solution could be to
110 // merge all possible profiles and let ApiBase
111 // subclasses do the check. Making ApiHelp and ApiSandbox
112 // comprehensive might be more difficult.
114 $params +
= $this->buildProfileApiParam();
121 * Build the profile api param definitions. Makes bold assumption only one search
122 * engine is available, ensure that is true before calling.
124 * @return array array containing available additional api param definitions.
125 * Empty if profiles are not supported by the searchEngine implementation.
126 * @suppress PhanTypeMismatchDimFetch
128 private function buildProfileApiParam() {
129 $this->checkDependenciesSet();
131 $configs = $this->getSearchProfileParams();
132 $searchEngine = $this->searchEngineFactory
->create();
134 foreach ( $configs as $paramName => $paramConfig ) {
135 $profiles = $searchEngine->getProfiles(
136 $paramConfig['profile-type'],
137 $this->getContext()->getUser()
145 $defaultProfile = null;
146 foreach ( $profiles as $profile ) {
147 $types[] = $profile['name'];
148 if ( isset( $profile['desc-message'] ) ) {
149 $helpMessages[$profile['name']] = $profile['desc-message'];
152 if ( !empty( $profile['default'] ) ) {
153 $defaultProfile = $profile['name'];
157 $params[$paramName] = [
158 ParamValidator
::PARAM_TYPE
=> $types,
159 ApiBase
::PARAM_HELP_MSG
=> $paramConfig['help-message'],
160 ApiBase
::PARAM_HELP_MSG_PER_VALUE
=> $helpMessages,
161 ParamValidator
::PARAM_DEFAULT
=> $defaultProfile,
169 * Build the search engine to use.
170 * If $params is provided then the following searchEngine options
172 * - backend: which search backend to use
175 * - namespace: mandatory
176 * - search engine profiles defined by SearchApi::getSearchProfileParams()
177 * @param array|null $params API request params (must be sanitized by
178 * ApiBase::extractRequestParams() before)
179 * @return SearchEngine
181 public function buildSearchEngine( ?
array $params = null ) {
182 $this->checkDependenciesSet();
184 if ( $params == null ) {
185 return $this->searchEngineFactory
->create();
188 $type = $params['backend'] ??
null;
189 if ( $type === self
::$BACKEND_NULL_PARAM ) {
192 $searchEngine = $this->searchEngineFactory
->create( $type );
193 $searchEngine->setNamespaces( $params['namespace'] );
194 $searchEngine->setLimitOffset( $params['limit'], $params['offset'] ??
0 );
196 // Initialize requested search profiles.
197 $configs = $this->getSearchProfileParams();
198 foreach ( $configs as $paramName => $paramConfig ) {
199 if ( isset( $params[$paramName] ) ) {
200 $searchEngine->setFeatureData(
201 $paramConfig['profile-type'],
206 return $searchEngine;
210 * @return array[] array of arrays mapping from parameter name to a two value map
211 * containing 'help-message' and 'profile-type' keys.
213 abstract public function getSearchProfileParams();
216 * @return IContextSource
218 abstract public function getContext();
221 /** @deprecated class alias since 1.43 */
222 class_alias( SearchApi
::class, 'SearchApi' );