*/
use Wikimedia\Rdbms\IDatabase;
+use Wikimedia\Rdbms\ILoadBalancer;
/**
* Base search engine base class for database-backed searches
* @since 1.23
*/
abstract class SearchDatabase extends SearchEngine {
- /**
- * @var IDatabase Replica database from which to read results
- */
+ /** @var ILoadBalancer */
+ protected $lb;
+ /** @var IDatabase (backwards compatibility) */
protected $db;
/**
- * @param IDatabase|null $db The database to search from
+ * @param ILoadBalancer $lb The load balancer for the DB cluster to search on
*/
- public function __construct( IDatabase $db = null ) {
- $this->db = $db ?: wfGetDB( DB_REPLICA );
+ public function __construct( ILoadBalancer $lb ) {
+ $this->lb = $lb;
+ // @TODO: remove this deprecated field in 1.35
+ $this->db = $lb->getLazyConnectionRef( DB_REPLICA ); // b/c
}
/**
<?php
use Wikimedia\Rdbms\IDatabase;
+use Wikimedia\Rdbms\ILoadBalancer;
+use MediaWiki\MediaWikiServices;
/**
* Factory class for SearchEngine.
* @return SearchEngine
*/
public function create( $type = null ) {
- $dbr = null;
+ $configuredClass = $this->config->getSearchType();
+ $alternativesClasses = $this->config->getSearchTypes();
- $configType = $this->config->getSearchType();
- $alternatives = $this->config->getSearchTypes();
-
- if ( $type && in_array( $type, $alternatives ) ) {
+ $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
+ if ( $type !== null && in_array( $type, $alternativesClasses ) ) {
$class = $type;
- } elseif ( $configType !== null ) {
- $class = $configType;
+ } elseif ( $configuredClass !== null ) {
+ $class = $configuredClass;
} else {
- $dbr = wfGetDB( DB_REPLICA );
- $class = self::getSearchEngineClass( $dbr );
+ $class = self::getSearchEngineClass( $lb );
}
- $search = new $class( $dbr );
- return $search;
+ if ( is_subclass_of( $class, SearchDatabase::class ) ) {
+ return new $class( $lb );
+ } else {
+ return new $class();
+ }
}
/**
- * @param IDatabase $db
+ * @param IDatabase|ILoadBalancer $dbOrLb
* @return string SearchEngine subclass name
* @since 1.28
*/
- public static function getSearchEngineClass( IDatabase $db ) {
- switch ( $db->getType() ) {
+ public static function getSearchEngineClass( $dbOrLb ) {
+ $type = ( $dbOrLb instanceof IDatabase )
+ ? $dbOrLb->getType()
+ : $dbOrLb->getServerType( $dbOrLb->getWriterIndex() );
+
+ switch ( $type ) {
case 'sqlite':
return SearchSqlite::class;
case 'mysql':
* @return SqlSearchResultSet
*/
protected function doSearchTextInDB( $term ) {
- $resultSet = $this->db->query( $this->getQuery( $this->filter( $term ), true ) );
+ $dbr = $this->lb->getConnectionRef( DB_REPLICA );
+ $resultSet = $dbr->query( $this->getQuery( $this->filter( $term ), true ) );
+
return new SqlSearchResultSet( $resultSet, $this->searchTerms );
}
* @return SqlSearchResultSet
*/
protected function doSearchTitleInDB( $term ) {
- $resultSet = $this->db->query( $this->getQuery( $this->filter( $term ), false ) );
+ $dbr = $this->lb->getConnectionRef( DB_REPLICA );
+ $resultSet = $dbr->query( $this->getQuery( $this->filter( $term ), false ) );
+
return new SqlSearchResultSet( $resultSet, $this->searchTerms );
}
* @return string
*/
private function queryLimit( $sql ) {
- return $this->db->limitResult( $sql, $this->limit, $this->offset );
+ $dbr = $this->lb->getConnectionRef( DB_REPLICA );
+
+ return $dbr->limitResult( $sql, $this->limit, $this->offset );
}
/**
*/
private function queryMain( $filteredTerm, $fulltext ) {
$match = $this->parseQuery( $filteredTerm, $fulltext );
- $page = $this->db->tableName( 'page' );
- $searchindex = $this->db->tableName( 'searchindex' );
+ $dbr = $this->lb->getMaintenanceConnectionRef( DB_REPLICA );
+ $page = $dbr->tableName( 'page' );
+ $searchindex = $dbr->tableName( 'searchindex' );
return 'SELECT page_id, page_namespace, page_title, ftindex.[RANK]' .
"FROM $page,FREETEXTTABLE($searchindex , $match, LANGUAGE 'English') as ftindex " .
}
}
- $searchon = $this->db->addQuotes( implode( ',', $q ) );
+ $dbr = $this->lb->getConnectionRef( DB_REPLICA );
+ $searchon = $dbr->addQuotes( implode( ',', $q ) );
$field = $this->getIndexField( $fulltext );
+
return "$field, $searchon";
}
// to properly decode the stream as UTF-8. SQL doesn't support UTF8 as a data type
// but the indexer will correctly handle it by this method. Since all we are doing
// is passing this data to the indexer and never retrieving it via PHP, this will save space
- $table = $this->db->tableName( 'searchindex' );
+ $dbr = $this->lb->getMaintenanceConnectionRef( DB_MASTER );
+ $table = $dbr->tableName( 'searchindex' );
$utf8bom = '0xEFBBBF';
$si_title = $utf8bom . bin2hex( $title );
$si_text = $utf8bom . bin2hex( $text );
$sql = "DELETE FROM $table WHERE si_page = $id;";
$sql .= "INSERT INTO $table (si_page, si_title, si_text) VALUES ($id, $si_title, $si_text)";
- return $this->db->query( $sql, 'SearchMssql::update' );
+ return $dbr->query( $sql, 'SearchMssql::update' );
}
/**
* @return bool|IResultWrapper
*/
function updateTitle( $id, $title ) {
- $table = $this->db->tableName( 'searchindex' );
+ $dbr = $this->lb->getMaintenanceConnectionRef( DB_MASTER );
+ $table = $dbr->tableName( 'searchindex' );
// see update for why we are using the utf8bom
$utf8bom = '0xEFBBBF';
$si_title = $utf8bom . bin2hex( $title );
$sql = "DELETE FROM $table WHERE si_page = $id;";
$sql .= "INSERT INTO $table (si_page, si_title, si_text) VALUES ($id, $si_title, 0x00)";
- return $this->db->query( $sql, 'SearchMssql::updateTitle' );
+ return $dbr->query( $sql, 'SearchMssql::updateTitle' );
}
}
wfDebug( __METHOD__ . ": Can't understand search query '{$filteredText}'\n" );
}
- $searchon = $this->db->addQuotes( $searchon );
+ $dbr = $this->lb->getConnectionRef( DB_REPLICA );
+ $searchon = $dbr->addQuotes( $searchon );
$field = $this->getIndexField( $fulltext );
return [
" MATCH($field) AGAINST($searchon IN BOOLEAN MODE) ",
$filteredTerm = $this->filter( $term );
$query = $this->getQuery( $filteredTerm, $fulltext );
- $resultSet = $this->db->select(
+ $dbr = $this->lb->getConnectionRef( DB_REPLICA );
+ $resultSet = $dbr->select(
$query['tables'], $query['fields'], $query['conds'],
__METHOD__, $query['options'], $query['joins']
);
$total = null;
$query = $this->getCountQuery( $filteredTerm, $fulltext );
- $totalResult = $this->db->select(
+ $totalResult = $dbr->select(
$query['tables'], $query['fields'], $query['conds'],
__METHOD__, $query['options'], $query['joins']
);
protected function queryFeatures( &$query ) {
foreach ( $this->features as $feature => $value ) {
if ( $feature === 'title-suffix-filter' && $value ) {
- $query['conds'][] = 'page_title' . $this->db->buildLike( $this->db->anyString(), $value );
+ $dbr = $this->lb->getConnectionRef( DB_REPLICA );
+ $query['conds'][] = 'page_title' . $dbr->buildLike( $dbr->anyString(), $value );
}
}
}
* @param string $text
*/
function update( $id, $title, $text ) {
- $dbw = wfGetDB( DB_MASTER );
+ $dbw = $this->lb->getConnectionRef( DB_MASTER );
$dbw->replace( 'searchindex',
[ 'si_page' ],
[
* @param string $title
*/
function updateTitle( $id, $title ) {
- $dbw = wfGetDB( DB_MASTER );
-
+ $dbw = $this->lb->getConnectionRef( DB_MASTER );
$dbw->update( 'searchindex',
[ 'si_title' => $this->normalizeText( $title ) ],
[ 'si_page' => $id ],
- __METHOD__,
- [ $dbw->lowPriorityOption() ] );
+ __METHOD__
+ );
}
/**
* @param string $title Title of page that was deleted
*/
function delete( $id, $title ) {
- $dbw = wfGetDB( DB_MASTER );
-
+ $dbw = $this->lb->getConnectionRef( DB_MASTER );
$dbw->delete( 'searchindex', [ 'si_page' => $id ], __METHOD__ );
}
if ( is_null( self::$mMinSearchLength ) ) {
$sql = "SHOW GLOBAL VARIABLES LIKE 'ft\\_min\\_word\\_len'";
- $dbr = wfGetDB( DB_REPLICA );
+ $dbr = $this->lb->getConnectionRef( DB_REPLICA );
$result = $dbr->query( $sql, __METHOD__ );
$row = $result->fetchObject();
$result->free();
return new SqlSearchResultSet( false, '' );
}
- $resultSet = $this->db->query( $this->getQuery( $this->filter( $term ), true ) );
+ $dbr = $this->lb->getConnectionRef( DB_REPLICA );
+ $resultSet = $dbr->query( $this->getQuery( $this->filter( $term ), true ) );
return new SqlSearchResultSet( $resultSet, $this->searchTerms );
}
return new SqlSearchResultSet( false, '' );
}
- $resultSet = $this->db->query( $this->getQuery( $this->filter( $term ), false ) );
+ $dbr = $this->lb->getConnectionRef( DB_REPLICA );
+ $resultSet = $dbr->query( $this->getQuery( $this->filter( $term ), false ) );
return new SqlSearchResultSet( $resultSet, $this->searchTerms );
}
if ( $this->namespaces === [] ) {
$namespaces = '0';
} else {
- $namespaces = $this->db->makeList( $this->namespaces );
+ $dbr = $this->lb->getConnectionRef( DB_REPLICA );
+ $namespaces = $dbr->makeList( $this->namespaces );
}
return 'AND page_namespace IN (' . $namespaces . ')';
}
* @return string
*/
private function queryLimit( $sql ) {
- return $this->db->limitResult( $sql, $this->limit, $this->offset );
+ $dbr = $this->lb->getConnectionRef( DB_REPLICA );
+
+ return $dbr->limitResult( $sql, $this->limit, $this->offset );
}
/**
*/
function queryMain( $filteredTerm, $fulltext ) {
$match = $this->parseQuery( $filteredTerm, $fulltext );
- $page = $this->db->tableName( 'page' );
- $searchindex = $this->db->tableName( 'searchindex' );
+
+ $dbr = $this->lb->getMaintenanceConnectionRef( DB_REPLICA );
+ $page = $dbr->tableName( 'page' );
+ $searchindex = $dbr->tableName( 'searchindex' );
+
return 'SELECT page_id, page_namespace, page_title ' .
"FROM $page,$searchindex " .
'WHERE page_id=si_page AND ' . $match;
}
}
- $searchon = $this->db->addQuotes( ltrim( $searchon, ' &' ) );
+ $dbr = $this->lb->getConnectionRef( DB_REPLICA );
+ $searchon = $dbr->addQuotes( ltrim( $searchon, ' &' ) );
$field = $this->getIndexField( $fulltext );
+
return " CONTAINS($field, $searchon, 1) > 0 ";
}
* @param string $text
*/
function update( $id, $title, $text ) {
- $dbw = wfGetDB( DB_MASTER );
+ $dbw = $this->lb->getConnection( DB_MASTER );
$dbw->replace( 'searchindex',
[ 'si_page' ],
[
* @param string $title
*/
function updateTitle( $id, $title ) {
- $dbw = wfGetDB( DB_MASTER );
-
+ $dbw = $this->lb->getConnectionRef( DB_MASTER );
$dbw->update( 'searchindex',
[ 'si_title' => $title ],
[ 'si_page' => $id ],
protected function doSearchTitleInDB( $term ) {
$q = $this->searchQuery( $term, 'titlevector', 'page_title' );
$olderror = error_reporting( E_ERROR );
- $resultSet = $this->db->query( $q, 'SearchPostgres', true );
+ $dbr = $this->lb->getConnectionRef( DB_REPLICA );
+ $resultSet = $dbr->query( $q, 'SearchPostgres', true );
error_reporting( $olderror );
return new SqlSearchResultSet( $resultSet, $this->searchTerms );
}
protected function doSearchTextInDB( $term ) {
$q = $this->searchQuery( $term, 'textvector', 'old_text' );
$olderror = error_reporting( E_ERROR );
- $resultSet = $this->db->query( $q, 'SearchPostgres', true );
+ $dbr = $this->lb->getConnectionRef( DB_REPLICA );
+ $resultSet = $dbr->query( $q, 'SearchPostgres', true );
error_reporting( $olderror );
return new SqlSearchResultSet( $resultSet, $this->searchTerms );
}
$searchstring = preg_replace( '/^[\'"](.*)[\'"]$/', "$1", $searchstring );
# # Quote the whole thing
- $searchstring = $this->db->addQuotes( $searchstring );
+ $dbr = $this->lb->getConnectionRef( DB_REPLICA );
+ $searchstring = $dbr->addQuotes( $searchstring );
wfDebug( "parseQuery returned: $searchstring \n" );
# # We need a separate query here so gin does not complain about empty searches
$sql = "SELECT to_tsquery($searchstring)";
- $res = $this->db->query( $sql );
+ $dbr = $this->lb->getConnectionRef( DB_REPLICA );
+ $res = $dbr->query( $sql );
if ( !$res ) {
# # TODO: Better output (example to catch: one 'two)
die( "Sorry, that was not a valid search string. Please go back and try again" );
if ( count( $this->namespaces ) < 1 ) {
$query .= ' AND page_namespace = 0';
} else {
- $namespaces = $this->db->makeList( $this->namespaces );
+ $namespaces = $dbr->makeList( $this->namespaces );
$query .= " AND page_namespace IN ($namespaces)";
}
}
$query .= " ORDER BY score DESC, page_id DESC";
- $query .= $this->db->limitResult( '', $this->limit, $this->offset );
+ $query .= $dbr->limitResult( '', $this->limit, $this->offset );
wfDebug( "searchQuery returned: $query \n" );
" AND s.slot_role_id = " . $slotRoleStore->getId( SlotRecord::MAIN ) . " " .
" AND c.content_id = s.slot_content_id " .
" ORDER BY old_rev_text_id DESC OFFSET 1)";
- $this->db->query( $sql );
+
+ $dbw = $this->lb->getConnectionRef( DB_MASTER );
+ $dbw->query( $sql );
+
return true;
}
function updateTitle( $id, $title ) {
return true;
}
-
}
*/
use MediaWiki\MediaWikiServices;
+use Wikimedia\Rdbms\DatabaseSqlite;
/**
* Search engine hook for SQLite
* @return bool
*/
function fulltextSearchSupported() {
- return $this->db->checkForEnabledSearch();
+ /** @var DatabaseSqlite $dbr */
+ $dbr = $this->lb->getConnection( DB_REPLICA );
+
+ return $dbr->checkForEnabledSearch();
}
/**
wfDebug( __METHOD__ . ": Can't understand search query '{$filteredText}'\n" );
}
- $searchon = $this->db->addQuotes( $searchon );
+ $dbr = $this->lb->getConnectionRef( DB_REPLICA );
+ $searchon = $dbr->addQuotes( $searchon );
$field = $this->getIndexField( $fulltext );
+
return " $field MATCH $searchon ";
}
$filteredTerm =
$this->filter( MediaWikiServices::getInstance()->getContentLanguage()->lc( $term ) );
- $resultSet = $this->db->query( $this->getQuery( $filteredTerm, $fulltext ) );
+ $dbr = $this->lb->getConnectionRef( DB_REPLICA );
+ $resultSet = $dbr->query( $this->getQuery( $filteredTerm, $fulltext ) );
$total = null;
- $totalResult = $this->db->query( $this->getCountQuery( $filteredTerm, $fulltext ) );
+ $totalResult = $dbr->query( $this->getCountQuery( $filteredTerm, $fulltext ) );
$row = $totalResult->fetchObject();
if ( $row ) {
$total = intval( $row->c );
if ( $this->namespaces === [] ) {
$namespaces = '0';
} else {
- $namespaces = $this->db->makeList( $this->namespaces );
+ $dbr = $this->lb->getConnectionRef( DB_REPLICA );
+ $namespaces = $dbr->makeList( $this->namespaces );
}
return 'AND page_namespace IN (' . $namespaces . ')';
}
* @return string
*/
private function limitResult( $sql ) {
- return $this->db->limitResult( $sql, $this->limit, $this->offset );
+ $dbr = $this->lb->getConnectionRef( DB_REPLICA );
+
+ return $dbr->limitResult( $sql, $this->limit, $this->offset );
}
/**
*/
private function queryMain( $filteredTerm, $fulltext ) {
$match = $this->parseQuery( $filteredTerm, $fulltext );
- $page = $this->db->tableName( 'page' );
- $searchindex = $this->db->tableName( 'searchindex' );
+ $dbr = $this->lb->getMaintenanceConnectionRef( DB_REPLICA );
+ $page = $dbr->tableName( 'page' );
+ $searchindex = $dbr->tableName( 'searchindex' );
return "SELECT $searchindex.rowid, page_namespace, page_title " .
"FROM $page,$searchindex " .
"WHERE page_id=$searchindex.rowid AND $match";
private function getCountQuery( $filteredTerm, $fulltext ) {
$match = $this->parseQuery( $filteredTerm, $fulltext );
- $page = $this->db->tableName( 'page' );
- $searchindex = $this->db->tableName( 'searchindex' );
+ $dbr = $this->lb->getMaintenanceConnectionRef( DB_REPLICA );
+ $page = $dbr->tableName( 'page' );
+ $searchindex = $dbr->tableName( 'searchindex' );
return "SELECT COUNT(*) AS c " .
"FROM $page,$searchindex " .
"WHERE page_id=$searchindex.rowid AND $match " .
}
// @todo find a method to do it in a single request,
// couldn't do it so far due to typelessness of FTS3 tables.
- $dbw = wfGetDB( DB_MASTER );
-
+ $dbw = $this->lb->getConnectionRef( DB_MASTER );
$dbw->delete( 'searchindex', [ 'rowid' => $id ], __METHOD__ );
-
$dbw->insert( 'searchindex',
[
'rowid' => $id,
if ( !$this->fulltextSearchSupported() ) {
return;
}
- $dbw = wfGetDB( DB_MASTER );
+ $dbw = $this->lb->getConnectionRef( DB_MASTER );
$dbw->update( 'searchindex',
[ 'si_title' => $title ],
[ 'rowid' => $id ],
public static $title;
public static $text;
- public function __construct( $db ) {
- }
-
public function update( $id, $title, $text ) {
self::$id = $id;
self::$title = $title;
<?php
+use Wikimedia\Rdbms\LoadBalancerSingle;
+
/**
* @group Search
* @group Database
]
] );
- $this->search = new $searchType( $this->db );
+ $lb = LoadBalancerSingle::newFromConnection( $this->db );
+ $this->search = new $searchType( $lb );
}
protected function tearDown() {