From 2f02cef4b062e26751ebebc8574e5dc72261a423 Mon Sep 17 00:00:00 2001 From: gjacobjn <52928254+gjacobjn@users.noreply.github.com> Date: Thu, 6 Feb 2020 10:48:16 +0100 Subject: [PATCH 01/17] Update Edit.php --- lib/Alchemy/Phrasea/Helper/User/Edit.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Alchemy/Phrasea/Helper/User/Edit.php b/lib/Alchemy/Phrasea/Helper/User/Edit.php index 5c760a55de..2c7513fb3f 100644 --- a/lib/Alchemy/Phrasea/Helper/User/Edit.php +++ b/lib/Alchemy/Phrasea/Helper/User/Edit.php @@ -657,8 +657,8 @@ class Edit extends \Alchemy\Phrasea\Helper\Helper ->setEmail($parm['email']) ->setAddress($parm['address']) ->setZipCode($parm['zip']) - ->setActivity($parm['function']) - ->setJob($parm['activite']) + ->setActivity($parm['activite']) + ->setJob($parm['function']) ->setCompany($parm['company']) ->setPhone($parm['telephone']) ->setFax($parm['fax']); From 3c1c6c41f5ec3e6114fe24b891094e5f8a82e255 Mon Sep 17 00:00:00 2001 From: gjacobjn <52928254+gjacobjn@users.noreply.github.com> Date: Thu, 6 Feb 2020 14:17:51 +0100 Subject: [PATCH 02/17] Fix in template --- templates/web/account/account.html.twig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/web/account/account.html.twig b/templates/web/account/account.html.twig index 887b29ec03..8dea3e8726 100644 --- a/templates/web/account/account.html.twig +++ b/templates/web/account/account.html.twig @@ -107,7 +107,7 @@
- +

@@ -121,7 +121,7 @@
- +

From b18ef096615056190e9ce2c0f40c54715aa6928e Mon Sep 17 00:00:00 2001 From: gjacobjn <52928254+gjacobjn@users.noreply.github.com> Date: Fri, 7 Feb 2020 17:17:54 +0100 Subject: [PATCH 03/17] Update AccountController.php --- lib/Alchemy/Phrasea/Controller/Root/AccountController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Alchemy/Phrasea/Controller/Root/AccountController.php b/lib/Alchemy/Phrasea/Controller/Root/AccountController.php index b4053a6ce1..6bf2d11f17 100644 --- a/lib/Alchemy/Phrasea/Controller/Root/AccountController.php +++ b/lib/Alchemy/Phrasea/Controller/Root/AccountController.php @@ -458,9 +458,9 @@ class AccountController extends Controller ->setZipCode($request->request->get("form_zip")) ->setPhone($request->request->get("form_phone")) ->setFax($request->request->get("form_fax")) - ->setJob($request->request->get("form_activity")) + ->setJob($request->request->get("form_function")) ->setCompany($request->request->get("form_company")) - ->setPosition($request->request->get("form_function")) + ->setPosition($request->request->get("form_activity")) ->setNotifications((Boolean) $request->request->get("mail_notifications")); $service->updateAccount($command); From a61e32b21f8f9be6daf37ddc6afebe17b6ffc291 Mon Sep 17 00:00:00 2001 From: Harrys Ravalomanana Date: Mon, 17 Feb 2020 18:48:44 +0400 Subject: [PATCH 04/17] upgrade of phraseanet-production-client --- package.json | 2 +- yarn.lock | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index f9103746d7..34944f5377 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "normalize-css": "^2.1.0", "npm": "^6.0.0", "npm-modernizr": "^2.8.3", - "phraseanet-production-client": "0.34.133-d", + "phraseanet-production-client": "0.34.135-d", "requirejs": "^2.3.5", "tinymce": "^4.0.28", "underscore": "^1.8.3", diff --git a/yarn.lock b/yarn.lock index a84b0bd496..c87e73c52c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7577,11 +7577,10 @@ phraseanet-common@^0.4.5-d: js-cookie "^2.1.0" pym.js "^1.3.1" - -phraseanet-production-client@0.34.133-d: - version "0.34.133-d" - resolved "https://registry.yarnpkg.com/phraseanet-production-client/-/phraseanet-production-client-0.34.133-d.tgz#41e10c29d839607364c52a0a824eaebc39f0c1a9" - integrity sha512-w4hKmjHtUsA94A0aLmp4O35hQuRM25i+HDmylmMKIT9wcn8Bm5ercZUEcbUQI7XNkBWj9V826XP0TcwHH6QExw== +phraseanet-production-client@0.34.135-d: + version "0.34.135-d" + resolved "https://registry.yarnpkg.com/phraseanet-production-client/-/phraseanet-production-client-0.34.135-d.tgz#fafbeecc0bd7aac7271742596576d5601b35d93e" + integrity sha512-mIORoFSZ4ZQgT0/1PhXsbwExrpTtNskdq/37/tuTrh5s/6SasFgvutSkUCvqr7EDyE2LNHZDAfcTCFLqo9T7DQ== dependencies: "@mapbox/mapbox-gl-language" "^0.9.2" "@turf/turf" "^5.1.6" From 2ccdcfed27969db0a5e19ad5fd7cddf6e46284b0 Mon Sep 17 00:00:00 2001 From: Jean-Yves Gaulier Date: Wed, 22 Jan 2020 12:26:34 +0100 Subject: [PATCH 05/17] PHRAS-2879_facet-order_4.1 save all facet settings in conf, order is preserved in admin/search-engine-settings. todo : ux to re-order facets todo : return facets into query results in this setting order. todo : migration from dbox settings (field struct) to conf ; remove field setting from admin --- .../Admin/SearchEngineController.php | 6 +- .../Elastic/ElasticsearchOptions.php | 73 ++++----- .../Elastic/ElasticsearchSettingsFormType.php | 141 ++++++++++++------ 3 files changed, 135 insertions(+), 85 deletions(-) diff --git a/lib/Alchemy/Phrasea/Controller/Admin/SearchEngineController.php b/lib/Alchemy/Phrasea/Controller/Admin/SearchEngineController.php index 8cf04e7839..8073027302 100644 --- a/lib/Alchemy/Phrasea/Controller/Admin/SearchEngineController.php +++ b/lib/Alchemy/Phrasea/Controller/Admin/SearchEngineController.php @@ -13,6 +13,7 @@ namespace Alchemy\Phrasea\Controller\Admin; use Alchemy\Phrasea\Controller\Controller; use Alchemy\Phrasea\SearchEngine\Elastic\ElasticsearchSettingsFormType; use Alchemy\Phrasea\SearchEngine\Elastic\ElasticsearchOptions; +use Alchemy\Phrasea\SearchEngine\Elastic\Structure\GlobalStructure; use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -85,7 +86,10 @@ class SearchEngineController extends Controller */ private function getConfigurationForm(ElasticsearchOptions $options) { - return $this->app->form(new ElasticsearchSettingsFormType(), $options, [ + /** @var GlobalStructure $g */ + $g = $this->app['search_engine.structure']; + + return $this->app->form(new ElasticsearchSettingsFormType($g, $options), $options, [ 'action' => $this->app->url('admin_searchengine_form'), ]); } diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php index 0532f85c30..b9b3d5a054 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php @@ -9,6 +9,9 @@ */ namespace Alchemy\Phrasea\SearchEngine\Elastic; +use igorw; + + class ElasticsearchOptions { const POPULATE_ORDER_RID = "RECORD_ID"; @@ -58,13 +61,8 @@ class ElasticsearchOptions 'populate_direction' => self::POPULATE_DIRECTION_DESC, 'activeTab' => null, ]; - - foreach(self::getAggregableTechnicalFields() as $k => $f) { - $defaultOptions[$k.'_limit'] = 0; - } $options = array_replace($defaultOptions, $options); - $self = new self(); $self->setHost($options['host']); $self->setPort($options['port']); @@ -76,11 +74,10 @@ class ElasticsearchOptions $self->setPopulateOrder($options['populate_order']); $self->setPopulateDirection($options['populate_direction']); $self->setActiveTab($options['activeTab']); - foreach(self::getAggregableTechnicalFields() as $k => $f) { - $self->setAggregableFieldLimit($k, $options[$k.'_limit']); + foreach($options['aggregates'] as $fieldname=>$attributes) { + $self->setAggregableField($fieldname, $attributes); } - return $self; } @@ -99,10 +96,11 @@ class ElasticsearchOptions 'highlight' => $this->highlight, 'populate_order' => $this->populateOrder, 'populate_direction' => $this->populateDirection, - 'activeTab' => $this->activeTab + 'activeTab' => $this->activeTab, + 'aggregates' => [] ]; - foreach(self::getAggregableTechnicalFields() as $k => $f) { - $ret[$k.'_limit'] = $this->getAggregableFieldLimit($k); + foreach($this->_customValues['aggregates'] as $fieldname=>$attributes) { + $ret['aggregates'][$fieldname] = $attributes; } return $ret; @@ -220,14 +218,19 @@ class ElasticsearchOptions $this->highlight = $highlight; } - public function setAggregableFieldLimit($key, $value) + public function setAggregableField($key, $attributes) { - $this->_customValues[$key.'_limit'] = $value; + $this->_customValues['aggregates'][$key] = $attributes; } - public function getAggregableFieldLimit($key) + public function getAggregableField($key) { - return $this->_customValues[$key.'_limit']; + return $this->_customValues['aggregates'][$key]; + } + + public function getAggregableFields() + { + return $this->_customValues['aggregates']; } public function getActiveTab() @@ -241,56 +244,56 @@ class ElasticsearchOptions public function __get($key) { - if(!array_key_exists($key, $this->_customValues)) { - $this->_customValues[$key] = 0; - } - return $this->_customValues[$key]; + $keys = explode(':', $key); + + return igorw\get_in($this->_customValues, $keys); } public function __set($key, $value) { - $this->_customValues[$key] = $value; + $keys = explode(':', $key); + $this->_customValues = igorw\assoc_in($this->_customValues, $keys, $value); } public static function getAggregableTechnicalFields() { return [ - 'base_aggregate' => [ + 'base' => [ 'type' => 'string', 'label' => 'prod::facet:base_label', 'field' => "database", 'esfield' => 'databox_name', 'query' => 'database:%s', ], - 'collection_aggregate' => [ + 'collection' => [ 'type' => 'string', 'label' => 'prod::facet:collection_label', 'field' => "collection", 'esfield' => 'collection_name', 'query' => 'collection:%s', ], - 'doctype_aggregate' => [ + 'doctype' => [ 'type' => 'string', 'label' => 'prod::facet:doctype_label', 'field' => "type", 'esfield' => 'type', 'query' => 'type:%s', ], - 'camera_model_aggregate' => [ + 'camera_model' => [ 'type' => 'string', 'label' => 'Camera Model', 'field' => "meta.CameraModel", 'esfield' => 'metadata_tags.CameraModel', 'query' => 'meta.CameraModel:%s', ], - 'iso_aggregate' => [ + 'iso' => [ 'type' => 'number', 'label' => 'ISO', 'field' => "meta.ISO", 'esfield' => 'metadata_tags.ISO', 'query' => 'meta.ISO=%s', ], - 'aperture_aggregate' => [ + 'aperture' => [ 'type' => 'number', 'label' => 'Aperture', 'field' => "meta.Aperture", @@ -300,7 +303,7 @@ class ElasticsearchOptions return round($value, 1); }, ], - 'shutterspeed_aggregate' => [ + 'shutterspeed' => [ 'type' => 'number', 'label' => 'Shutter speed', 'field' => "meta.ShutterSpeed", @@ -313,7 +316,7 @@ class ElasticsearchOptions return $value . ' s.'; }, ], - 'flashfired_aggregate' => [ + 'flashfired' => [ 'type' => 'boolean', 'label' => 'FlashFired', 'field' => "meta.FlashFired", @@ -327,49 +330,49 @@ class ElasticsearchOptions return array_key_exists($value, $map) ? $map[$value] : $value; }, ], - 'framerate_aggregate' => [ + 'framerate' => [ 'type' => 'number', 'label' => 'FrameRate', 'field' => "meta.FrameRate", 'esfield' => 'metadata_tags.FrameRate', 'query' => 'meta.FrameRate=%s', ], - 'audiosamplerate_aggregate' => [ + 'audiosamplerate' => [ 'type' => 'number', 'label' => 'Audio Samplerate', 'field' => "meta.AudioSamplerate", 'esfield' => 'metadata_tags.AudioSamplerate', 'query' => 'meta.AudioSamplerate=%s', ], - 'videocodec_aggregate' => [ + 'videocodec' => [ 'type' => 'string', 'label' => 'Video codec', 'field' => "meta.VideoCodec", 'esfield' => 'metadata_tags.VideoCodec', 'query' => 'meta.VideoCodec:%s', ], - 'audiocodec_aggregate' => [ + 'audiocodec' => [ 'type' => 'string', 'label' => 'Audio codec', 'field' => "meta.AudioCodec", 'esfield' => 'metadata_tags.AudioCodec', 'query' => 'meta.AudioCodec:%s', ], - 'orientation_aggregate' => [ + 'orientation' => [ 'type' => 'string', 'label' => 'Orientation', 'field' => "meta.Orientation", 'esfield' => 'metadata_tags.Orientation', 'query' => 'meta.Orientation=%s', ], - 'colorspace_aggregate' => [ + 'colorspace' => [ 'type' => 'string', 'label' => 'Colorspace', 'field' => "meta.ColorSpace", 'esfield' => 'metadata_tags.ColorSpace', 'query' => 'meta.ColorSpace:%s', ], - 'mimetype_aggregate' => [ + 'mimetype' => [ 'type' => 'string', 'label' => 'MimeType', 'field' => "meta.MimeType", diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchSettingsFormType.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchSettingsFormType.php index e1aaa3e378..985bda7f0e 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchSettingsFormType.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchSettingsFormType.php @@ -9,6 +9,7 @@ */ namespace Alchemy\Phrasea\SearchEngine\Elastic; +use Alchemy\Phrasea\SearchEngine\Elastic\Structure\GlobalStructure; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; @@ -17,6 +18,18 @@ use Symfony\Component\Validator\Constraints\Range; class ElasticsearchSettingsFormType extends AbstractType { + /** @var GlobalStructure */ + private $globalStructure; + + /** @var ElasticsearchOptions */ + private $esSettings; + + public function __construct(GlobalStructure $g, ElasticsearchOptions $settings) + { + $this->globalStructure = $g; + $this->esSettings = $settings; + } + public function buildForm(FormBuilderInterface $builder, array $options) { $builder @@ -56,59 +69,89 @@ class ElasticsearchSettingsFormType extends AbstractType ->add('minScore', 'integer', [ 'label' => 'Thesaurus Min score', 'constraints' => new Range(['min' => 0]), - ]); + ]) + ->add('highlight', 'checkbox', [ + 'label' => 'Activate highlight', + 'required' => false + ]) + // ->add('save', 'submit', [ + // 'attr' => ['class' => 'btn btn-primary'] + // ]) + ->add('esSettingFromIndex', 'button', [ + 'label' => 'Get setting form index', + 'attr' => [ + 'onClick' => 'esSettingFromIndex()', + 'class' => 'btn' + ] + ]) + ->add('dumpField', 'textarea', [ + 'label' => false, + 'required' => false, + 'mapped' => false, + 'attr' => ['class' => 'dumpfield hide'] + ]) + ->add('activeTab', 'hidden'); - foreach(ElasticsearchOptions::getAggregableTechnicalFields() as $k => $f) { - if(array_key_exists('choices', $f)) { - // choices[] : choice_key => choice_value - $choices = $f['choices']; - } - else { - $choices = [ - "10 values" => 10, - "20 values" => 20, - "50 values" => 50, - "100 values" => 100, - "all values" => -1 - ]; - } - // array_unshift($choices, "not aggregated"); // always as first choice - $choices = array_merge(["not aggregated" => 0], $choices); - $builder - ->add($k.'_limit', ChoiceType::class, [ - // 'label' => $f['label'],// . ' ' . 'aggregate limit', - 'choices_as_values' => true, - 'choices' => $choices, - 'attr' => [ - 'class' => 'aggregate' - ] - ]); + // keep aggregates in configuration order with this intermediate array + $aggs = []; + + // helper fct to add aggregate to a tmp list + $addAgg = function($k, $label, $help, $disabled=false, $choices=null) use (&$aggs) { + if(!$choices) { + $choices = [ + "10 values" => 10, + "50 values" => 50, + "100 values" => 100, + "all values" => -1 + ]; } + $choices = array_merge(["not aggregated" => 0], $choices); // add this option always as first choice + $aggs[$k] = [ // default value will be replaced by hardcoded tech fields & all databoxes fields + 'label' => $label, + 'choices_as_values' => true, + 'choices' => $choices, + 'attr' => [ + 'class' => 'aggregate' + ], + 'disabled' => $disabled, + 'help_message' => $help // todo : not displayed ? + ]; + }; - $builder - ->add('highlight', 'checkbox', [ - 'label' => 'Activate highlight', - 'required' => false - ]) -// ->add('save', 'submit', [ -// 'attr' => ['class' => 'btn btn-primary'] -// ]) - ->add('esSettingFromIndex', 'button', [ - 'label' => 'Get setting form index', - 'attr' => [ - 'onClick' => 'esSettingFromIndex()', - 'class' => 'btn' - ] - ]) - ->add('dumpField', 'textarea', [ - 'label' => false, - 'required' => false, - 'mapped' => false, - 'attr' => ['class' => 'dumpfield hide'] - ]) - ->add('activeTab', 'hidden'); + // all fields fron conf + foreach($this->esSettings->getAggregableFields() as $k=>$f) { + // default value will be replaced by hardcoded tech fields & all databoxes fields + $addAgg($k, "/?\\ " . $k, "This field does not exists in current databoxes.", true); + } + + // add or replace hardcoded tech fields + foreach(ElasticsearchOptions::getAggregableTechnicalFields() as $k => $f) { + $choices = array_key_exists('choices', $f) ? $f['choices'] : null; // a tech-field can publish it's own choices + $help = null; + $label = '#' . $k; + if(!array_key_exists('_'.$k, $aggs)) { + $label = "/!\\ " . $label; + $help = "New field, please confirm setting."; + } + $addAgg('_'.$k, $label, $help, false, $choices); + } + + // add or replace all databoxes fields (nb: new db field - unknown in conf - will be a the end) + foreach($this->globalStructure->getAllFields() as $field) { + $k = $label = $field->getName(); + $help = null; + if(!array_key_exists($field->getName(), $aggs)) { + $label = "/!\\ " . $label; + $help = "New field, please confirm setting."; + } + $addAgg($k, $label, $help); // default choices + } + + // populate aggs to form + foreach($aggs as $k=>$agg) { + $builder->add('aggregates:' . $k . ':limit', ChoiceType::class, $agg); + } - ; } public function getName() From 6838198a6928c1677f483d2786356d2a70b7dedf Mon Sep 17 00:00:00 2001 From: Jean-Yves Gaulier Date: Wed, 22 Jan 2020 19:09:19 +0100 Subject: [PATCH 06/17] PHRAS-2879_facet-order_4.1 fix setup:install. --- config/configuration.sample.yml | 3 ++- .../Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/config/configuration.sample.yml b/config/configuration.sample.yml index fbaa368ed9..b7fef2618a 100644 --- a/config/configuration.sample.yml +++ b/config/configuration.sample.yml @@ -28,7 +28,8 @@ main: port: 11211 search-engine: type: phrasea - options: [] + options: + facets: [] task-manager: status: started enabled: true diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php index b9b3d5a054..72c2c6674d 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php @@ -60,6 +60,7 @@ class ElasticsearchOptions 'populate_order' => self::POPULATE_ORDER_RID, 'populate_direction' => self::POPULATE_DIRECTION_DESC, 'activeTab' => null, + 'facets' => [] ]; $options = array_replace($defaultOptions, $options); From 4305441a0318c923646c6bcc1cb8198db4692bff Mon Sep 17 00:00:00 2001 From: Jean-Yves Gaulier Date: Wed, 22 Jan 2020 19:24:31 +0100 Subject: [PATCH 07/17] PHRAS-2879_facet-order_4.1 fix setup:install (cont') --- .../Elastic/ElasticsearchOptions.php | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php index 72c2c6674d..22918b8b3a 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php @@ -100,8 +100,8 @@ class ElasticsearchOptions 'activeTab' => $this->activeTab, 'aggregates' => [] ]; - foreach($this->_customValues['aggregates'] as $fieldname=>$attributes) { - $ret['aggregates'][$fieldname] = $attributes; + foreach($this->getAggregableFields() as $fieldname=>$attributes) { + $ret['facets'][$fieldname] = $attributes; } return $ret; @@ -221,17 +221,22 @@ class ElasticsearchOptions public function setAggregableField($key, $attributes) { - $this->_customValues['aggregates'][$key] = $attributes; + $facets = $this->getAggregableFields(); + $facets[$key] = $attributes; } public function getAggregableField($key) { - return $this->_customValues['aggregates'][$key]; + $facets = $this->getAggregableFields(); + return array_key_exists($key, $facets) ? $facets[$key] : null; } - public function getAggregableFields() + public function &getAggregableFields() { - return $this->_customValues['aggregates']; + if(!array_key_exists('facets', $this->_customValues) || !is_array($this->_customValues['facets'])) { + $this->_customValues['facets'] = []; + } + return $this->_customValues['facets']; } public function getActiveTab() From 8171b7c1f12af24da4f0f2e5410cd0e959dd0d62 Mon Sep 17 00:00:00 2001 From: Jean-Yves Gaulier Date: Wed, 22 Jan 2020 19:35:31 +0100 Subject: [PATCH 08/17] PHRAS-2879_facet-order_4.1 fix setup:install (cont' 2) --- .../Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php index 22918b8b3a..d65221dc8f 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php @@ -38,7 +38,7 @@ class ElasticsearchOptions private $populateDirection; /** @var int[] */ - private $_customValues; + private $_customValues = []; private $activeTab; /** From 53f17e18979abffe7709d26cb7a60553b0dfc308 Mon Sep 17 00:00:00 2001 From: Jean-Yves Gaulier Date: Tue, 28 Jan 2020 17:56:30 +0100 Subject: [PATCH 09/17] save modifications in conf, and also write back to databoxes --- .../Admin/SearchEngineController.php | 11 +++++++++ .../Provider/SearchEngineServiceProvider.php | 3 ++- .../Elastic/ElasticSearchEngine.php | 2 +- .../Elastic/ElasticsearchOptions.php | 24 ++++++++++++++++--- 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/lib/Alchemy/Phrasea/Controller/Admin/SearchEngineController.php b/lib/Alchemy/Phrasea/Controller/Admin/SearchEngineController.php index 8073027302..565c2d759b 100644 --- a/lib/Alchemy/Phrasea/Controller/Admin/SearchEngineController.php +++ b/lib/Alchemy/Phrasea/Controller/Admin/SearchEngineController.php @@ -17,6 +17,7 @@ use Alchemy\Phrasea\SearchEngine\Elastic\Structure\GlobalStructure; use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use databox_descriptionStructure; class SearchEngineController extends Controller { @@ -77,6 +78,16 @@ class SearchEngineController extends Controller */ private function saveElasticSearchOptions(ElasticsearchOptions $configuration) { + // save to databoxes fields for backward compatibility (useless ?) + foreach($configuration->getAggregableFields() as $fname=>$aggregableField) { + foreach ($this->app->getDataboxes() as $databox) { + if(!is_null($f = $databox->get_meta_structure()->get_element_by_name($fname, databox_descriptionStructure::STRICT_COMPARE))) { + $f->set_aggregable($aggregableField['limit'])->save(); + } + } + } + + // save to conf $this->getConf()->set(['main', 'search-engine', 'options'], $configuration->toArray()); } diff --git a/lib/Alchemy/Phrasea/Core/Provider/SearchEngineServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/SearchEngineServiceProvider.php index 58f77475a1..faddfae370 100644 --- a/lib/Alchemy/Phrasea/Core/Provider/SearchEngineServiceProvider.php +++ b/lib/Alchemy/Phrasea/Core/Provider/SearchEngineServiceProvider.php @@ -228,7 +228,8 @@ class SearchEngineServiceProvider implements ServiceProviderInterface }); $app['elasticsearch.options'] = $app->share(function ($app) { - $options = ElasticsearchOptions::fromArray($app['conf']->get(['main', 'search-engine', 'options'], [])); + $conf = $app['conf']->get(['main', 'search-engine', 'options'], []); + $options = ElasticsearchOptions::fromArray($conf); if (empty($options->getIndexName())) { $options->setIndexName(strtolower(sprintf('phraseanet_%s', str_replace( diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticSearchEngine.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticSearchEngine.php index 63d9ec108e..15482f31d4 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticSearchEngine.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticSearchEngine.php @@ -652,7 +652,7 @@ class ElasticSearchEngine implements SearchEngineInterface $aggs = []; // technical aggregates (enable + optional limit) foreach (ElasticsearchOptions::getAggregableTechnicalFields() as $k => $f) { - $size = $this->options->getAggregableFieldLimit($k); + $size = $this->options->getAggregableFieldLimit('_'.$k); if ($size !== databox_field::FACET_DISABLED) { if ($size === databox_field::FACET_NO_LIMIT) { $size = ESField::FACET_NO_LIMIT; diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php index d65221dc8f..14ed77d008 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php @@ -9,6 +9,7 @@ */ namespace Alchemy\Phrasea\SearchEngine\Elastic; +use databox_field; use igorw; @@ -219,10 +220,24 @@ class ElasticsearchOptions $this->highlight = $highlight; } + public function setAggregableFieldLimit($key, $value) + { + if(is_null($this->getAggregableField($key))) { + $this->_customValues['facets'][$key] = []; + } + $this->_customValues['facets'][$key]['limit'] = $value; + } + public function setAggregableField($key, $attributes) { - $facets = $this->getAggregableFields(); - $facets[$key] = $attributes; + $this->getAggregableFields(); // ensure facets exists + $this->_customValues['facets'][$key] = $attributes; + } + + public function getAggregableFieldLimit($key) + { + $facet = $this->getAggregableField($key); + return (is_array($facet) && array_key_exists('limit', $facet)) ? $facet['limit'] : databox_field::FACET_DISABLED; } public function getAggregableField($key) @@ -231,7 +246,10 @@ class ElasticsearchOptions return array_key_exists($key, $facets) ? $facets[$key] : null; } - public function &getAggregableFields() + /** + * @return array + */ + public function getAggregableFields() { if(!array_key_exists('facets', $this->_customValues) || !is_array($this->_customValues['facets'])) { $this->_customValues['facets'] = []; From 20839f1dd7bee23296092fefdf1669ab375f472a Mon Sep 17 00:00:00 2001 From: Jean-Yves Gaulier Date: Wed, 29 Jan 2020 18:39:15 +0100 Subject: [PATCH 10/17] return facets following the order defined in conf. --- .../Controller/Prod/QueryController.php | 10 +------ .../Provider/SearchEngineServiceProvider.php | 2 +- .../Elastic/ElasticSearchEngine.php | 27 ++++++++++--------- .../Elastic/Search/FacetsResponse.php | 11 ++++++-- .../Elastic/Structure/GlobalStructure.php | 15 ++--------- .../Elastic/Structure/LimitedStructure.php | 8 ------ .../Elastic/Structure/Structure.php | 5 ---- 7 files changed, 28 insertions(+), 50 deletions(-) diff --git a/lib/Alchemy/Phrasea/Controller/Prod/QueryController.php b/lib/Alchemy/Phrasea/Controller/Prod/QueryController.php index d327e860c5..82fab9e671 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/QueryController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/QueryController.php @@ -360,6 +360,7 @@ class QueryController extends Controller // add technical fields $fieldsInfosByName = []; foreach(ElasticsearchOptions::getAggregableTechnicalFields() as $k => $f) { + $k = '_'.$k; $fieldsInfosByName[$k] = $f; $fieldsInfosByName[$k]['trans_label'] = $this->app->trans($f['label']); $fieldsInfosByName[$k]['labels'] = []; @@ -433,24 +434,15 @@ class QueryController extends Controller // populates facets (aggregates) $facets = []; - // $facetClauses = []; foreach ($result->getFacets() as $facet) { $facetName = $facet['name']; if(array_key_exists($facetName, $fieldsInfosByName)) { - $f = $fieldsInfosByName[$facetName]; - $facet['label'] = $f['trans_label']; $facet['labels'] = $f['labels']; $facet['type'] = strtoupper($f['type']) . "-AGGREGATE"; $facets[] = $facet; - - // $facetClauses[] = [ - // 'type' => strtoupper($f['type']) . "-AGGREGATE", - // 'field' => $f['field'], - // 'facet' => $facet - // ]; } } diff --git a/lib/Alchemy/Phrasea/Core/Provider/SearchEngineServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/SearchEngineServiceProvider.php index faddfae370..e342bf8380 100644 --- a/lib/Alchemy/Phrasea/Core/Provider/SearchEngineServiceProvider.php +++ b/lib/Alchemy/Phrasea/Core/Provider/SearchEngineServiceProvider.php @@ -97,7 +97,7 @@ class SearchEngineServiceProvider implements ServiceProviderInterface }); $app['elasticsearch.facets_response.factory'] = $app->protect(function (array $response) use ($app) { - return new FacetsResponse(new Escaper(), $response, $app['search_engine.structure']); + return new FacetsResponse($app['elasticsearch.options'], new Escaper(), $response, $app['search_engine.structure']); }); return $app; diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticSearchEngine.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticSearchEngine.php index 15482f31d4..df89adf167 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticSearchEngine.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticSearchEngine.php @@ -663,22 +663,25 @@ class ElasticSearchEngine implements SearchEngineInterface 'size' => $size ] ]; - $aggs[$k] = $agg; + $aggs['_'.$k] = $agg; } } // fields aggregates $structure = $this->context_factory->getLimitedStructure($options); - foreach ($structure->getFacetFields() as $name => $field) { - // 2015-05-26 (mdarse) Removed databox filtering. - // It was already done by the ACL filter in the query scope, so no - // document that shouldn't be displayed can go this far. - $agg = [ - 'terms' => [ - 'field' => $field->getIndexField(true), - 'size' => $field->getFacetValuesLimit() - ] - ]; - $aggs[$name] = AggregationHelper::wrapPrivateFieldAggregation($field, $agg); + foreach($structure->getAllFields() as $name => $field) { + $size = $this->options->getAggregableFieldLimit($name); + if ($size !== databox_field::FACET_DISABLED) { + if ($size === databox_field::FACET_NO_LIMIT) { + $size = ESField::FACET_NO_LIMIT; + } + $agg = [ + 'terms' => [ + 'field' => $field->getIndexField(true), + 'size' => $size + ] + ]; + $aggs[$name] = AggregationHelper::wrapPrivateFieldAggregation($field, $agg); + } } return $aggs; diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Search/FacetsResponse.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Search/FacetsResponse.php index 2f2d966c51..efcc6d1c76 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Search/FacetsResponse.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Search/FacetsResponse.php @@ -15,7 +15,7 @@ class FacetsResponse private $escaper; private $facets = array(); - public function __construct(Escaper $escaper, array $response, GlobalStructure $structure) + public function __construct(ElasticsearchOptions $options, Escaper $escaper, array $response, GlobalStructure $structure) { $this->escaper = $escaper; @@ -25,7 +25,13 @@ class FacetsResponse $atf = ElasticsearchOptions::getAggregableTechnicalFields(); - foreach ($response['aggregations'] as $name => $aggregation) { + // sort facets respecting the order defined in options + foreach($options->getAggregableFields() as $name=>$foptions) { + if(!array_key_exists($name, $response['aggregations'])) { + continue; + } + $aggregation = $response['aggregations'][$name]; + $tf = null; $valueFormatter = function($v){ return $v; }; // default equality formatter @@ -78,6 +84,7 @@ class FacetsResponse ]; } } + } diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/GlobalStructure.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/GlobalStructure.php index ea4022dffb..0793c91c7b 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/GlobalStructure.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/GlobalStructure.php @@ -35,11 +35,6 @@ final class GlobalStructure implements Structure */ private $private = array(); - /** - * @var Field[] - */ - private $facets = array(); - /** * @var Flag[] */ @@ -145,9 +140,11 @@ final class GlobalStructure implements Structure $this->private[$name] = $field; } + /* if ($field->isFacet() && $field->isSearchable()) { $this->facets[$name] = $field; } + */ if ($field->hasConceptInference()) { $this->thesaurus_fields[$name] = $field; @@ -183,14 +180,6 @@ final class GlobalStructure implements Structure return $this->private; } - /** - * @return Field[] - */ - public function getFacetFields() - { - return $this->facets; - } - /** * @return Field[] */ diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/LimitedStructure.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/LimitedStructure.php index 671bf87c93..053ca6b0e0 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/LimitedStructure.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/LimitedStructure.php @@ -47,14 +47,6 @@ final class LimitedStructure implements Structure return $this->limit($this->structure->getPrivateFields()); } - /** - * @return Field[] - */ - public function getFacetFields() - { - return $this->limit($this->structure->getFacetFields()); - } - public function getThesaurusEnabledFields() { return $this->limit($this->structure->getThesaurusEnabledFields()); diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/Structure.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/Structure.php index 3c2be701e1..44d58e9d5f 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/Structure.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/Structure.php @@ -33,11 +33,6 @@ interface Structure */ public function getPrivateFields(); - /** - * @return Field[] - */ - public function getFacetFields(); - /** * @return Field[] */ From 8312be6fc8adc015b0088a87310dac7b9decbfe0 Mon Sep 17 00:00:00 2001 From: Jean-Yves Gaulier Date: Wed, 29 Jan 2020 19:00:00 +0100 Subject: [PATCH 11/17] removed useless tests --- .../SearchEngine/Structure/StructureTest.php | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/tests/Alchemy/Tests/Phrasea/SearchEngine/Structure/StructureTest.php b/tests/Alchemy/Tests/Phrasea/SearchEngine/Structure/StructureTest.php index 27f01811c1..b3bde05cd9 100644 --- a/tests/Alchemy/Tests/Phrasea/SearchEngine/Structure/StructureTest.php +++ b/tests/Alchemy/Tests/Phrasea/SearchEngine/Structure/StructureTest.php @@ -20,7 +20,6 @@ class StructureTest extends \PHPUnit_Framework_TestCase $this->assertEmpty($structure->getAllFields()); $this->assertEmpty($structure->getUnrestrictedFields()); $this->assertEmpty($structure->getPrivateFields()); - $this->assertEmpty($structure->getFacetFields()); $this->assertEmpty($structure->getThesaurusEnabledFields()); $this->assertEmpty($structure->getDateFields()); } @@ -95,19 +94,6 @@ class StructureTest extends \PHPUnit_Framework_TestCase $this->assertNotContains($unrestricted_field, $private_fields); } - public function testGetFacetFields() - { - $facet = new Field('foo', FieldMapping::TYPE_STRING, ['facet' => Field::FACET_NO_LIMIT]); - $not_facet = new Field('bar', FieldMapping::TYPE_STRING, ['facet' => Field::FACET_DISABLED]); - $structure = new Structure(); - $structure->add($facet); - $this->assertContains($facet, $structure->getFacetFields()); - $structure->add($not_facet); - $facet_fields = $structure->getFacetFields(); - $this->assertContains($facet, $facet_fields); - $this->assertNotContains($not_facet, $facet_fields); - } - public function testGetDateFields() { $string = new Field('foo', FieldMapping::TYPE_STRING); From b1b35cc92b0a5d657f42000209258b30659ca291 Mon Sep 17 00:00:00 2001 From: Jean-Yves Gaulier Date: Thu, 30 Jan 2020 15:25:40 +0100 Subject: [PATCH 12/17] fix malformed query on tech fields ('_' in query) --- .../Controller/Prod/QueryController.php | 1 - .../Elastic/ElasticSearchEngine.php | 4 +-- .../Elastic/ElasticsearchOptions.php | 30 +++++++++---------- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/lib/Alchemy/Phrasea/Controller/Prod/QueryController.php b/lib/Alchemy/Phrasea/Controller/Prod/QueryController.php index 82fab9e671..8afd22d2b3 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/QueryController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/QueryController.php @@ -360,7 +360,6 @@ class QueryController extends Controller // add technical fields $fieldsInfosByName = []; foreach(ElasticsearchOptions::getAggregableTechnicalFields() as $k => $f) { - $k = '_'.$k; $fieldsInfosByName[$k] = $f; $fieldsInfosByName[$k]['trans_label'] = $this->app->trans($f['label']); $fieldsInfosByName[$k]['labels'] = []; diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticSearchEngine.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticSearchEngine.php index df89adf167..2424330031 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticSearchEngine.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticSearchEngine.php @@ -652,7 +652,7 @@ class ElasticSearchEngine implements SearchEngineInterface $aggs = []; // technical aggregates (enable + optional limit) foreach (ElasticsearchOptions::getAggregableTechnicalFields() as $k => $f) { - $size = $this->options->getAggregableFieldLimit('_'.$k); + $size = $this->options->getAggregableFieldLimit($k); if ($size !== databox_field::FACET_DISABLED) { if ($size === databox_field::FACET_NO_LIMIT) { $size = ESField::FACET_NO_LIMIT; @@ -663,7 +663,7 @@ class ElasticSearchEngine implements SearchEngineInterface 'size' => $size ] ]; - $aggs['_'.$k] = $agg; + $aggs[$k] = $agg; } } // fields aggregates diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php index 14ed77d008..b469af4d92 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php @@ -282,42 +282,42 @@ class ElasticsearchOptions public static function getAggregableTechnicalFields() { return [ - 'base' => [ + '_base' => [ 'type' => 'string', 'label' => 'prod::facet:base_label', 'field' => "database", 'esfield' => 'databox_name', 'query' => 'database:%s', ], - 'collection' => [ + '_collection' => [ 'type' => 'string', 'label' => 'prod::facet:collection_label', 'field' => "collection", 'esfield' => 'collection_name', 'query' => 'collection:%s', ], - 'doctype' => [ + '_doctype' => [ 'type' => 'string', 'label' => 'prod::facet:doctype_label', 'field' => "type", 'esfield' => 'type', 'query' => 'type:%s', ], - 'camera_model' => [ + '_camera_model' => [ 'type' => 'string', 'label' => 'Camera Model', 'field' => "meta.CameraModel", 'esfield' => 'metadata_tags.CameraModel', 'query' => 'meta.CameraModel:%s', ], - 'iso' => [ + '_iso' => [ 'type' => 'number', 'label' => 'ISO', 'field' => "meta.ISO", 'esfield' => 'metadata_tags.ISO', 'query' => 'meta.ISO=%s', ], - 'aperture' => [ + '_aperture' => [ 'type' => 'number', 'label' => 'Aperture', 'field' => "meta.Aperture", @@ -327,7 +327,7 @@ class ElasticsearchOptions return round($value, 1); }, ], - 'shutterspeed' => [ + '_shutterspeed' => [ 'type' => 'number', 'label' => 'Shutter speed', 'field' => "meta.ShutterSpeed", @@ -340,7 +340,7 @@ class ElasticsearchOptions return $value . ' s.'; }, ], - 'flashfired' => [ + '_flashfired' => [ 'type' => 'boolean', 'label' => 'FlashFired', 'field' => "meta.FlashFired", @@ -354,49 +354,49 @@ class ElasticsearchOptions return array_key_exists($value, $map) ? $map[$value] : $value; }, ], - 'framerate' => [ + '_framerate' => [ 'type' => 'number', 'label' => 'FrameRate', 'field' => "meta.FrameRate", 'esfield' => 'metadata_tags.FrameRate', 'query' => 'meta.FrameRate=%s', ], - 'audiosamplerate' => [ + '_audiosamplerate' => [ 'type' => 'number', 'label' => 'Audio Samplerate', 'field' => "meta.AudioSamplerate", 'esfield' => 'metadata_tags.AudioSamplerate', 'query' => 'meta.AudioSamplerate=%s', ], - 'videocodec' => [ + '_videocodec' => [ 'type' => 'string', 'label' => 'Video codec', 'field' => "meta.VideoCodec", 'esfield' => 'metadata_tags.VideoCodec', 'query' => 'meta.VideoCodec:%s', ], - 'audiocodec' => [ + '_audiocodec' => [ 'type' => 'string', 'label' => 'Audio codec', 'field' => "meta.AudioCodec", 'esfield' => 'metadata_tags.AudioCodec', 'query' => 'meta.AudioCodec:%s', ], - 'orientation' => [ + '_orientation' => [ 'type' => 'string', 'label' => 'Orientation', 'field' => "meta.Orientation", 'esfield' => 'metadata_tags.Orientation', 'query' => 'meta.Orientation=%s', ], - 'colorspace' => [ + '_colorspace' => [ 'type' => 'string', 'label' => 'Colorspace', 'field' => "meta.ColorSpace", 'esfield' => 'metadata_tags.ColorSpace', 'query' => 'meta.ColorSpace:%s', ], - 'mimetype' => [ + '_mimetype' => [ 'type' => 'string', 'label' => 'MimeType', 'field' => "meta.MimeType", From 3b8d75d39d34acafa60ef282c32e8f3d85ee2b7a Mon Sep 17 00:00:00 2001 From: Jean-Yves Gaulier Date: Mon, 3 Feb 2020 20:46:53 +0100 Subject: [PATCH 13/17] fix : tech facets did appear twice. --- .../SearchEngine/Elastic/ElasticsearchSettingsFormType.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchSettingsFormType.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchSettingsFormType.php index 985bda7f0e..4dbf6899bf 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchSettingsFormType.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchSettingsFormType.php @@ -129,11 +129,11 @@ class ElasticsearchSettingsFormType extends AbstractType $choices = array_key_exists('choices', $f) ? $f['choices'] : null; // a tech-field can publish it's own choices $help = null; $label = '#' . $k; - if(!array_key_exists('_'.$k, $aggs)) { + if(!array_key_exists($k, $aggs)) { $label = "/!\\ " . $label; $help = "New field, please confirm setting."; } - $addAgg('_'.$k, $label, $help, false, $choices); + $addAgg($k, $label, $help, false, $choices); } // add or replace all databoxes fields (nb: new db field - unknown in conf - will be a the end) From c7fc2b64222fc9472bf9a2f6118c2cf27bdce1ae Mon Sep 17 00:00:00 2001 From: Jean-Yves Gaulier Date: Thu, 6 Feb 2020 16:25:40 +0100 Subject: [PATCH 14/17] change patch number --- lib/classes/patch/{410alpha20a.php => 410alpha21a.php} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename lib/classes/patch/{410alpha20a.php => 410alpha21a.php} (96%) diff --git a/lib/classes/patch/410alpha20a.php b/lib/classes/patch/410alpha21a.php similarity index 96% rename from lib/classes/patch/410alpha20a.php rename to lib/classes/patch/410alpha21a.php index ff02a35c96..58091e62f2 100644 --- a/lib/classes/patch/410alpha20a.php +++ b/lib/classes/patch/410alpha21a.php @@ -11,10 +11,10 @@ use Alchemy\Phrasea\Application; -class patch_410alpha20a implements patchInterface +class patch_410alpha21a implements patchInterface { /** @var string */ - private $release = '4.1.0-alpha.20a'; + private $release = '4.1.0-alpha.21a'; /** @var array */ private $concern = [base::DATA_BOX]; From eeec1ab716c7d6f838f80a2946fdd1f764f8067b Mon Sep 17 00:00:00 2001 From: Jean-Yves Gaulier Date: Mon, 10 Feb 2020 20:56:46 +0100 Subject: [PATCH 15/17] save facets following the order of the admin form. --- .../Controller/Admin/SearchEngineController.php | 14 +++++++++++++- .../SearchEngine/Elastic/ElasticsearchOptions.php | 12 ++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/lib/Alchemy/Phrasea/Controller/Admin/SearchEngineController.php b/lib/Alchemy/Phrasea/Controller/Admin/SearchEngineController.php index 565c2d759b..04762b3aef 100644 --- a/lib/Alchemy/Phrasea/Controller/Admin/SearchEngineController.php +++ b/lib/Alchemy/Phrasea/Controller/Admin/SearchEngineController.php @@ -33,7 +33,19 @@ class SearchEngineController extends Controller $form->handleRequest($request); if ($form->isValid()) { - $this->saveElasticSearchOptions($form->getData()); + /** @var ElasticsearchOptions $data */ + $data = $form->getData(); + // $q = $request->request->get('elasticsearch_settings'); + $facetNames = []; // rebuild the data "_customValues/facets" list following the form order + foreach($request->request->get('elasticsearch_settings') as $name=>$value) { + $matches = null; + if(preg_match('/^facets:(.+):limit$/', $name, $matches) === 1) { + $facetNames[] = $matches[1]; + } + } + $data->reorderAggregableFields($facetNames); + + $this->saveElasticSearchOptions($data); return $this->app->redirectPath('admin_searchengine_form'); } diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php index b469af4d92..c471980752 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php @@ -257,6 +257,18 @@ class ElasticsearchOptions return $this->_customValues['facets']; } + // set to change the facets order during admin/form save + public function reorderAggregableFields($facetNames) + { + $newFacets = []; + foreach ($facetNames as $name) { + if(($facet = $this->getAggregableField($name)) !== null) { + $newFacets[$name] = $facet; + } + } + $this->_customValues['facets'] = $newFacets; + } + public function getActiveTab() { return $this->activeTab; From 4f7be4c0a97bd7bbf32562147b9d68e44759019c Mon Sep 17 00:00:00 2001 From: Jean-Yves Gaulier Date: Mon, 10 Feb 2020 21:15:02 +0100 Subject: [PATCH 16/17] fix bad merge happened cause last rebase (?) --- .../Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php index c471980752..07e54449eb 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchOptions.php @@ -76,7 +76,7 @@ class ElasticsearchOptions $self->setPopulateOrder($options['populate_order']); $self->setPopulateDirection($options['populate_direction']); $self->setActiveTab($options['activeTab']); - foreach($options['aggregates'] as $fieldname=>$attributes) { + foreach($options['facets'] as $fieldname=>$attributes) { $self->setAggregableField($fieldname, $attributes); } @@ -99,7 +99,7 @@ class ElasticsearchOptions 'populate_order' => $this->populateOrder, 'populate_direction' => $this->populateDirection, 'activeTab' => $this->activeTab, - 'aggregates' => [] + 'facets' => [] ]; foreach($this->getAggregableFields() as $fieldname=>$attributes) { $ret['facets'][$fieldname] = $attributes; From 722b1e37505e39d4e3b67a4b3056e92a839f7a9d Mon Sep 17 00:00:00 2001 From: Jean-Yves Gaulier Date: Mon, 17 Feb 2020 20:16:19 +0100 Subject: [PATCH 17/17] include Harrys's new templates fix form restore fix Version --- lib/Alchemy/Phrasea/Core/Version.php | 2 +- .../Elastic/ElasticsearchSettingsFormType.php | 2 +- .../web/admin/fields/templates.html.twig | 14 ++-- .../general-aggregation.html.twig | 66 +++++++++++++++++-- 4 files changed, 70 insertions(+), 14 deletions(-) diff --git a/lib/Alchemy/Phrasea/Core/Version.php b/lib/Alchemy/Phrasea/Core/Version.php index 5f488cdd5a..3a3ff63afc 100644 --- a/lib/Alchemy/Phrasea/Core/Version.php +++ b/lib/Alchemy/Phrasea/Core/Version.php @@ -16,7 +16,7 @@ class Version /** * @var string */ - private $number = '4.1.0-alpha.20a'; + private $number = '4.1.0-alpha.21a'; /** * @var string diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchSettingsFormType.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchSettingsFormType.php index 4dbf6899bf..1f2fa3d1e2 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchSettingsFormType.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticsearchSettingsFormType.php @@ -149,7 +149,7 @@ class ElasticsearchSettingsFormType extends AbstractType // populate aggs to form foreach($aggs as $k=>$agg) { - $builder->add('aggregates:' . $k . ':limit', ChoiceType::class, $agg); + $builder->add('facets:' . $k . ':limit', ChoiceType::class, $agg); } } diff --git a/templates/web/admin/fields/templates.html.twig b/templates/web/admin/fields/templates.html.twig index 0dc661fc5c..016534fd52 100644 --- a/templates/web/admin/fields/templates.html.twig +++ b/templates/web/admin/fields/templates.html.twig @@ -238,14 +238,12 @@ - + <%= field['aggregable'] == "0" ? '{% trans %}Not aggregated{% endtrans %}' : '' %> + <%= field['aggregable'] == "10" ? '10 values' : '' %> + <%= field['aggregable'] == "20" ? '20 values' : '' %> + <%= field['aggregable'] == "50" ? '50 values' : '' %> + <%= field['aggregable'] == "100" ? '100 values' : '' %> + <%= field['aggregable'] == "-1" ? 'All values' : '' %> diff --git a/templates/web/admin/search-engine/general-aggregation.html.twig b/templates/web/admin/search-engine/general-aggregation.html.twig index d4b83ba31b..0c377d2ecd 100644 --- a/templates/web/admin/search-engine/general-aggregation.html.twig +++ b/templates/web/admin/search-engine/general-aggregation.html.twig @@ -1,12 +1,37 @@
+ +
+ {#{{ 'See' | trans }} : #} + + + + + + + +
    {% for formdata in form %} {% set attr = formdata.vars['attr']|join(',') %} + {% set label = formdata.vars['label']|join(',') %} {% if attr == 'aggregate' %} -
  • +
  • + +
    + + {{ form_label(formdata, null, { 'label_attr': {'class': 'label-aggregation'} @@ -22,6 +47,39 @@ {% endif %} {% endfor %} - - \ No newline at end of file + + + + \ No newline at end of file