// execute worker
$this->worker->execute();
+ $this->worker->finalize();
+
// send current state
$this->sendResponse($progress, $this->worker->getParameters(), $this->worker->getProceedURL());
}
*/
public $searchIndexCondition = null;
- /**
- * class name for $searchIndexCondition object
- * @var string
- */
- public $searchIndexConditionClassName = 'wcf\system\database\util\PreparedStatementConditionBuilder';
-
/**
* search hash to modify existing search
* @var string
// default conditions
$userIDs = $this->getUserIDs();
- $this->searchIndexCondition = new $this->searchIndexConditionClassName(false);
+ $conditionBuilderClassName = SearchEngine::getInstance()->getConditionBuilderClassName();
+ $this->searchIndexCondition = new $conditionBuilderClassName(false);
// user ids
if (!empty($userIDs)) {
<?php
namespace wcf\system\search;
use wcf\system\SingletonFactory;
+use wcf\util\StringUtil;
/**
* Default implementation for search engines, this class should be extended by
* @subpackage system.search
* @category Community Framework
*/
-abstract class AbstractSearchEngine extends SingletonFactory implements ISearchEngine { }
+abstract class AbstractSearchEngine extends SingletonFactory implements ISearchEngine {
+ /**
+ * class name for preferred condition builder
+ * @var string
+ */
+ protected $conditionBuilderClassName = 'wcf\system\database\util\PreparedStatementConditionBuilder';
+
+ /**
+ * @see \wcf\system\search\ISearchEngine::getConditionBuilderClassName()
+ */
+ public function getConditionBuilderClassName() {
+ return $this->conditionBuilderClassName;
+ }
+
+ /**
+ * Manipulates the search term (< and > used as quotation marks):
+ *
+ * - <test foo> becomes <+test* +foo*>
+ * - <test -foo bar> becomes <+test* -foo* +bar*>
+ * - <test "foo bar"> becomes <+test* +"foo bar">
+ *
+ * @see http://dev.mysql.com/doc/refman/5.5/en/fulltext-boolean.html
+ *
+ * @param string $query
+ */
+ protected function parseSearchQuery($query) {
+ $query = StringUtil::trim($query);
+
+ // expand search terms with a * unless they're encapsulated with quotes
+ $inQuotes = false;
+ $previousChar = $tmp = '';
+ $controlCharacterOrSpace = false;
+ $chars = array('+', '-', '*');
+ $ftMinWordLen = $this->getFulltextMinimumWordLength();
+ for ($i = 0, $length = mb_strlen($query); $i < $length; $i++) {
+ $char = mb_substr($query, $i, 1);
+
+ if ($inQuotes) {
+ if ($char == '"') {
+ $inQuotes = false;
+ }
+ }
+ else {
+ if ($char == '"') {
+ $inQuotes = true;
+ }
+ else {
+ if ($char == ' ' && !$controlCharacterOrSpace) {
+ $controlCharacterOrSpace = true;
+ $tmp .= '*';
+ }
+ else if (in_array($char, $chars)) {
+ $controlCharacterOrSpace = true;
+ }
+ else {
+ $controlCharacterOrSpace = false;
+ }
+ }
+ }
+
+ /*
+ * prepend a plus sign (logical AND) if ALL these conditions are given:
+ *
+ * 1) previous character:
+ * - is empty (start of string)
+ * - is a space (MySQL uses spaces to separate words)
+ *
+ * 2) not within quotation marks
+ *
+ * 3) current char:
+ * - is NOT +, - or *
+ */
+ if (($previousChar == '' || $previousChar == ' ') && !$inQuotes && !in_array($char, $chars)) {
+ // check if the term is shorter than the minimum fulltext word length
+ if ($i + $ftMinWordLen <= $length) {
+ $term = '';// $char;
+ for ($j = $i, $innerLength = $ftMinWordLen + $i; $j < $innerLength; $j++) {
+ $currentChar = mb_substr($query, $j, 1);
+ if ($currentChar == '"' || $currentChar == ' ' || in_array($currentChar, $chars)) {
+ break;
+ }
+
+ $term .= $currentChar;
+ }
+
+ if (mb_strlen($term) == $ftMinWordLen) {
+ $tmp .= '+';
+ }
+ }
+ }
+
+ $tmp .= $char;
+ $previousChar = $char;
+ }
+
+ // handle last char
+ if (!$inQuotes && !$controlCharacterOrSpace) {
+ $tmp .= '*';
+ }
+
+ return $tmp;
+ }
+
+ /**
+ * Returns minimum word length for fulltext indices.
+ *
+ * @return integer
+ */
+ abstract protected function getFulltextMinimumWordLength();
+}
* @return boolean
*/
abstract protected function createSearchIndex(ObjectType $objectType);
+
+ /**
+ * @see \wcf\system\search\ISearchIndexManager::beginBulkOperation()
+ */
+ public function beginBulkOperation() {
+ // does nothing
+ }
+
+ /**
+ * @see \wcf\system\search\ISearchIndexManager::commitBulkOperation()
+ */
+ public function commitBulkOperation() {
+ // does nothing
+ }
}
}
/**
- * @see \wcf\system\search\ISearchableObjectType::getSpecialSQLQuery()
+ * @see \wcf\system\search\ISearchableObjectType::getOuterSQLQuery()
*/
- public function getSpecialSQLQuery(PreparedStatementConditionBuilder &$fulltextCondition = null, PreparedStatementConditionBuilder &$searchIndexConditions = null, PreparedStatementConditionBuilder &$additionalConditions = null, $orderBy = 'time DESC') {
+ public function getOuterSQLQuery($q, PreparedStatementConditionBuilder &$searchIndexConditions = null, PreparedStatementConditionBuilder &$additionalConditions = null) {
return '';
}
* @category Community Framework
*/
interface ISearchEngine {
+ /**
+ * Returns the condition builder class name required to provide conditions for getInnerJoin().
+ *
+ * @return string
+ */
+ public function getConditionBuilderClassName();
+
+ /**
+ * Returns the inner join query and the condition parameters. This method is allowed to return NULL
+ * for the 'fulltextCondition' index instead of a PreparedStatementConditionBuilder instance:
+ *
+ * array(
+ * 'fulltextCondition' => $fulltextCondition || null,
+ * 'sql' => $sql
+ * );
+ *
+ * @param string $objectTypeName
+ * @param string $q
+ * @param boolean $subjectOnly
+ * @param \wcf\system\database\util\PreparedStatementConditionBuilder $searchIndexCondition
+ * @param string $orderBy
+ * @param integer $limit
+ * @return array
+ */
+ public function getInnerJoin($objectTypeName, $q, $subjectOnly = false, PreparedStatementConditionBuilder $searchIndexCondition = null, $orderBy = 'time DESC', $limit = 1000);
+
/**
* Searches for the given string and returns the data of the found messages.
*
* @param string $username
* @param integer $languageID
* @param string $metaData
- * @param array<mixed> $additionalData
*/
- public function add($objectType, $objectID, $message, $subject, $time, $userID, $username, $languageID = null, $metaData = '', array $additionalData = array());
+ public function add($objectType, $objectID, $message, $subject, $time, $userID, $username, $languageID = null, $metaData = '');
/**
* Updates the search index.
* @param string $username
* @param integer $languageID
* @param string $metaData
- * @param array<mixed> $additionalData
*/
- public function update($objectType, $objectID, $message, $subject, $time, $userID, $username, $languageID = null, $metaData = '', array $additionalData = array());
+ public function update($objectType, $objectID, $message, $subject, $time, $userID, $username, $languageID = null, $metaData = '');
/**
* Deletes search index entries.
* Creates the search index for all searchable objects.
*/
public function createSearchIndices();
+
+ /**
+ * Begins the bulk operation.
+ */
+ public function beginBulkOperation();
+
+ /**
+ * Commits the bulk operation.
+ */
+ public function commitBulkOperation();
}
public function getFormTemplateName();
/**
- * Provides the option to replace the default search index SQL query by an own version.
+ * Replaces the outer SQL query with a custom version. Querying the search index requires the
+ * placeholder {WCF_SEARCH_INNER_JOIN} within an empty INNER JOIN() statement.
*
- * @param \wcf\system\database\util\PreparedStatementConditionBuilder $fulltextCondition
+ * @param string $q
* @param \wcf\system\database\util\PreparedStatementConditionBuilder $searchIndexConditions
* @param \wcf\system\database\util\PreparedStatementConditionBuilder $additionalConditions
- * @param string $orderBy
* @return string
*/
- public function getSpecialSQLQuery(PreparedStatementConditionBuilder &$fulltextCondition = null, PreparedStatementConditionBuilder &$searchIndexConditions = null, PreparedStatementConditionBuilder &$additionalConditions = null, $orderBy = 'time DESC');
+ public function getOuterSQLQuery($q, PreparedStatementConditionBuilder &$searchIndexConditions = null, PreparedStatementConditionBuilder &$additionalConditions = null);
/**
* Returns the name of the active main menu item.
namespace wcf\system\search;
use wcf\data\object\type\ObjectTypeCache;
use wcf\system\database\util\PreparedStatementConditionBuilder;
+use wcf\system\exception\SystemException;
use wcf\system\SingletonFactory;
/**
public function search($q, array $objectTypes, $subjectOnly = false, PreparedStatementConditionBuilder $searchIndexCondition = null, array $additionalConditions = array(), $orderBy = 'time DESC', $limit = 1000) {
return $this->getSearchEngine()->search($q, $objectTypes, $subjectOnly, $searchIndexCondition, $additionalConditions, $orderBy, $limit);
}
+
+ /**
+ * @see \wcf\system\search\ISearchEngine::getInnerJoin()
+ */
+ public function getInnerJoin($objectTypeName, $q, $subjectOnly = false, PreparedStatementConditionBuilder $searchIndexCondition = null, $orderBy = 'time DESC', $limit = 1000) {
+ $conditionBuilderClassName = $this->getConditionBuilderClassName();
+ if ($searchIndexCondition !== null && !($searchIndexCondition instanceof $conditionBuilderClassName)) {
+ throw new SystemException("Search engine '" . SEARCH_ENGINE . "' requires a different condition builder, please use 'SearchEngine::getInstance()->getConditionBuilderClassName()'!");
+ }
+
+ return $this->getSearchEngine()->getInnerJoin($objectTypeName, $q, $subjectOnly, $searchIndexCondition, $orderBy, $limit);
+ }
+
+ /**
+ * @see \wcf\system\search\ISearchEngine::getConditionBuilderClassName()
+ */
+ public function getConditionBuilderClassName() {
+ return $this->getSearchEngine()->getConditionBuilderClassName();
+ }
}
if ($this->searchIndexManager === null) {
$className = '';
if (SEARCH_ENGINE != 'mysql') {
- $className = 'wcf\system\search\\'.SEARCH_ENGINE.'\\'.ucfirst(SEARCH_ENGINE).'SearchEngine';
+ $className = 'wcf\system\search\\'.SEARCH_ENGINE.'\\'.ucfirst(SEARCH_ENGINE).'SearchIndexManager';
if (!class_exists($className)) {
$className = '';
}
/**
* @see \wcf\system\search\ISearchIndexManager::add()
*/
- public function add($objectType, $objectID, $message, $subject, $time, $userID, $username, $languageID = null, $metaData = '', array $additionalData = array()) {
- $this->getSearchIndexManager()->add($objectType, $objectID, $message, $subject, $time, $userID, $username, $languageID, $metaData, $additionalData);
+ public function add($objectType, $objectID, $message, $subject, $time, $userID, $username, $languageID = null, $metaData = '') {
+ $this->getSearchIndexManager()->add($objectType, $objectID, $message, $subject, $time, $userID, $username, $languageID, $metaData);
}
/**
* @see \wcf\system\search\ISearchIndexManager::update()
*/
- public function update($objectType, $objectID, $message, $subject, $time, $userID, $username, $languageID = null, $metaData = '', array $additionalData = array()) {
- $this->getSearchIndexManager()->update($objectType, $objectID, $message, $subject, $time, $userID, $username, $languageID, $metaData, $additionalData);
+ public function update($objectType, $objectID, $message, $subject, $time, $userID, $username, $languageID = null, $metaData = '') {
+ $this->getSearchIndexManager()->update($objectType, $objectID, $message, $subject, $time, $userID, $username, $languageID, $metaData);
}
/**
$this->getSearchIndexManager()->createSearchIndices();
}
+ /**
+ * @see \wcf\system\search\ISearchIndexManager::supportsBulkInsert()
+ */
+ public function supportsBulkInsert() {
+ return $this->getSearchIndexManager()->supportsBulkInsert();
+ }
+
+ /**
+ * @see \wcf\system\search\ISearchIndexManager::beginBulkOperation()
+ */
+ public function beginBulkOperation() {
+ $this->getSearchIndexManager()->beginBulkOperation();
+ }
+
+ /**
+ * @see \wcf\system\search\ISearchIndexManager::commitBulkOperation()
+ */
+ public function commitBulkOperation() {
+ $this->getSearchIndexManager()->commitBulkOperation();
+ }
+
/**
* Returns the database table name for the object type's search index.
*
use wcf\system\database\DatabaseException;
use wcf\system\exception\SystemException;
use wcf\system\search\AbstractSearchEngine;
+use wcf\system\search\SearchEngine;
use wcf\system\search\SearchIndexManager;
use wcf\system\WCF;
-use wcf\util\StringUtil;
-use wcf\system\search\SearchEngine;
/**
* Search engine using MySQL's FULLTEXT index.
* MySQL's minimum word length for fulltext indices
* @var integer
*/
- protected static $ftMinWordLen = null;
+ protected $ftMinWordLen = null;
/**
* @see \wcf\system\search\ISearchEngine::search()
*/
public function search($q, array $objectTypes, $subjectOnly = false, PreparedStatementConditionBuilder $searchIndexCondition = null, array $additionalConditions = array(), $orderBy = 'time DESC', $limit = 1000) {
- // handle sql types
- $fulltextCondition = null;
- $relevanceCalc = '';
- if (!empty($q)) {
- $q = $this->parseSearchQuery($q);
-
- $fulltextCondition = new PreparedStatementConditionBuilder(false);
- switch (WCF::getDB()->getDBType()) {
- case 'wcf\system\database\MySQLDatabase':
- $fulltextCondition->add("MATCH (subject".(!$subjectOnly ? ', message, metaData' : '').") AGAINST (? IN BOOLEAN MODE)", array($q));
- break;
-
- case 'wcf\system\database\PostgreSQLDatabase':
- // replace * with :*
- $q = str_replace('*', ':*', $q);
-
- $fulltextCondition->add("fulltextIndex".($subjectOnly ? "SubjectOnly" : '')." @@ to_tsquery(?)", array($q));
- break;
-
- default:
- throw new SystemException("your database type doesn't support fulltext search");
- break;
- }
-
- if ($orderBy == 'relevance ASC' || $orderBy == 'relevance DESC') {
- switch (WCF::getDB()->getDBType()) {
- case 'wcf\system\database\MySQLDatabase':
- $relevanceCalc = "MATCH (subject".(!$subjectOnly ? ', message, metaData' : '').") AGAINST ('".escapeString($q)."') + (5 / (1 + POW(LN(1 + (".TIME_NOW." - time) / 2592000), 2))) AS relevance";
- break;
-
- case 'wcf\system\database\PostgreSQLDatabase':
- $relevanceCalc = "ts_rank_cd(fulltextIndex".($subjectOnly ? "SubjectOnly" : '').", '".escapeString($q)."') AS relevance";
- break;
- }
- }
- }
-
// build search query
$sql = '';
$parameters = array();
foreach ($objectTypes as $objectTypeName) {
$objectType = SearchEngine::getInstance()->getObjectType($objectTypeName);
- if (!empty($sql)) $sql .= "\nUNION\n";
+
+ if (!empty($sql)) $sql .= "\nUNION ALL\n";
$additionalConditionsConditionBuilder = (isset($additionalConditions[$objectTypeName]) ? $additionalConditions[$objectTypeName] : null);
- if (($specialSQL = $objectType->getSpecialSQLQuery($fulltextCondition, $searchIndexCondition, $additionalConditionsConditionBuilder, $orderBy))) {
- $sql .= "(".$specialSQL.")";
+
+ $query = $objectType->getOuterSQLQuery($q, $searchIndexCondition, $additionalConditionsConditionBuilder);
+ if (empty($query)) {
+ $query = "SELECT ".$objectType->getIDFieldName()." AS objectID,
+ ".$objectType->getSubjectFieldName()." AS subject,
+ ".$objectType->getTimeFieldName()." AS time,
+ ".$objectType->getUsernameFieldName()." AS username,
+ '".$objectTypeName."' AS objectType
+ ".($orderBy == 'relevance ASC' || $orderBy == 'relevance DESC' ? ',search_index.relevance' : '')."
+ FROM ".$objectType->getTableName()."
+ INNER JOIN (
+ {WCF_SEARCH_INNER_JOIN}
+ ) search_index
+ ON (".$objectType->getIDFieldName()." = search_index.objectID)
+ ".$objectType->getJoins()."
+ ".(isset($additionalConditions[$objectTypeName]) ? $additionalConditions[$objectTypeName] : '');
}
- else {
- $sql .= "(
- SELECT ".$objectType->getIDFieldName()." AS objectID,
- ".$objectType->getSubjectFieldName()." AS subject,
- ".$objectType->getTimeFieldName()." AS time,
- ".$objectType->getUsernameFieldName()." AS username,
- '".$objectTypeName."' AS objectType
- ".($relevanceCalc ? ',search_index.relevance' : '')."
- FROM ".$objectType->getTableName()."
- INNER JOIN (
- SELECT objectID
- ".($relevanceCalc ? ','.$relevanceCalc : '')."
- FROM ".SearchIndexManager::getTableName($objectTypeName)."
- WHERE ".($fulltextCondition !== null ? $fulltextCondition : '')."
- ".(($searchIndexCondition !== null && $searchIndexCondition->__toString()) ? ($fulltextCondition !== null ? "AND " : '').$searchIndexCondition : '')."
- ".(!empty($orderBy) && $fulltextCondition === null ? 'ORDER BY '.$orderBy : '')."
- LIMIT 1000
- ) search_index
- ON (".$objectType->getIDFieldName()." = search_index.objectID)
- ".$objectType->getJoins()."
- ".(isset($additionalConditions[$objectTypeName]) ? $additionalConditions[$objectTypeName] : '')."
- )";
+
+ if (mb_strpos($query, '{WCF_SEARCH_INNER_JOIN}')) {
+ $innerJoin = $this->getInnerJoin($objectTypeName, $q, $subjectOnly, $searchIndexCondition, $orderBy, $limit);
+
+ $query = str_replace('{WCF_SEARCH_INNER_JOIN}', $innerJoin['sql'], $query);
+ if ($innerJoin['fulltextCondition'] !== null) $parameters = array_merge($parameters, $innerJoin['fulltextCondition']->getParameters());
}
- if ($fulltextCondition !== null) $parameters = array_merge($parameters, $fulltextCondition->getParameters());
if ($searchIndexCondition !== null) $parameters = array_merge($parameters, $searchIndexCondition->getParameters());
if (isset($additionalConditions[$objectTypeName])) $parameters = array_merge($parameters, $additionalConditions[$objectTypeName]->getParameters());
+
+ $sql .= $query;
}
if (empty($sql)) {
throw new SystemException('no object types given');
}
/**
- * Manipulates the search term (< and > used as quotation marks):
- *
- * - <test foo> becomes <+test* +foo*>
- * - <test -foo bar> becomes <+test* -foo* +bar*>
- * - <test "foo bar"> becomes <+test* +"foo bar">
- *
- * @see http://dev.mysql.com/doc/refman/5.5/en/fulltext-boolean.html
- *
- * @param string $query
+ * @see \wcf\system\search\ISearchEngine::getInnerJoin()
*/
- protected function parseSearchQuery($query) {
- $query = StringUtil::trim($query);
-
- // expand search terms with a * unless they're encapsulated with quotes
- $inQuotes = false;
- $previousChar = $tmp = '';
- $controlCharacterOrSpace = false;
- $chars = array('+', '-', '*');
- $ftMinWordLen = self::getFulltextMinimumWordLength();
- for ($i = 0, $length = mb_strlen($query); $i < $length; $i++) {
- $char = mb_substr($query, $i, 1);
+ public function getInnerJoin($objectTypeName, $q, $subjectOnly = false, PreparedStatementConditionBuilder $searchIndexCondition = null, $orderBy = 'time DESC', $limit = 1000) {
+ $fulltextCondition = null;
+ $relevanceCalc = '';
+ if (!empty($q)) {
+ $q = $this->parseSearchQuery($q);
- if ($inQuotes) {
- if ($char == '"') {
- $inQuotes = false;
- }
- }
- else {
- if ($char == '"') {
- $inQuotes = true;
- }
- else {
- if ($char == ' ' && !$controlCharacterOrSpace) {
- $controlCharacterOrSpace = true;
- $tmp .= '*';
- }
- else if (in_array($char, $chars)) {
- $controlCharacterOrSpace = true;
- }
- else {
- $controlCharacterOrSpace = false;
- }
- }
- }
+ $fulltextCondition = new PreparedStatementConditionBuilder(false);
+ $fulltextCondition->add("MATCH (subject".(!$subjectOnly ? ', message, metaData' : '').") AGAINST (? IN BOOLEAN MODE)", array($q));
- /*
- * prepend a plus sign (logical AND) if ALL these conditions are given:
- *
- * 1) previous character:
- * - is empty (start of string)
- * - is a space (MySQL uses spaces to separate words)
- *
- * 2) not within quotation marks
- *
- * 3) current char:
- * - is NOT +, - or *
- */
- if (($previousChar == '' || $previousChar == ' ') && !$inQuotes && !in_array($char, $chars)) {
- // check if the term is shorter than MySQL's ft_min_word_len
-
- if ($i + $ftMinWordLen <= $length) {
- $term = '';// $char;
- for ($j = $i, $innerLength = $ftMinWordLen + $i; $j < $innerLength; $j++) {
- $currentChar = mb_substr($query, $j, 1);
- if ($currentChar == '"' || $currentChar == ' ' || in_array($currentChar, $chars)) {
- break;
- }
-
- $term .= $currentChar;
- }
-
- if (mb_strlen($term) == $ftMinWordLen) {
- $tmp .= '+';
- }
- }
+ if ($orderBy == 'relevance ASC' || $orderBy == 'relevance DESC') {
+ $relevanceCalc = "MATCH (subject".(!$subjectOnly ? ', message, metaData' : '').") AGAINST ('".escapeString($q)."') + (5 / (1 + POW(LN(1 + (".TIME_NOW." - time) / 2592000), 2))) AS relevance";
}
-
- $tmp .= $char;
- $previousChar = $char;
}
- // handle last char
- if (!$inQuotes && !$controlCharacterOrSpace) {
- $tmp .= '*';
- }
+ $sql = "SELECT objectID
+ ".($relevanceCalc ? ','.$relevanceCalc : '')."
+ FROM ".SearchIndexManager::getTableName($objectTypeName)."
+ WHERE ".($fulltextCondition !== null ? $fulltextCondition : '')."
+ ".(($searchIndexCondition !== null && $searchIndexCondition->__toString()) ? ($fulltextCondition !== null ? "AND " : '').$searchIndexCondition : '')."
+ ".(!empty($orderBy) && $fulltextCondition === null ? 'ORDER BY '.$orderBy : '')."
+ LIMIT 1000";
- return $tmp;
+ return array(
+ 'fulltextCondition' => $fulltextCondition,
+ 'sql' => $sql
+ );
}
/**
- * Returns MySQL's minimum word length for fulltext indices.
- *
- * @return integer
+ * @see \wcf\system\search\AbstractSearchEngine::getFulltextMinimumWordLength()
*/
- protected static function getFulltextMinimumWordLength() {
- if (self::$ftMinWordLen === null) {
+ protected function getFulltextMinimumWordLength() {
+ if ($this->ftMinWordLen === null) {
$sql = "SHOW VARIABLES LIKE ?";
$statement = WCF::getDB()->prepareStatement($sql);
$row = array('Value' => 4);
}
- self::$ftMinWordLen = $row['Value'];
+ $this->ftMinWordLen = $row['Value'];
}
- return self::$ftMinWordLen;
+ return $this->ftMinWordLen;
}
}
/**
* @see \wcf\system\search\ISearchIndexManager::add()
*/
- public function add($objectType, $objectID, $message, $subject, $time, $userID, $username, $languageID = null, $metaData = '', array $additionalData = array()) {
+ public function add($objectType, $objectID, $message, $subject, $time, $userID, $username, $languageID = null, $metaData = '') {
if ($languageID === null) $languageID = 0;
// save new entry
/**
* @see \wcf\system\search\ISearchIndexManager::update()
*/
- public function update($objectType, $objectID, $message, $subject, $time, $userID, $username, $languageID = null, $metaData = '', array $additionalData = array()) {
+ public function update($objectType, $objectID, $message, $subject, $time, $userID, $username, $languageID = null, $metaData = '') {
// delete existing entry
$this->delete($objectType, array($objectID));
// save new entry
- $this->add($objectType, $objectID, $message, $subject, $time, $userID, $username, $languageID, $metaData, $additionalData);
+ $this->add($objectType, $objectID, $message, $subject, $time, $userID, $username, $languageID, $metaData);
}
/**
use wcf\system\request\LinkHandler;
use wcf\system\WCF;
use wcf\util\ClassUtil;
+use wcf\system\search\SearchIndexManager;
/**
* Abstract implementation of rebuild data worker.
public function execute() {
$this->objectList->readObjects();
+ SearchIndexManager::getInstance()->beginBulkOperation();
+
EventHandler::getInstance()->fireAction($this, 'execute');
}
$this->objectList->sqlLimit = $this->limit;
$this->objectList->sqlOffset = $this->limit * $this->loopCount;
}
+
+ /**
+ * @see \wcf\system\worker\IWorker::finalize()
+ */
+ public function finalize() {
+ SearchIndexManager::getInstance()->commitBulkOperation();
+ }
}
public function getParameters() {
return $this->parameters;
}
+
+ /**
+ * @see \wcf\system\worker\IWorker::finalize()
+ */
+ public function finalize() {
+ // does nothing
+ }
}
* @return string
*/
public function getProceedURL();
+
+ /**
+ * Executes actions after worker has been executed.
+ */
+ public function finalize();
}
<item name="wcf.acp.option.category.general.system.http"><![CDATA[HTTP]]></item>
<item name="wcf.acp.option.category.general.system.proxy"><![CDATA[Proxy-Server]]></item>
<item name="wcf.acp.option.category.general.system.proxy.description"><![CDATA[Hier können Sie optional Proxy-Server für Verbindungen zu externen Servern konfigurieren.]]></item>
+ <item name="wcf.acp.option.category.general.system.search"><![CDATA[Suche]]></item>
+ <item name="wcf.acp.option.category.general.system.search.description"><![CDATA[Die Änderung des Backends erfordert die Aktualisierung des Such-Index über die Seite „Anzeigen aktualisieren“.]]></item>
<item name="wcf.acp.option.category.general.mail"><![CDATA[E-Mails]]></item>
<item name="wcf.acp.option.category.general.mail.general"><![CDATA[Allgemein]]></item>
<item name="wcf.acp.option.category.general.mail.send"><![CDATA[Versand]]></item>
<item name="wcf.acp.option.gravatar_default_type.wavatar"><![CDATA[Wavatar]]></item>
<item name="wcf.acp.option.gravatar_default_type.monsterid"><![CDATA[Monster-ID]]></item>
<item name="wcf.acp.option.gravatar_default_type.retro"><![CDATA[Retro]]></item>
+ <item name="wcf.acp.option.search_engine"><![CDATA[Suche]]></item>
+ <item name="wcf.acp.option.search_engine.mysql"><![CDATA[MySQL FULLTEXT (Standard)]]></item>
</category>
<category name="wcf.acp.package">
<item name="wcf.acp.option.category.general.system.http"><![CDATA[HTTP]]></item>
<item name="wcf.acp.option.category.general.system.proxy"><![CDATA[Proxy-Server]]></item>
<item name="wcf.acp.option.category.general.system.proxy.description"><![CDATA[Optionally provide a Proxy-Server for outgoing connections.]]></item>
+ <item name="wcf.acp.option.category.general.system.search"><![CDATA[Search]]></item>
+ <item name="wcf.acp.option.category.general.system.search.description"><![CDATA[Changing the backend requires a rebuild of the search indices available on the page “Rebuild Data”.]]></item>
<item name="wcf.acp.option.category.general.mail"><![CDATA[Emails]]></item>
<item name="wcf.acp.option.category.general.mail.general"><![CDATA[General]]></item>
<item name="wcf.acp.option.category.general.mail.send"><![CDATA[Sending]]></item>
<item name="wcf.acp.option.gravatar_default_type.wavatar"><![CDATA[Wavatar]]></item>
<item name="wcf.acp.option.gravatar_default_type.monsterid"><![CDATA[Monster id]]></item>
<item name="wcf.acp.option.gravatar_default_type.retro"><![CDATA[Retro]]></item>
+ <item name="wcf.acp.option.search_engine"><![CDATA[Search Engine]]></item>
+ <item name="wcf.acp.option.search_engine.mysql"><![CDATA[MySQL FULLTEXT (default)]]></item>
</category>
<category name="wcf.acp.package">