instead.
* Sanitizer::attributeWhitelist() and Sanitizer::setupAttributeWhitelist()
have been deprecated; they will be made private in the future.
+* SearchResult::termMatches() method is deprecated. It was unreliable because
+ only populated by few search engine implementations. Use
+ SqlSearchResult::getTermMatches() if really needed.
+* SearchResult::getTextSnippet( $terms ) the $terms param is being deprecated
+ and should no longer be passed. Search engine implemenations should be
+ responsible for carrying relevant information needed for highlighting with
+ their own SearchResultSet/SearchResult sub-classes.
+* SearchEngine::$searchTerms protected field is deprecated. Moved to
+ SearchDatabase.
+* The use of the $terms param in the ShowSearchHit and ShowSearchHitTitle
+ hooks is highly discouraged as it's only populated by SearchDatabase search
+ engines.
=== Other changes in 1.34 ===
* …
'SpecialWatchlist' => __DIR__ . '/includes/specials/SpecialWatchlist.php',
'SpecialWhatLinksHere' => __DIR__ . '/includes/specials/SpecialWhatLinksHere.php',
'SqlBagOStuff' => __DIR__ . '/includes/objectcache/SqlBagOStuff.php',
+ 'SqlSearchResult' => __DIR__ . '/includes/search/SqlSearchResult.php',
'SqlSearchResultSet' => __DIR__ . '/includes/search/SqlSearchResultSet.php',
'Sqlite' => __DIR__ . '/maintenance/sqlite.inc',
'SqliteInstaller' => __DIR__ . '/includes/installer/SqliteInstaller.php',
'ShowSearchHit': Customize display of search hit.
$searchPage: The SpecialSearch instance.
$result: The SearchResult to show
-$terms: Search terms, for highlighting
+$terms: Search terms, for highlighting (unreliable as search engine dependent).
&$link: HTML of link to the matching page. May be modified.
&$redirect: HTML of redirect info. May be modified.
&$section: HTML of matching section. May be modified.
* @file
*/
-use MediaWiki\MediaWikiServices;
-
/**
* Query module to perform full text search within wiki titles and content
*
}
}
- // Add the search results to the result
- $terms = MediaWikiServices::getInstance()->getContentLanguage()->
- convertForSearchResult( $matches->termMatches() );
$titles = [];
$count = 0;
}
if ( $resultPageSet === null ) {
- $vals = $this->getSearchResultData( $result, $prop, $terms );
+ $vals = $this->getSearchResultData( $result, $prop );
if ( $vals ) {
// Add item to results and see whether it fits
$fit = $apiResult->addValue( [ 'query', $this->getModuleName() ], null, $vals );
// Interwiki results inside main result set
$canAddInterwiki = (bool)$params['enablerewrites'] && ( $resultPageSet === null );
if ( $canAddInterwiki ) {
- $this->addInterwikiResults( $matches, $apiResult, $prop, $terms, 'additional',
+ $this->addInterwikiResults( $matches, $apiResult, $prop, 'additional',
SearchResultSet::INLINE_RESULTS );
}
// Interwiki results outside main result set
if ( $interwiki && $resultPageSet === null ) {
- $this->addInterwikiResults( $matches, $apiResult, $prop, $terms, 'interwiki',
+ $this->addInterwikiResults( $matches, $apiResult, $prop, 'interwiki',
SearchResultSet::SECONDARY_RESULTS );
}
* Assemble search result data.
* @param SearchResult $result Search result
* @param array $prop Props to extract (as keys)
- * @param array $terms Terms list
* @return array|null Result data or null if result is broken in some way.
*/
- private function getSearchResultData( SearchResult $result, $prop, $terms ) {
+ private function getSearchResultData( SearchResult $result, $prop ) {
// Silently skip broken and missing titles
if ( $result->isBrokenTitle() || $result->isMissingRevision() ) {
return null;
$vals['wordcount'] = $result->getWordCount();
}
if ( isset( $prop['snippet'] ) ) {
- $vals['snippet'] = $result->getTextSnippet( $terms );
+ $vals['snippet'] = $result->getTextSnippet();
}
if ( isset( $prop['timestamp'] ) ) {
$vals['timestamp'] = wfTimestamp( TS_ISO_8601, $result->getTimestamp() );
* @param SearchResultSet $matches
* @param ApiResult $apiResult
* @param array $prop Props to extract (as keys)
- * @param array $terms Terms list
* @param string $section Section name where results would go
* @param int $type Interwiki result type
* @return int|null Number of total hits in the data or null if none was produced
*/
private function addInterwikiResults(
SearchResultSet $matches, ApiResult $apiResult, $prop,
- $terms, $section, $type
+ $section, $type
) {
$totalhits = null;
if ( $matches->hasInterwikiResults( $type ) ) {
foreach ( $interwikiMatches as $result ) {
$title = $result->getTitle();
- $vals = $this->getSearchResultData( $result, $prop, $terms );
+ $vals = $this->getSearchResultData( $result, $prop );
$vals['namespace'] = $result->getInterwikiNamespaceText();
$vals['title'] = $title->getText();
/** @var IDatabase (backwards compatibility) */
protected $db;
+ /**
+ * @var string[] search terms
+ */
+ protected $searchTerms = [];
+
/**
* @param ILoadBalancer $lb The load balancer for the DB cluster to search on
*/
/** @var int */
protected $offset = 0;
- /** @var string[] */
+ /**
+ * @var string[]
+ * @deprecated since 1.34
+ */
protected $searchTerms = [];
/** @var bool */
}
/**
- * @param string[] $terms Terms to highlight
+ * @param string[] $terms Terms to highlight (this parameter is deprecated and ignored)
* @return string Highlighted text snippet, null (and not '') if not supported
*/
- function getTextSnippet( $terms ) {
- global $wgAdvancedSearchHighlighting;
- $this->initText();
-
- // TODO: make highliter take a content object. Make ContentHandler a factory for SearchHighliter.
- list( $contextlines, $contextchars ) = $this->searchEngine->userHighlightPrefs();
-
- $h = new SearchHighlighter();
- if ( count( $terms ) > 0 ) {
- if ( $wgAdvancedSearchHighlighting ) {
- return $h->highlightText( $this->mText, $terms, $contextlines, $contextchars );
- } else {
- return $h->highlightSimple( $this->mText, $terms, $contextlines, $contextchars );
- }
- } else {
- return $h->highlightNone( $this->mText, $contextlines, $contextchars );
- }
+ function getTextSnippet( $terms = [] ) {
+ return '';
}
/**
* STUB
*
* @return string[]
+ * @deprecated since 1.34 (use SqlSearchResult)
*/
function termMatches() {
return [];
--- /dev/null
+<?php
+
+/**
+ * Search engine result issued from SearchData search engines.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Search
+ */
+
+class SqlSearchResult extends SearchResult {
+ /** @var string[] */
+ private $terms;
+
+ /**
+ * SqlSearchResult constructor.
+ * @param Title $title
+ * @param string[] $terms list of parsed terms
+ */
+ public function __construct( Title $title, array $terms ) {
+ $this->initFromTitle( $title );
+ $this->terms = $terms;
+ }
+
+ /**
+ * return string[]
+ */
+ public function getTermMatches(): array {
+ return $this->terms;
+ }
+
+ /**
+ * @param array $terms Terms to highlight (this parameter is deprecated)
+ * @return string Highlighted text snippet, null (and not '') if not supported
+ */
+ function getTextSnippet( $terms = [] ) {
+ global $wgAdvancedSearchHighlighting;
+ $this->initText();
+
+ // TODO: make highliter take a content object. Make ContentHandler a factory for SearchHighliter.
+ list( $contextlines, $contextchars ) = $this->searchEngine->userHighlightPrefs();
+
+ $h = new SearchHighlighter();
+ if ( count( $this->terms ) > 0 ) {
+ if ( $wgAdvancedSearchHighlighting ) {
+ return $h->highlightText( $this->mText, $this->terms, $contextlines, $contextchars );
+ } else {
+ return $h->highlightSimple( $this->mText, $this->terms, $contextlines, $contextchars );
+ }
+ } else {
+ return $h->highlightNone( $this->mText, $contextlines, $contextchars );
+ }
+ }
+
+}
/** @var int|null Total number of hits for $terms */
protected $totalHits;
- function __construct( IResultWrapper $resultSet, $terms, $total = null ) {
+ /**
+ * @param IResultWrapper $resultSet
+ * @param string[] $terms
+ * @param int|null $total
+ */
+ function __construct( IResultWrapper $resultSet, array $terms, $total = null ) {
+ parent::__construct();
$this->resultSet = $resultSet;
$this->terms = $terms;
$this->totalHits = $total;
}
+ /**
+ * @return string[]
+ * @deprecated since 1.34
+ */
function termMatches() {
return $this->terms;
}
if ( $this->results === null ) {
$this->results = [];
$this->resultSet->rewind();
+ $terms = \MediaWiki\MediaWikiServices::getInstance()->getContentLanguage()
+ ->convertForSearchResult( $this->terms );
while ( ( $row = $this->resultSet->fetchObject() ) !== false ) {
- $this->results[] = SearchResult::newFromTitle(
- Title::makeTitle( $row->page_namespace, $row->page_title ), $this
+ $result = new SqlSearchResult(
+ Title::makeTitle( $row->page_namespace, $row->page_title ),
+ $terms
);
+ $this->augmentResult( $result );
+ $this->results[] = $result;
}
}
return $this->results;
* @return string HTML
*/
protected function renderResultSet( SearchResultSet $resultSet, $offset ) {
- $terms = MediaWikiServices::getInstance()->getContentLanguage()->
- convertForSearchResult( $resultSet->termMatches() );
-
$hits = [];
foreach ( $resultSet as $result ) {
- $hits[] = $this->resultWidget->render( $result, $terms, $offset++ );
+ $hits[] = $this->resultWidget->render( $result, $offset++ );
}
return "<ul class='mw-search-results'>" . implode( '', $hits ) . "</ul>";
/**
* @param SearchResult $result The result to render
- * @param string[] $terms Terms to be highlighted (@see SearchResult::getTextSnippet)
* @param int $position The result position, including offset
* @return string HTML
*/
- public function render( SearchResult $result, $terms, $position ) {
+ public function render( SearchResult $result, $position ) {
// If the page doesn't *exist*... our search index is out of date.
// The least confusing at this point is to drop the result.
// You may get less results, but... on well. :P
return '';
}
- $link = $this->generateMainLinkHtml( $result, $terms, $position );
+ $link = $this->generateMainLinkHtml( $result, $position );
// If page content is not readable, just return ths title.
// This is not quite safe, but better than showing excerpts from
// non-readable pages. Note that hiding the entry entirely would
$this->specialPage->getUser()
);
list( $file, $desc, $thumb ) = $this->generateFileHtml( $result );
- $snippet = $result->getTextSnippet( $terms );
+ $snippet = $result->getTextSnippet();
if ( $snippet ) {
$extract = "<div class='searchresult'>$snippet</div>";
} else {
$html = null;
$score = '';
$related = '';
+ // TODO: remove this instanceof and always pass [], let implementors do the cast if
+ // they want to be SearchDatabase specific
+ $terms = $result instanceof \SqlSearchResult ? $result->getTermMatches() : [];
if ( !Hooks::run( 'ShowSearchHit', [
$this->specialPage, $result, $terms,
&$link, &$redirect, &$section, &$extract,
* title with highlighted words).
*
* @param SearchResult $result
- * @param string[] $terms
* @param int $position
* @return string HTML
*/
- protected function generateMainLinkHtml( SearchResult $result, $terms, $position ) {
+ protected function generateMainLinkHtml( SearchResult $result, $position ) {
$snippet = $result->getTitleSnippet();
if ( $snippet === '' ) {
$snippet = null;
$attributes = [ 'data-serp-pos' => $position ];
Hooks::run( 'ShowSearchHitTitle',
- [ &$title, &$snippet, $result, $terms, $this->specialPage, &$query, &$attributes ] );
+ [ &$title, &$snippet, $result,
+ $result instanceof \SqlSearchResult ? $result->getTermMatches() : [],
+ $this->specialPage, &$query, &$attributes ] );
$link = $this->linkRenderer->makeLink(
$title,
/**
* @param SearchResult $result The result to render
- * @param string[] $terms Terms to be highlighted (@see SearchResult::getTextSnippet)
* @param int $position The result position, including offset
* @return string HTML
*/
- public function render( SearchResult $result, $terms, $position ) {
+ public function render( SearchResult $result, $position ) {
$title = $result->getTitle();
$titleSnippet = $result->getTitleSnippet();
- $snippet = $result->getTextSnippet( $terms );
+ $snippet = $result->getTextSnippet();
if ( $titleSnippet ) {
$titleSnippet = new HtmlArmor( $titleSnippet );
interface SearchResultWidget {
/**
* @param SearchResult $result The result to render
- * @param string[] $terms Terms to be highlighted (@see SearchResult::getTextSnippet)
* @param int $position The zero indexed result position, including offset
* @return string HTML
*/
- public function render( SearchResult $result, $terms, $position );
+ public function render( SearchResult $result, $position );
}
/**
* @param SearchResult $result The result to render
- * @param string[] $terms Terms to be highlighted (@see SearchResult::getTextSnippet)
* @param int $position The result position, including offset
* @return string HTML
*/
- public function render( SearchResult $result, $terms, $position ) {
+ public function render( SearchResult $result, $position ) {
$title = $result->getTitle();
$titleSnippet = $result->getTitleSnippet();
if ( $titleSnippet ) {
$match = $res->getIterator()->current();
$snippet = "A <span class='searchmatch'>" . $phrase . "</span>";
$this->assertStringStartsWith( $snippet,
- $match->getTextSnippet( $res->termMatches() ),
+ $match->getTextSnippet(),
"Highlight a phrase search" );
}