mirror of
https://github.com/salesagility/SuiteCRM.git
synced 2024-11-22 07:52:36 +00:00
333 lines
10 KiB
PHP
333 lines
10 KiB
PHP
<?php
|
|
/**
|
|
* SugarCRM Community Edition is a customer relationship management program developed by
|
|
* SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
|
|
*
|
|
* SuiteCRM is an extension to SugarCRM Community Edition developed by SalesAgility Ltd.
|
|
* Copyright (C) 2011 - 2018 SalesAgility Ltd.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it under
|
|
* the terms of the GNU Affero General Public License version 3 as published by the
|
|
* Free Software Foundation with the addition of the following permission added
|
|
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
|
|
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
|
|
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
|
|
*
|
|
* 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 Affero General Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License along with
|
|
* this program; if not, see http://www.gnu.org/licenses or write to the Free
|
|
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301 USA.
|
|
*
|
|
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
|
|
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
|
|
*
|
|
* The interactive user interfaces in modified source and object code versions
|
|
* of this program must display Appropriate Legal Notices, as required under
|
|
* Section 5 of the GNU Affero General Public License version 3.
|
|
*
|
|
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
|
* these Appropriate Legal Notices must retain the display of the "Powered by
|
|
* SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
|
|
* reasonably feasible for technical reasons, the Appropriate Legal Notices must
|
|
* display the words "Powered by SugarCRM" and "Supercharged by SuiteCRM".
|
|
*/
|
|
|
|
namespace SuiteCRM\Search\Index;
|
|
|
|
if (!defined('sugarEntry') || !sugarEntry) {
|
|
die('Not A Valid Entry Point');
|
|
}
|
|
|
|
use InvalidArgumentException;
|
|
use Monolog\Handler\StreamHandler;
|
|
use Monolog\Logger;
|
|
use ReflectionClass;
|
|
use SuiteCRM\Log\CliLoggerHandler;
|
|
use SuiteCRM\Log\SugarLoggerHandler;
|
|
use SuiteCRM\Search\Index\Documentify\AbstractDocumentifier;
|
|
use SuiteCRM\Search\Index\Documentify\JsonSerializerDocumentifier;
|
|
use SuiteCRM\Search\SearchWrapper;
|
|
|
|
/**
|
|
* This class defines common methods and fields for a search indexer.
|
|
*
|
|
* A search indexer is a component with the task of creating an index to improve search efficiency.
|
|
* This is usually achieved by creating a copy of the sql database in an external service.
|
|
*
|
|
* It also offers logging facilities on a separate file using Monolog, and also colored console output if configured.
|
|
*
|
|
* @see \SuiteCRM\Search\ElasticSearch\ElasticSearchIndexer
|
|
*/
|
|
#[\AllowDynamicProperties]
|
|
abstract class AbstractIndexer
|
|
{
|
|
/** @var bool when enabled only beans changed after the last indexing should be indexed */
|
|
protected $differentialIndexing = false;
|
|
/** @var AbstractDocumentifier determines how a bean is converted into a document */
|
|
protected $documentifier = null;
|
|
/** @var string[] The modules that have to be indexed */
|
|
protected $modulesToIndex = null;
|
|
/** @var Logger Monolog instance to log on a separate file */
|
|
protected $logger;
|
|
/** @var string where the log files are going to be stored */
|
|
protected $logFile = 'search_index.log';
|
|
|
|
public function __construct()
|
|
{
|
|
$this->documentifier = new JsonSerializerDocumentifier();
|
|
$this->modulesToIndex = SearchWrapper::getModules();
|
|
$this->setupLogger();
|
|
}
|
|
|
|
/**
|
|
* Returns the short name (class name, without namespace) of the current Indexer.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getIndexerName()
|
|
{
|
|
return $this->getObjectClassName($this);
|
|
}
|
|
|
|
/**
|
|
* Performs the indexing procedures for the whole database.
|
|
*
|
|
* All modules specified in `getModulesToIndex()` must be indexed.
|
|
* This method should adhere to the options set in the indexer, such as partial indexing.
|
|
*
|
|
* @see AbstractIndexer:getModulesToIndex
|
|
* @return void
|
|
*/
|
|
abstract public function index();
|
|
|
|
/**
|
|
* Indexes a single module.
|
|
*
|
|
* If `$differentialIndexingEnabled` is set to `false` all beans in that module must be indexed.
|
|
*
|
|
* If `$differentialIndexingEnabled` is set to `true`, it should only perform indexing on beans
|
|
* that have been created/modified/deleted after the last indexing run.
|
|
* Additionally, beans that have been removed must be removed from the index too.
|
|
*
|
|
* @param string $module the name of the module, e.g. Accounts, Contacts, etc.
|
|
*
|
|
* @return void
|
|
*/
|
|
abstract public function indexModule($module);
|
|
|
|
/**
|
|
* Indexes a single bean.
|
|
*
|
|
* @param \SugarBean $bean
|
|
*
|
|
* @return void
|
|
*/
|
|
abstract public function indexBean(\SugarBean $bean);
|
|
|
|
/**
|
|
* Indexes an array of SugarBeans.
|
|
*
|
|
* This should not take in account of the differential indexing.
|
|
*
|
|
* @param string $module name of the module, e.g. Accounts, Contacts, etc.
|
|
* @param \SugarBean[] $beans
|
|
*
|
|
* @return void
|
|
*/
|
|
abstract public function indexBeans($module, array $beans);
|
|
|
|
/**
|
|
* Removes a bean from the index.
|
|
*
|
|
* @param \SugarBean $bean
|
|
*
|
|
* @return void
|
|
*/
|
|
abstract public function removeBean(\SugarBean $bean);
|
|
|
|
/**
|
|
* Removes an array of beans from the index.
|
|
*
|
|
* @param array $beans
|
|
*
|
|
* @return void
|
|
*/
|
|
abstract public function removeBeans(array $beans);
|
|
|
|
/**
|
|
* Deletes all the records from the index.
|
|
*
|
|
* @param string $index
|
|
*
|
|
* @return void
|
|
*/
|
|
abstract public function removeIndex(string $index);
|
|
|
|
/**
|
|
* Returns whether the next indexing should be performed differentially or not.
|
|
*
|
|
* If it is set to `true`, the next indexing should only be performed on beans
|
|
* that have been created/modified/deleted after the last indexing run.
|
|
* Additionally, beans that have been removed must be removed from the index too.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function isDifferentialIndexing()
|
|
{
|
|
return $this->differentialIndexing;
|
|
}
|
|
|
|
/**
|
|
* Sets whether the next indexing should be performed differentially or not.
|
|
*
|
|
* @param bool $differentialIndexing
|
|
*
|
|
* @see isDifferentialIndexing()
|
|
*/
|
|
public function setDifferentialIndexing($differentialIndexing)
|
|
{
|
|
$this->differentialIndexing = (bool) $differentialIndexing;
|
|
}
|
|
|
|
/**
|
|
* Returns the currently set Documentifier.
|
|
*
|
|
* @return AbstractDocumentifier
|
|
*/
|
|
public function getDocumentifier()
|
|
{
|
|
return $this->documentifier;
|
|
}
|
|
|
|
/**
|
|
* Sets the documentifier to use for the future index runs.
|
|
*
|
|
* The documentifier converts a SugarBean into a index-friendly document.
|
|
*
|
|
* @param AbstractDocumentifier $documentifier
|
|
*/
|
|
public function setDocumentifier(AbstractDocumentifier $documentifier)
|
|
{
|
|
$this->documentifier = $documentifier;
|
|
}
|
|
|
|
/**
|
|
* Returns the short (not fully qualified) name of the selected documentifier, i.e. the class name.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getDocumentifierName()
|
|
{
|
|
return $this->getObjectClassName($this->documentifier);
|
|
}
|
|
|
|
/**
|
|
* Returns the modules that have to be indexed.
|
|
*
|
|
* This can be overridden in subclasses to index different modules.
|
|
*
|
|
* @return string[]
|
|
*/
|
|
public function getModulesToIndex()
|
|
{
|
|
return $this->modulesToIndex;
|
|
}
|
|
|
|
/**
|
|
* Overrides the list of modules that have to be indexed for the next indexing runs.
|
|
*
|
|
* @param string[] $modules
|
|
*/
|
|
public function setModulesToIndex(array $modules)
|
|
{
|
|
if ($modules === null) {
|
|
throw new InvalidArgumentException('Modules cannot be `null`.');
|
|
}
|
|
|
|
if (isset($this->logger)) {
|
|
$this->logger->debug('Modules have been set to ' . json_encode($modules, JSON_THROW_ON_ERROR));
|
|
}
|
|
|
|
$this->modulesToIndex = $modules;
|
|
}
|
|
|
|
/**
|
|
* Adds one or more module to index for the next indexing runs.
|
|
*
|
|
* @param string|string[] $modules
|
|
*/
|
|
public function addModulesToIndex($modules)
|
|
{
|
|
if (is_array($modules)) {
|
|
$this->modulesToIndex = array_merge($this->modulesToIndex, $modules);
|
|
return;
|
|
}
|
|
|
|
if (is_string($modules)) {
|
|
$this->modulesToIndex[] = $modules;
|
|
return;
|
|
}
|
|
|
|
throw new InvalidArgumentException("Wrong type provided to AddModulesToIndex");
|
|
}
|
|
|
|
/**
|
|
* Retrieves the Monolog instance. This can be used to provide additional logging in the Indexer channel.
|
|
*
|
|
* @return Logger
|
|
*/
|
|
public function getLogger()
|
|
{
|
|
return $this->logger;
|
|
}
|
|
|
|
/**
|
|
* Sets up the internal logger.
|
|
*/
|
|
protected function setupLogger()
|
|
{
|
|
$this->logger = new Logger($this->getIndexerName());
|
|
|
|
// Set up SugarLog handler (this will forward messages to the default logging)
|
|
$this->logger->pushHandler(new SugarLoggerHandler());
|
|
|
|
// Set up Monolog logfile logger
|
|
try {
|
|
$this->logger->pushHandler(new StreamHandler($this->logFile));
|
|
} catch (\Exception $exception) {
|
|
$this->logger->error('Failed to create indexer log stream handler.');
|
|
$this->logger->error($exception);
|
|
}
|
|
|
|
// Set up Monolog CLI handler
|
|
try {
|
|
$this->logger->pushHandler(new CliLoggerHandler());
|
|
} catch (\Exception $exception) {
|
|
$this->logger->error('Failed to create CLI logger handler.');
|
|
$this->logger->error($exception);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Method to retrieve the (short) class name of an object.
|
|
*
|
|
* @param object $obj
|
|
*
|
|
* @return string
|
|
*/
|
|
private function getObjectClassName($obj)
|
|
{
|
|
try {
|
|
$reflect = new ReflectionClass($obj);
|
|
return $reflect->getShortName();
|
|
} catch (\ReflectionException $exception) {
|
|
return get_class($obj);
|
|
}
|
|
}
|
|
}
|