+ $results = $this->search( $search, $params );
+
+ // Allow hooks to populate extracts and images
+ Hooks::run( 'ApiOpenSearchSuggest', [ &$results ] );
+
+ // Trim extracts, if necessary
+ $length = $this->getConfig()->get( 'OpenSearchDescriptionLength' );
+ foreach ( $results as &$r ) {
+ if ( is_string( $r['extract'] ) && !$r['extract trimmed'] ) {
+ $r['extract'] = self::trimExtract( $r['extract'], $length );
+ }
+ }
+ }
+
+ // Populate result object
+ $this->populateResult( $search, $results );
+ }
+
+ /**
+ * Perform the search
+ * @param string $search the search query
+ * @param array $params api request params
+ * @return array search results. Keys are integers.
+ */
+ private function search( $search, array $params ) {
+ $searchEngine = $this->buildSearchEngine( $params );
+ $titles = $searchEngine->extractTitles( $searchEngine->completionSearchWithVariants( $search ) );
+ $results = [];
+
+ if ( !$titles ) {
+ return $results;
+ }
+
+ // Special pages need unique integer ids in the return list, so we just
+ // assign them negative numbers because those won't clash with the
+ // always positive articleIds that non-special pages get.
+ $nextSpecialPageId = -1;
+
+ if ( $params['redirects'] === null ) {
+ // Backwards compatibility, don't resolve for JSON.
+ $resolveRedir = $this->getFormat() !== 'json';
+ } else {
+ $resolveRedir = $params['redirects'] === 'resolve';
+ }
+
+ if ( $resolveRedir ) {
+ // Query for redirects
+ $redirects = [];
+ $lb = new LinkBatch( $titles );
+ if ( !$lb->isEmpty() ) {
+ $db = $this->getDB();
+ $res = $db->select(
+ [ 'page', 'redirect' ],
+ [ 'page_namespace', 'page_title', 'rd_namespace', 'rd_title' ],
+ [
+ 'rd_from = page_id',
+ 'rd_interwiki IS NULL OR rd_interwiki = ' . $db->addQuotes( '' ),
+ $lb->constructSet( 'page', $db ),
+ ],
+ __METHOD__
+ );
+ foreach ( $res as $row ) {
+ $redirects[$row->page_namespace][$row->page_title] =
+ [ $row->rd_namespace, $row->rd_title ];
+ }
+ }