mirror of
https://github.com/alchemy-fr/Phraseanet.git
synced 2025-10-11 20:13:28 +00:00
Add Form for ElasticSearch Configuration.
Also changed type from array to Options object.
This commit is contained in:
@@ -10,37 +10,62 @@
|
|||||||
|
|
||||||
namespace Alchemy\Phrasea\Controller\Admin;
|
namespace Alchemy\Phrasea\Controller\Admin;
|
||||||
|
|
||||||
use Alchemy\Phrasea\SearchEngine\ConfigurationPanelInterface;
|
use Alchemy\Phrasea\Controller\Controller;
|
||||||
|
use Alchemy\Phrasea\SearchEngine\Elastic\ElasticSearchSettingFormType;
|
||||||
|
use Alchemy\Phrasea\SearchEngine\Elastic\GlobalElasticOptions;
|
||||||
|
use Symfony\Component\Form\FormInterface;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
class SearchEngineController
|
class SearchEngineController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* @var ConfigurationPanelInterface
|
|
||||||
*/
|
|
||||||
private $configurationPanel;
|
|
||||||
|
|
||||||
public function __construct(ConfigurationPanelInterface $configurationPanel)
|
|
||||||
{
|
|
||||||
$this->configurationPanel = $configurationPanel;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @return Response
|
* @return Response
|
||||||
*/
|
*/
|
||||||
public function getConfigurationPanelAction(Request $request)
|
public function formConfigurationPanelAction(Request $request)
|
||||||
{
|
{
|
||||||
return $this->configurationPanel->get($request);
|
$options = $this->getElasticSearchOptions();
|
||||||
|
$form = $this->getConfigurationForm($options);
|
||||||
|
|
||||||
|
$form->handleRequest($request);
|
||||||
|
|
||||||
|
if ($form->isValid()) {
|
||||||
|
$this->saveElasticSearchOptions($form->getData());
|
||||||
|
|
||||||
|
return $this->app->redirectPath('admin_searchengine_form');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('admin/search-engine/elastic-search.html.twig', [
|
||||||
|
'form' => $form->createView(),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
* @return GlobalElasticOptions
|
||||||
* @return Response
|
|
||||||
*/
|
*/
|
||||||
public function postConfigurationPanelAction(Request $request)
|
private function getElasticSearchOptions()
|
||||||
{
|
{
|
||||||
return $this->configurationPanel->post($request);
|
return $this->app['elasticsearch.options'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param GlobalElasticOptions $configuration
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function saveElasticSearchOptions(GlobalElasticOptions $configuration)
|
||||||
|
{
|
||||||
|
$this->getConf()->set(['main', 'search-engine', 'options'], $configuration->toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param GlobalElasticOptions $options
|
||||||
|
* @return FormInterface
|
||||||
|
*/
|
||||||
|
private function getConfigurationForm(GlobalElasticOptions $options)
|
||||||
|
{
|
||||||
|
return $this->app->form(new ElasticSearchSettingFormType(), $options, [
|
||||||
|
'action' => $this->app->url('admin_searchengine_form'),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -356,7 +356,7 @@ class V1Controller extends Controller
|
|||||||
'engine' => [
|
'engine' => [
|
||||||
'type' => $searchEngine->getName(),
|
'type' => $searchEngine->getName(),
|
||||||
'status' => $SEStatus,
|
'status' => $SEStatus,
|
||||||
'configuration' => $searchEngine->getConfigurationPanel()->getConfiguration(),
|
'configuration' => $conf->get(['main', 'searchengine', 'options']),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'binary' => [
|
'binary' => [
|
||||||
|
@@ -9,7 +9,6 @@
|
|||||||
*/
|
*/
|
||||||
namespace Alchemy\Phrasea\Controller\Prod;
|
namespace Alchemy\Phrasea\Controller\Prod;
|
||||||
|
|
||||||
use Alchemy\Phrasea\Application;
|
|
||||||
use Alchemy\Phrasea\Controller\Controller;
|
use Alchemy\Phrasea\Controller\Controller;
|
||||||
use Alchemy\Phrasea\Controller\RecordsRequest;
|
use Alchemy\Phrasea\Controller\RecordsRequest;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
@@ -24,9 +24,7 @@ class SearchEngine implements ControllerProviderInterface, ServiceProviderInterf
|
|||||||
public function register(Application $app)
|
public function register(Application $app)
|
||||||
{
|
{
|
||||||
$app['controller.admin.search-engine'] = $app->share(function (PhraseaApplication $app) {
|
$app['controller.admin.search-engine'] = $app->share(function (PhraseaApplication $app) {
|
||||||
/** @var SearchEngineInterface $searchEngine */
|
return new SearchEngineController($app);
|
||||||
$searchEngine = $app['search_engine'];
|
|
||||||
return new SearchEngineController($searchEngine->getConfigurationPanel());
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,11 +37,9 @@ class SearchEngine implements ControllerProviderInterface, ServiceProviderInterf
|
|||||||
/** @var ControllerCollection $controllers */
|
/** @var ControllerCollection $controllers */
|
||||||
$controllers = $app['controllers_factory'];
|
$controllers = $app['controllers_factory'];
|
||||||
|
|
||||||
$controllers->get('/', 'controller.admin.search-engine:getConfigurationPanelAction')
|
$controllers->match('/', 'controller.admin.search-engine:formConfigurationPanelAction')
|
||||||
->bind('admin_searchengine_get');
|
->method('GET|POST')
|
||||||
|
->bind('admin_searchengine_form');
|
||||||
$controllers->post('/', 'controller.admin.search-engine:postConfigurationPanelAction')
|
|
||||||
->bind('admin_searchengine_post');
|
|
||||||
|
|
||||||
return $controllers;
|
return $controllers;
|
||||||
}
|
}
|
||||||
|
@@ -12,6 +12,7 @@
|
|||||||
namespace Alchemy\Phrasea\Core\Provider;
|
namespace Alchemy\Phrasea\Core\Provider;
|
||||||
|
|
||||||
use Alchemy\Phrasea\Controller\LazyLocator;
|
use Alchemy\Phrasea\Controller\LazyLocator;
|
||||||
|
use Alchemy\Phrasea\SearchEngine\Elastic\GlobalElasticOptions;
|
||||||
use Alchemy\Phrasea\SearchEngine\SearchEngineLogger;
|
use Alchemy\Phrasea\SearchEngine\SearchEngineLogger;
|
||||||
use Alchemy\Phrasea\Exception\InvalidArgumentException;
|
use Alchemy\Phrasea\Exception\InvalidArgumentException;
|
||||||
use Alchemy\Phrasea\SearchEngine\SearchEngineInterface;
|
use Alchemy\Phrasea\SearchEngine\SearchEngineInterface;
|
||||||
@@ -53,11 +54,14 @@ class SearchEngineServiceProvider implements ServiceProviderInterface
|
|||||||
if ($type !== SearchEngineInterface::TYPE_ELASTICSEARCH) {
|
if ($type !== SearchEngineInterface::TYPE_ELASTICSEARCH) {
|
||||||
throw new InvalidArgumentException(sprintf('Invalid search engine type "%s".', $type));
|
throw new InvalidArgumentException(sprintf('Invalid search engine type "%s".', $type));
|
||||||
}
|
}
|
||||||
|
/** @var GlobalElasticOptions $options */
|
||||||
|
$options = $app['elasticsearch.options'];
|
||||||
|
|
||||||
return new ElasticSearchEngine(
|
return new ElasticSearchEngine(
|
||||||
$app,
|
$app,
|
||||||
$app['search_engine.structure'],
|
$app['search_engine.structure'],
|
||||||
$app['elasticsearch.client'],
|
$app['elasticsearch.client'],
|
||||||
$app['elasticsearch.options']['index'],
|
$options->getIndexName(),
|
||||||
$app['locales.available'],
|
$app['locales.available'],
|
||||||
$app['elasticsearch.facets_response.factory']
|
$app['elasticsearch.facets_response.factory']
|
||||||
);
|
);
|
||||||
@@ -132,11 +136,13 @@ class SearchEngineServiceProvider implements ServiceProviderInterface
|
|||||||
/* Low-level elasticsearch services */
|
/* Low-level elasticsearch services */
|
||||||
|
|
||||||
$app['elasticsearch.client'] = $app->share(function($app) {
|
$app['elasticsearch.client'] = $app->share(function($app) {
|
||||||
|
/** @var GlobalElasticOptions $options */
|
||||||
$options = $app['elasticsearch.options'];
|
$options = $app['elasticsearch.options'];
|
||||||
$clientParams = ['hosts' => [sprintf('%s:%s', $options['host'], $options['port'])]];
|
$clientParams = ['hosts' => [sprintf('%s:%s', $options->getHost(), $options->getPort())]];
|
||||||
|
|
||||||
// Create file logger for debug
|
// Create file logger for debug
|
||||||
if ($app['debug']) {
|
if ($app['debug']) {
|
||||||
|
/** @var Logger $logger */
|
||||||
$logger = new $app['monolog.logger.class']('search logger');
|
$logger = new $app['monolog.logger.class']('search logger');
|
||||||
$logger->pushHandler(new RotatingFileHandler($app['log.path'].DIRECTORY_SEPARATOR.'elasticsearch.log', 2), Logger::INFO);
|
$logger->pushHandler(new RotatingFileHandler($app['log.path'].DIRECTORY_SEPARATOR.'elasticsearch.log', 2), Logger::INFO);
|
||||||
|
|
||||||
@@ -148,22 +154,16 @@ class SearchEngineServiceProvider implements ServiceProviderInterface
|
|||||||
});
|
});
|
||||||
|
|
||||||
$app['elasticsearch.options'] = $app->share(function($app) {
|
$app['elasticsearch.options'] = $app->share(function($app) {
|
||||||
$options = $app['conf']->get(['main', 'search-engine', 'options'], []);
|
$options = GlobalElasticOptions::fromArray($app['conf']->get(['main', 'search-engine', 'options'], []));
|
||||||
|
|
||||||
$indexName = sprintf('phraseanet_%s', str_replace(
|
if (empty($options->getIndexName())) {
|
||||||
|
$options->setIndexName(strtolower(sprintf('phraseanet_%s', str_replace(
|
||||||
array('/', '.'), array('', ''),
|
array('/', '.'), array('', ''),
|
||||||
$app['conf']->get(['main', 'key'])
|
$app['conf']->get(['main', 'key'])
|
||||||
));
|
))));
|
||||||
|
}
|
||||||
|
|
||||||
$defaults = [
|
return $options;
|
||||||
'host' => '127.0.0.1',
|
|
||||||
'port' => 9200,
|
|
||||||
'index' => strtolower($indexName),
|
|
||||||
'shards' => 3,
|
|
||||||
'replicas' => 0
|
|
||||||
];
|
|
||||||
|
|
||||||
return array_replace($defaults, $options);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -175,7 +175,7 @@ class SearchEngineServiceProvider implements ServiceProviderInterface
|
|||||||
$logger->pushHandler(new \Monolog\Handler\ErrorLogHandler());
|
$logger->pushHandler(new \Monolog\Handler\ErrorLogHandler());
|
||||||
return new Thesaurus(
|
return new Thesaurus(
|
||||||
$app['elasticsearch.client'],
|
$app['elasticsearch.client'],
|
||||||
$app['elasticsearch.options']['index'],
|
$app['elasticsearch.options'],
|
||||||
$logger
|
$logger
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@@ -1,57 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Phraseanet
|
|
||||||
*
|
|
||||||
* (c) 2005-2015 Alchemy
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Alchemy\Phrasea\SearchEngine;
|
|
||||||
|
|
||||||
use Alchemy\Phrasea\Core\Configuration\PropertyAccess;
|
|
||||||
|
|
||||||
abstract class AbstractConfigurationPanel implements ConfigurationPanelInterface
|
|
||||||
{
|
|
||||||
/** @var PropertyAccess */
|
|
||||||
protected $conf;
|
|
||||||
|
|
||||||
public function __construct(PropertyAccess $conf)
|
|
||||||
{
|
|
||||||
$this->conf = $conf;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param \databox[] $databoxes
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getAvailableDateFields(array $databoxes)
|
|
||||||
{
|
|
||||||
$date_fields = [];
|
|
||||||
|
|
||||||
foreach ($databoxes as $databox) {
|
|
||||||
/** @var \databox_field $field */
|
|
||||||
foreach ($databox->get_meta_structure() as $field) {
|
|
||||||
if ($field->get_type() !== \databox_field::TYPE_DATE) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$date_fields[] = $field->get_name();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $date_fields;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function saveConfiguration(array $configuration)
|
|
||||||
{
|
|
||||||
$this->conf->set(['main', 'search-engine', 'options'], $configuration);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,65 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Phraseanet
|
|
||||||
*
|
|
||||||
* (c) 2005-2015 Alchemy
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Alchemy\Phrasea\SearchEngine;
|
|
||||||
|
|
||||||
use Alchemy\Phrasea\Application;
|
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
|
||||||
|
|
||||||
interface ConfigurationPanelInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Handles the GET request to the configuration panel
|
|
||||||
*
|
|
||||||
* @param Request $request
|
|
||||||
* @return Response
|
|
||||||
*/
|
|
||||||
public function get(Request $request);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the POST request to the configuration panel
|
|
||||||
*
|
|
||||||
* @param Request $request
|
|
||||||
* @return Response
|
|
||||||
*/
|
|
||||||
public function post(Request $request);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the associated search engine name
|
|
||||||
*
|
|
||||||
* @return string The name
|
|
||||||
*/
|
|
||||||
public function getName();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the configuration of the search engine
|
|
||||||
*
|
|
||||||
* @return array The configuration
|
|
||||||
*/
|
|
||||||
public function getConfiguration();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves the search engine configuration
|
|
||||||
*
|
|
||||||
* @param array $configuration
|
|
||||||
* @return ConfigurationPanelInterface
|
|
||||||
*/
|
|
||||||
public function saveConfiguration(array $configuration);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the names of the date fields
|
|
||||||
*
|
|
||||||
* @param \databox[] $databoxes
|
|
||||||
* @return array An array of date fields names
|
|
||||||
*/
|
|
||||||
public function getAvailableDateFields(array $databoxes);
|
|
||||||
}
|
|
@@ -1,68 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Phraseanet
|
|
||||||
*
|
|
||||||
* (c) 2005-2014 Alchemy
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Alchemy\Phrasea\SearchEngine\Elastic;
|
|
||||||
|
|
||||||
use Alchemy\Phrasea\Application;
|
|
||||||
use Alchemy\Phrasea\Core\Configuration\PropertyAccess;
|
|
||||||
use Alchemy\Phrasea\SearchEngine\AbstractConfigurationPanel;
|
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
|
||||||
|
|
||||||
class ConfigurationPanel extends AbstractConfigurationPanel
|
|
||||||
{
|
|
||||||
/** @var Application */
|
|
||||||
private $app;
|
|
||||||
|
|
||||||
public function __construct(Application $app, PropertyAccess $conf)
|
|
||||||
{
|
|
||||||
parent::__construct($conf);
|
|
||||||
$this->app = $app;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function getName()
|
|
||||||
{
|
|
||||||
return 'elastic-search-engine';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function get(Request $request)
|
|
||||||
{
|
|
||||||
return $this->app['twig']->render('admin/search-engine/elastic-search.html.twig', ['configuration' => $this->getConfiguration()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function post(Request $request)
|
|
||||||
{
|
|
||||||
$configuration = $this->getConfiguration();
|
|
||||||
|
|
||||||
$configuration['host'] = $request->request->get('host');
|
|
||||||
$configuration['port'] = $request->request->get('port');
|
|
||||||
|
|
||||||
$this->saveConfiguration($configuration);
|
|
||||||
|
|
||||||
return $this->app->redirectPath('admin_searchengine_get');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function getConfiguration()
|
|
||||||
{
|
|
||||||
return $this->conf->get(['main', 'search-engine', 'options'], []);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -100,18 +100,6 @@ class ElasticSearchEngine implements SearchEngineInterface
|
|||||||
return $ret;
|
return $ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function getConfigurationPanel()
|
|
||||||
{
|
|
||||||
if (!$this->configurationPanel) {
|
|
||||||
$this->configurationPanel = new ConfigurationPanel($this->app, $this->app['conf']);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->configurationPanel;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
@@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* This file is part of Phraseanet
|
||||||
|
*
|
||||||
|
* (c) 2005-2015 Alchemy
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
namespace Alchemy\Phrasea\SearchEngine\Elastic;
|
||||||
|
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||||
|
use Symfony\Component\Validator\Constraints\Range;
|
||||||
|
|
||||||
|
class ElasticSearchSettingFormType extends AbstractType
|
||||||
|
{
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
|
{
|
||||||
|
$builder
|
||||||
|
->add('host', 'text', [
|
||||||
|
'label' => 'ElasticSearch server host',
|
||||||
|
])
|
||||||
|
->add('port', 'integer', [
|
||||||
|
'label' => 'ElasticSearch service port',
|
||||||
|
'constraints' => new Range(['min' => 1, 'max' => 65535]),
|
||||||
|
])
|
||||||
|
->add('indexName', 'text', [
|
||||||
|
'label' => 'ElasticSearch index name',
|
||||||
|
'constraints' => new NotBlank(),
|
||||||
|
])
|
||||||
|
->add('shards', 'integer', [
|
||||||
|
'label' => 'Number of shards',
|
||||||
|
'constraints' => new Range(['min' => 1]),
|
||||||
|
])
|
||||||
|
->add('replicas', 'integer', [
|
||||||
|
'label' => 'Number of replicas',
|
||||||
|
'constraints' => new Range(['min' => 0]),
|
||||||
|
])
|
||||||
|
->add('minScore', 'integer', [
|
||||||
|
'label' => 'Thesaurus Min score',
|
||||||
|
'constraints' => new Range(['min' => 0]),
|
||||||
|
])
|
||||||
|
->add('save', 'submit')
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return 'elastic_settings';
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,165 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* This file is part of Phraseanet
|
||||||
|
*
|
||||||
|
* (c) 2005-2015 Alchemy
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
namespace Alchemy\Phrasea\SearchEngine\Elastic;
|
||||||
|
|
||||||
|
class GlobalElasticOptions
|
||||||
|
{
|
||||||
|
/** @var string */
|
||||||
|
private $host;
|
||||||
|
/** @var int */
|
||||||
|
private $port;
|
||||||
|
/** @var string */
|
||||||
|
private $indexName;
|
||||||
|
/** @var int */
|
||||||
|
private $shards;
|
||||||
|
/** @var int */
|
||||||
|
private $replicas;
|
||||||
|
/** @var int */
|
||||||
|
private $minScore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory method to hydrate an instance from serialized options
|
||||||
|
*
|
||||||
|
* @param array $options
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public static function fromArray(array $options)
|
||||||
|
{
|
||||||
|
$options = array_replace([
|
||||||
|
'host' => '127.0.0.1',
|
||||||
|
'port' => 9200,
|
||||||
|
'index' => '',
|
||||||
|
'shards' => 3,
|
||||||
|
'replicas' => 0,
|
||||||
|
'minScore' => 4,
|
||||||
|
], $options);
|
||||||
|
|
||||||
|
$self = new self();
|
||||||
|
$self->setHost($options['host']);
|
||||||
|
$self->setPort($options['port']);
|
||||||
|
$self->setIndexName($options['index']);
|
||||||
|
$self->setShards($options['shards']);
|
||||||
|
$self->setReplicas($options['replicas']);
|
||||||
|
$self->setMinScore($options['minScore']);
|
||||||
|
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function toArray()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'host' => $this->host,
|
||||||
|
'port' => $this->port,
|
||||||
|
'index' => $this->indexName,
|
||||||
|
'shards' => $this->shards,
|
||||||
|
'replicas' => $this->replicas,
|
||||||
|
'minScore' => $this->minScore,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $host
|
||||||
|
*/
|
||||||
|
public function setHost($host)
|
||||||
|
{
|
||||||
|
$this->host = $host;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getHost()
|
||||||
|
{
|
||||||
|
return $this->host;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $port
|
||||||
|
*/
|
||||||
|
public function setPort($port)
|
||||||
|
{
|
||||||
|
$this->port = (int)$port;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getPort()
|
||||||
|
{
|
||||||
|
return $this->port;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $minScore
|
||||||
|
*/
|
||||||
|
public function setMinScore($minScore)
|
||||||
|
{
|
||||||
|
$this->minScore = (int)$minScore;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getMinScore()
|
||||||
|
{
|
||||||
|
return $this->minScore;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $indexName
|
||||||
|
*/
|
||||||
|
public function setIndexName($indexName)
|
||||||
|
{
|
||||||
|
$this->indexName = $indexName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getIndexName()
|
||||||
|
{
|
||||||
|
return $this->indexName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $shards
|
||||||
|
*/
|
||||||
|
public function setShards($shards)
|
||||||
|
{
|
||||||
|
$this->shards = (int)$shards;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getShards()
|
||||||
|
{
|
||||||
|
return $this->shards;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $replicas
|
||||||
|
*/
|
||||||
|
public function setReplicas($replicas)
|
||||||
|
{
|
||||||
|
$this->replicas = (int)$replicas;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getReplicas()
|
||||||
|
{
|
||||||
|
return $this->replicas;
|
||||||
|
}
|
||||||
|
}
|
@@ -32,6 +32,7 @@ class Indexer
|
|||||||
|
|
||||||
/** @var \Elasticsearch\Client */
|
/** @var \Elasticsearch\Client */
|
||||||
private $client;
|
private $client;
|
||||||
|
/** @var GlobalElasticOptions */
|
||||||
private $options;
|
private $options;
|
||||||
private $appbox;
|
private $appbox;
|
||||||
/** @var LoggerInterface|null */
|
/** @var LoggerInterface|null */
|
||||||
@@ -48,7 +49,7 @@ class Indexer
|
|||||||
const DEFAULT_REFRESH_INTERVAL = '1s';
|
const DEFAULT_REFRESH_INTERVAL = '1s';
|
||||||
const REFRESH_INTERVAL_KEY = 'index.refresh_interval';
|
const REFRESH_INTERVAL_KEY = 'index.refresh_interval';
|
||||||
|
|
||||||
public function __construct(Client $client, array $options, TermIndexer $termIndexer, RecordIndexer $recordIndexer, appbox $appbox, LoggerInterface $logger = null)
|
public function __construct(Client $client, GlobalElasticOptions $options, TermIndexer $termIndexer, RecordIndexer $recordIndexer, appbox $appbox, LoggerInterface $logger = null)
|
||||||
{
|
{
|
||||||
$this->client = $client;
|
$this->client = $client;
|
||||||
$this->options = $options;
|
$this->options = $options;
|
||||||
@@ -64,9 +65,9 @@ class Indexer
|
|||||||
public function createIndex($withMapping = true)
|
public function createIndex($withMapping = true)
|
||||||
{
|
{
|
||||||
$params = array();
|
$params = array();
|
||||||
$params['index'] = $this->options['index'];
|
$params['index'] = $this->options->getIndexName();
|
||||||
$params['body']['settings']['number_of_shards'] = $this->options['shards'];
|
$params['body']['settings']['number_of_shards'] = $this->options->getShards();
|
||||||
$params['body']['settings']['number_of_replicas'] = $this->options['replicas'];
|
$params['body']['settings']['number_of_replicas'] = $this->options->getReplicas();
|
||||||
$params['body']['settings']['analysis'] = $this->getAnalysis();;
|
$params['body']['settings']['analysis'] = $this->getAnalysis();;
|
||||||
|
|
||||||
if ($withMapping) {
|
if ($withMapping) {
|
||||||
@@ -80,7 +81,7 @@ class Indexer
|
|||||||
public function updateMapping()
|
public function updateMapping()
|
||||||
{
|
{
|
||||||
$params = array();
|
$params = array();
|
||||||
$params['index'] = $this->options['index'];
|
$params['index'] = $this->options->getIndexName();
|
||||||
$params['type'] = RecordIndexer::TYPE_NAME;
|
$params['type'] = RecordIndexer::TYPE_NAME;
|
||||||
$params['body'][RecordIndexer::TYPE_NAME] = $this->recordIndexer->getMapping();
|
$params['body'][RecordIndexer::TYPE_NAME] = $this->recordIndexer->getMapping();
|
||||||
$params['body'][TermIndexer::TYPE_NAME] = $this->termIndexer->getMapping();
|
$params['body'][TermIndexer::TYPE_NAME] = $this->termIndexer->getMapping();
|
||||||
@@ -91,13 +92,13 @@ class Indexer
|
|||||||
|
|
||||||
public function deleteIndex()
|
public function deleteIndex()
|
||||||
{
|
{
|
||||||
$params = array('index' => $this->options['index']);
|
$params = array('index' => $this->options->getIndexName());
|
||||||
$this->client->indices()->delete($params);
|
$this->client->indices()->delete($params);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function indexExists()
|
public function indexExists()
|
||||||
{
|
{
|
||||||
$params = array('index' => $this->options['index']);
|
$params = array('index' => $this->options->getIndexName());
|
||||||
|
|
||||||
return $this->client->indices()->exists($params);
|
return $this->client->indices()->exists($params);
|
||||||
}
|
}
|
||||||
@@ -132,7 +133,7 @@ class Indexer
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Optimize index
|
// Optimize index
|
||||||
$params = array('index' => $this->options['index']);
|
$params = array('index' => $this->options->getIndexName());
|
||||||
$this->client->indices()->optimize($params);
|
$this->client->indices()->optimize($params);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -203,7 +204,7 @@ class Indexer
|
|||||||
try {
|
try {
|
||||||
// Prepare the bulk operation
|
// Prepare the bulk operation
|
||||||
$bulk = new BulkOperation($this->client, $this->logger);
|
$bulk = new BulkOperation($this->client, $this->logger);
|
||||||
$bulk->setDefaultIndex($this->options['index']);
|
$bulk->setDefaultIndex($this->options->getIndexName());
|
||||||
$bulk->setAutoFlushLimit(1000);
|
$bulk->setAutoFlushLimit(1000);
|
||||||
// Do the work
|
// Do the work
|
||||||
$work($bulk);
|
$work($bulk);
|
||||||
@@ -233,7 +234,7 @@ class Indexer
|
|||||||
|
|
||||||
private function getSetting($name)
|
private function getSetting($name)
|
||||||
{
|
{
|
||||||
$index = $this->options['index'];
|
$index = $this->options->getIndexName();
|
||||||
$params = array();
|
$params = array();
|
||||||
$params['index'] = $index;
|
$params['index'] = $index;
|
||||||
$params['name'] = $name;
|
$params['name'] = $name;
|
||||||
@@ -245,7 +246,7 @@ class Indexer
|
|||||||
|
|
||||||
private function setSetting($name, $value)
|
private function setSetting($name, $value)
|
||||||
{
|
{
|
||||||
$index = $this->options['index'];
|
$index = $this->options->getIndexName();
|
||||||
$params = array();
|
$params = array();
|
||||||
$params['index'] = $index;
|
$params['index'] = $index;
|
||||||
$params['body'][$name] = $value;
|
$params['body'][$name] = $value;
|
||||||
|
@@ -22,16 +22,17 @@ use Psr\Log\LoggerInterface;
|
|||||||
|
|
||||||
class Thesaurus
|
class Thesaurus
|
||||||
{
|
{
|
||||||
|
/** @var Client */
|
||||||
private $client;
|
private $client;
|
||||||
private $index;
|
/** @var GlobalElasticOptions */
|
||||||
|
private $options;
|
||||||
|
/** @var LoggerInterface */
|
||||||
private $logger;
|
private $logger;
|
||||||
|
|
||||||
const MIN_SCORE = 4;
|
public function __construct(Client $client, GlobalElasticOptions $options, LoggerInterface $logger)
|
||||||
|
|
||||||
public function __construct(Client $client, $index, LoggerInterface $logger)
|
|
||||||
{
|
{
|
||||||
$this->client = $client;
|
$this->client = $client;
|
||||||
$this->index = $index;
|
$this->options = $options;
|
||||||
$this->logger = $logger;
|
$this->logger = $logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,7 +137,7 @@ class Thesaurus
|
|||||||
|
|
||||||
// Search request
|
// Search request
|
||||||
$params = array();
|
$params = array();
|
||||||
$params['index'] = $this->index;
|
$params['index'] = $this->options->getIndexName();
|
||||||
$params['type'] = TermIndexer::TYPE_NAME;
|
$params['type'] = TermIndexer::TYPE_NAME;
|
||||||
$params['body']['query'] = $query;
|
$params['body']['query'] = $query;
|
||||||
$params['body']['aggs'] = $aggs;
|
$params['body']['aggs'] = $aggs;
|
||||||
@@ -144,7 +145,7 @@ class Thesaurus
|
|||||||
// inexact concepts.
|
// inexact concepts.
|
||||||
// We also need to disable TF/IDF on terms, and try to boost score only
|
// We also need to disable TF/IDF on terms, and try to boost score only
|
||||||
// when the search match nearly all tokens of term's value field.
|
// when the search match nearly all tokens of term's value field.
|
||||||
$params['body']['min_score'] = self::MIN_SCORE;
|
$params['body']['min_score'] = $this->options->getMinScore();
|
||||||
// No need to get any hits since we extract data from aggs
|
// No need to get any hits since we extract data from aggs
|
||||||
$params['body']['size'] = 0;
|
$params['body']['size'] = 0;
|
||||||
|
|
||||||
|
@@ -40,11 +40,6 @@ interface SearchEngineInterface
|
|||||||
*/
|
*/
|
||||||
public function getStatus();
|
public function getStatus();
|
||||||
|
|
||||||
/**
|
|
||||||
* @return ConfigurationPanelInterface
|
|
||||||
*/
|
|
||||||
public function getConfigurationPanel();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array an array of field names
|
* @return array an array of field names
|
||||||
*/
|
*/
|
||||||
|
@@ -408,7 +408,7 @@ class SearchEngineOptions
|
|||||||
}, $value);
|
}, $value);
|
||||||
}
|
}
|
||||||
if (in_array($key, ['collections', 'business_fields'])) {
|
if (in_array($key, ['collections', 'business_fields'])) {
|
||||||
$value = array_map(function ($collection) {
|
$value = array_map(function (\collection $collection) {
|
||||||
return $collection->get_base_id();
|
return $collection->get_base_id();
|
||||||
}, $value);
|
}, $value);
|
||||||
}
|
}
|
||||||
|
@@ -1,11 +1,3 @@
|
|||||||
<h1>{{ 'ElasticSearch configuration' | trans }}</h1>
|
<h1>{{ 'ElasticSearch configuration' | trans }}</h1>
|
||||||
|
|
||||||
<form method="post" action="{{ path('admin_searchengine_post') }}">
|
{{ form(form) }}
|
||||||
<div>{{ 'ElasticSearch connection configuration' | trans }}</div>
|
|
||||||
|
|
||||||
<div>{{ 'ElasticSearch server' | trans }}</div>
|
|
||||||
<input type="text" name="host" value="{{ configuration['host'] | default('localhost') }}"/>
|
|
||||||
<input type="text" name="port" value="{{ configuration['port'] | default('9200') }}"/>
|
|
||||||
|
|
||||||
<button type="submit" class="btn btn-warning" >{{ 'boutton::valider' | trans }}</button>
|
|
||||||
</form>
|
|
||||||
|
@@ -23,7 +23,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a target="right" href="{{ path('admin_searchengine_get') }}">
|
<a target="right" href="{{ path('admin_searchengine_form') }}">
|
||||||
<span>{{ 'SearchEngine settings' | trans }}</span>
|
<span>{{ 'SearchEngine settings' | trans }}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
@@ -1,43 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Alchemy\Tests\Phrasea\SearchEngine;
|
|
||||||
|
|
||||||
abstract class ConfigurationPanelAbstractTest extends \PhraseanetTestCase
|
|
||||||
{
|
|
||||||
|
|
||||||
abstract public function getPanel();
|
|
||||||
|
|
||||||
public function testGetName()
|
|
||||||
{
|
|
||||||
$this->assertInternalType('string', $this->getPanel()->getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetConfiguration()
|
|
||||||
{
|
|
||||||
$this->assertInternalType('array', $this->getPanel()->getConfiguration());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testSaveConfiguration()
|
|
||||||
{
|
|
||||||
$config = $this->getPanel()->getConfiguration();
|
|
||||||
$data = 'Yodelali' . mt_rand();
|
|
||||||
$config['test'] = $data;
|
|
||||||
$this->getPanel()->saveConfiguration($config);
|
|
||||||
|
|
||||||
$config = $this->getPanel()->getConfiguration();
|
|
||||||
$this->assertEquals($data, $config['test']);
|
|
||||||
unset($config['test']);
|
|
||||||
$this->getPanel()->saveConfiguration($config);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetAvailableDateFields()
|
|
||||||
{
|
|
||||||
$dateFields = $this->getPanel()->getAvailableDateFields(self::$DI['app']['phraseanet.appbox']->get_databoxes());
|
|
||||||
$this->assertInternalType('array', $dateFields);
|
|
||||||
|
|
||||||
foreach ($dateFields as $dateField) {
|
|
||||||
$this->assertInternalType('string', $dateField);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -556,11 +556,6 @@ abstract class SearchEngineAbstractTest extends \PhraseanetAuthenticatedTestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testConfigurationPanel()
|
|
||||||
{
|
|
||||||
$this->assertInstanceOf('\\Alchemy\\Phrasea\\SearchEngine\\ConfigurationPanelInterface', self::$searchEngine->getConfigurationPanel());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testStatus()
|
public function testStatus()
|
||||||
{
|
{
|
||||||
foreach (self::$searchEngine->getStatus() as $StatusKeyValue) {
|
foreach (self::$searchEngine->getStatus() as $StatusKeyValue) {
|
||||||
|
@@ -711,9 +711,6 @@ abstract class PhraseanetTestCase extends WebTestCase
|
|||||||
$mock->expects($this->any())
|
$mock->expects($this->any())
|
||||||
->method('createSubscriber')
|
->method('createSubscriber')
|
||||||
->will($this->returnValue($this->getMock('Symfony\Component\EventDispatcher\EventSubscriberInterface')));
|
->will($this->returnValue($this->getMock('Symfony\Component\EventDispatcher\EventSubscriberInterface')));
|
||||||
$mock->expects($this->any())
|
|
||||||
->method('getConfigurationPanel')
|
|
||||||
->will($this->returnValue($this->getMock('Alchemy\Phrasea\SearchEngine\ConfigurationPanelInterface')));
|
|
||||||
$mock->expects($this->any())
|
$mock->expects($this->any())
|
||||||
->method('getStatus')
|
->method('getStatus')
|
||||||
->will($this->returnValue([]));
|
->will($this->returnValue([]));
|
||||||
|
Reference in New Issue
Block a user