|
Perforce Chronicle 2012.2/486814
API Documentation
|
Manages the search and index. More...
Public Member Functions | |
| indexAction () | |
| Show a manage search page. | |
| optimizeAction () | |
| optimize the Lucene search index. | |
| rebuildAction () | |
| Rebuild the Lucene search index. | |
| statusAction () | |
| Provide a status update in Json format. | |
Public Attributes | |
| $contexts | |
| const | REBUILD_BATCH_SIZE = 100 |
Protected Member Functions | |
| _removeSearchIndex ($indexName) | |
| Remove the search index by deleting all files from its folder on disk. | |
| _setActiveSearchIndex ($index) | |
| Make a search index active. | |
Static Protected Member Functions | |
| static | _nomaliseIndexName ($index) |
| Normalise a Lucene search index name. | |
Protected Attributes | |
| $_activeIndexPath = 'search-index' | |
| $_maintenanceLockFile = 'search.maintenance.lock.file' | |
| $_maintenanceStatusFile = 'search.maintenance.status.file' | |
| $_statusFile = null | |
Manages the search and index.
| static Search_ManageController::_nomaliseIndexName | ( | $ | index | ) | [static, protected] |
Normalise a Lucene search index name.
| string | $index | the original index name |
{
// if the name is not a string
if (!is_string($index)) {
return '';
}
// trim spaces and slashes
$index = trim($index, " \t\n\r\0\x0B/\\");
return $index;
}
| Search_ManageController::_removeSearchIndex | ( | $ | indexName | ) | [protected] |
Remove the search index by deleting all files from its folder on disk.
| string | $indexName | the folder name of a search index |
{
// if the index folder is an empty string, nothing to do
if (strlen($indexName) == 0) {
return true;
}
$indexDirectory = P4Cms_Site::fetchActive()->getDataPath() . '/' . $indexName;
// if the index does not exist, nothing to do
if (!file_exists($indexDirectory)) {
return true;
}
$files = scandir($indexDirectory);
// remove all files in the search index folder
foreach ($files as $file) {
if (is_dir($file)) {
continue;
}
unlink($indexDirectory . '/' . $file);
}
return rmdir($indexDirectory);
}
| Search_ManageController::_setActiveSearchIndex | ( | $ | index | ) | [protected] |
Make a search index active.
| string | $index | the search index directory |
{
// if $index is not a string or it's an empty string
// we cannot get search index
$index = $this->_nomaliseIndexName($index);
if (strlen($index) == 0) {
throw new Zend_Search_Exception(
'Require a folder name to set the active Search index.'
);
}
$activeIndex = $this->_activeIndexPath;
// nothing to do if the index given is the active index
if ($index == $activeIndex) {
return true;
}
// remove the active index contents
if (!$this->_removeSearchIndex($activeIndex)) {
throw new Zend_Search_Exception(
"Failed removing the active search index: $activeIndex."
);
}
$dataPath = P4Cms_Site::fetchActive()->getDataPath() . '/';
$newPath = $dataPath . $index;
$activePath = $dataPath . $activeIndex;
return rename($newPath, $activePath);
}
| Search_ManageController::indexAction | ( | ) |
Show a manage search page.
{
// enforce permissions.
$this->acl->check('search', 'manage');
$request = $this->getRequest();
// get the search form.
$form = new Search_Form_Manage;
if ($request->isPost()) {
$data = $request->getPost();
if ( $form->isValid($data)) {
$maxBufferedDocs = $data['maxBufferedDocs'];
$maxMergeDocs = $data['maxMergeDocs'];
$mergeFactor = $data['mergeFactor'];
$config = array();
if (strlen($maxBufferedDocs) != 0) {
$config['maxBufferedDocs'] = $maxBufferedDocs;
} else {
$config['maxBufferedDocs'] = Search_Module::getMaxBufferedDocs();
}
if (strlen($maxMergeDocs) != 0) {
$config['maxMergeDocs'] = $maxMergeDocs;
} else {
$config['maxMergeDocs'] = Search_Module::getMaxMergeDocs();
}
if (strlen($mergeFactor) != 0) {
$config['mergeFactor'] = $mergeFactor;
} else {
$config['mergeFactor'] = Search_Module::getMergeFactor();
}
$this->_saveConfig($config);
P4Cms_Notifications::add(
'Search configuration saved.',
P4Cms_Notifications::SEVERITY_SUCCESS
);
$this->redirector->gotoSimple('index');
}
} else {
$data = array();
$data['maxBufferedDocs'] = Search_Module::getMaxBufferedDocs();
$data['maxMergeDocs'] = (Search_Module::getMaxMergeDocs()
&& (Search_Module::getMaxMergeDocs() != PHP_INT_MAX))
? Search_Module::getMaxMergeDocs()
: '';
$data['mergeFactor'] = Search_Module::getMergeFactor();
$form->populate($data);
}
$this->view->form = $form;
$this->getHelper('layout')->setLayout('manage-layout');
$this->view->headTitle()->set('Manage Search');
}
| Search_ManageController::optimizeAction | ( | ) |
optimize the Lucene search index.
{
// enforce permissions.
$this->acl->check('search', 'manage');
// check if there is another maintenance task (optimize/rebuild)
// running. if it is, redirect to the status page
$maitenanceLockFile = P4Cms_Site::fetchActive()->getDataPath()
. '/' . $this->_maintenanceLockFile;
if (file_exists($maitenanceLockFile)) {
$redirector = $this->_helper->getHelper('redirector');
$redirector->gotoSimple('status');
return;
}
// create the maintenance lock file
touch($maitenanceLockFile);
P4Cms_Log::log(
"optimize Search Index: BEGIN; pid=". getmypid(),
P4Cms_Log::DEBUG
);
// put the current task in the session
$_SESSION['searchMaintenanceTask'] = 'optimize';
$this->_writeStatusFile(
array(
'action' => 'optimize',
'label' => 'optimizing search index',
'message' => 'Start optimizing search index.',
'time' => time(),
'done' => false,
)
);
// close the session but continue running, since index rebuilt may
// take longer than browser timeout
$this->getHelper('browserDisconnect')->disconnect('status', 10);
$index = Search_Module::factory();
$index->optimize();
$this->_writeStatusFile(
array(
'action' => 'optimize',
'label' => 'optimizing search index',
'message' => 'Done. Search index optimization completed.',
'time' => time(),
'done' => true,
)
);
unlink($maitenanceLockFile);
}
| Search_ManageController::rebuildAction | ( | ) |
Rebuild the Lucene search index.
p4cms.search.index.rebuild Return a Zend_Paginator of P4Cms_Content entries (or null) to be included when the search index is rebuilt.
{
// enforce permissions.
$this->acl->check('search', 'manage');
// check if there is another maintenance task (optimize/rebuild)
// running. if it is, redirect to the status page
$maitenanceLockFile = P4Cms_Site::fetchActive()->getDataPath()
. '/' . $this->_maintenanceLockFile;
if (file_exists($maitenanceLockFile)) {
$redirector = $this->_helper->getHelper('redirector');
$redirector->gotoSimple('status');
return;
}
// create the maintenance lock file
touch($maitenanceLockFile);
P4Cms_Log::log(
"Rebuild Search Index: BEGIN; pid=". getmypid(),
P4Cms_Log::DEBUG
);
// put the current task in the session
$_SESSION['searchMaintenanceTask'] = 'rebuild';
// put the status file's filename in the session
$this->_statusFile = tempnam('/tmp', 'p4cms-search-rebuild.'. getmypid() .'.');
$_SESSION['searchMaintenanceStatusFile'] = $this->_statusFile;
$this->_writeStatusFile(
array(
'action' => 'rebuild',
'label' => 'rebuilding search index',
'message' => 'Start rebuilding search index.',
'time' => time(),
'done' => false,
)
);
// close the session but continue running, since index rebuilt may
// take longer than browser timeout
$this->getHelper('browserDisconnect')->disconnect('status', 60 * 24);
// clear the current index, if any
// and create a new one
$index = Search_Module::factory('temp-index');
// publish the search index rebuild topic, expects subscribers to
// return Zend_Paginator instances
$feedbacks = P4Cms_PubSub::publish('p4cms.search.index.rebuild');
$entryCount = 0;
// get the total number of content entries
foreach ($feedbacks as $feedback) {
if ($feedback instanceof Zend_Paginator) {
$entryCount += $feedback->getTotalItemCount();
}
}
// start to rebuild the search index
$count = 0;
foreach ($feedbacks as $feedback) {
// if the feedback is not a paginator as expected, skip it
if (!$feedback instanceof Zend_Paginator) {
continue;
}
$feedback->setItemCountPerPage(self::REBUILD_BATCH_SIZE);
// for each page, get the items and index them
for ($i = 1; $i <= $feedback->count(); $i++) {
// set the current page
$feedback->setCurrentPageNumber($i);
// if there is no items in the current page, nothing to do
if ($feedback->getCurrentItemCount() == 0) {
continue;
}
$itemCountPerPage = $feedback->getCurrentItemCount();
// get a batch of entries
$this->_writeStatusFile(
array(
'action' => 'rebuild',
'label' => 'fetching entries',
'message' => "Fetching the $i batch of $entryCount existing entries...",
'time' => time(),
'done' => false,
)
);
// get the items in the current page
$items = $feedback->getCurrentItems();
// update the status
$this->_writeStatusFile(
array(
'action' => 'rebuild',
'label' => 'rebuilding',
'message' => "Start rebuilding from the $i batch of $entryCount existing entries.",
'time' => time(),
'done' => false,
)
);
foreach ($items as $item) {
if (!$item instanceof Zend_Search_Lucene_Document) {
if (method_exists($item, 'toLuceneDocument')) {
try {
$item = $item->toLuceneDocument();
} catch (Zend_Filter_Exception $e) {
P4Cms_Log::logException(
'Failed converting content to Lucene document.',
$e
);
continue;
} catch (Zend_Search_Lucene_Exception $e) {
P4Cms_Log::logException(
'Failed converting content to Lucene document.',
$e
);
continue;
}
} else {
continue;
}
}
$index->addDocument($item);
$count++;
// update the status
$this->_writeStatusFile(
array(
'action' => 'rebuild',
'label' => 'indexing content',
'message' => "Indexing content no. $count of $entryCount.",
'count' => $count,
'total' => $entryCount,
'time' => time(),
'done' => false,
)
);
}
}
}
// optimize the index after it's rebuilt
$this->_writeStatusFile(
array(
'action' => 'optimize',
'label' => 'optimizing search index',
'index' => 'temp-index',
'message' => 'Optimizing the search index after rebuild...',
'time' => time(),
'done' => false,
)
);
$index->optimize();
$this->_writeStatusFile(
array(
'action' => 'optimize',
'label' => 'optimizing search index',
'index' => 'temp-index',
'message' => "Done. Search Index has been rebuilt.",
'time' => time(),
'done' => true
)
);
Search_Module::clearSearchInstances();
$this->_setActiveSearchIndex('temp-index');
unlink($maitenanceLockFile);
}
| Search_ManageController::statusAction | ( | ) |
Provide a status update in Json format.
{
// enforce permissions.
$this->acl->check('search', 'manage');
$statusFile = P4Cms_Site::fetchActive()->getDataPath()
. '/' . $this->_maintenanceStatusFile;
if (!file_exists($statusFile) ) {
$status = array(
'message' => 'Search Index maintenance task completed',
'done' => true
);
$this->view->status = $status;
return;
}
$status = $this->_readStatusFile();
// for optimize, get the progress and merge it to the status file contents
if (isset($status['action']) &&
($status['action'] == 'optimize') &&
!$status['done']) {
if (isset($status['index'])) {
$status = array_merge(
$status,
$this->_getoptimizeProgress($status['index'])
);
} else {
$status = array_merge($status, $this->_getoptimizeProgress());
}
}
if (!array_key_exists('searchMaintenanceTask', $_SESSION)) {
$status['message'] = "A Search Index '" . ucfirst($status['action'])
. "' operation is currently running. Its status and progress is below -- "
. $status['message'];
}
$this->contextSwitch->initContext('json');
$this->view->status = $status;
}
Search_ManageController::$_activeIndexPath = 'search-index' [protected] |
Search_ManageController::$_maintenanceLockFile = 'search.maintenance.lock.file' [protected] |
Search_ManageController::$_maintenanceStatusFile = 'search.maintenance.status.file' [protected] |
Search_ManageController::$_statusFile = null [protected] |
| Search_ManageController::$contexts |
array(
'status' => array('json' => array('POST', 'GET'))
)
| const Search_ManageController::REBUILD_BATCH_SIZE = 100 |