Skip to content

Commit

Permalink
Implement boost on spans and make it configurable. Also allow to conf…
Browse files Browse the repository at this point in the history
…igure norms for fields.
  • Loading branch information
romainruaud committed Apr 5, 2023
1 parent ed44a33 commit 58f22d9
Show file tree
Hide file tree
Showing 17 changed files with 350 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ public function __construct(
/**
* Append ES specifics fields into the attribute edit store front tab.
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*
* @param Front $subject The StoreFront tab
* @param Front $result Result
* @param Form $form The form
Expand Down Expand Up @@ -124,6 +126,13 @@ public function afterSetForm(Front $subject, Front $result, Form $form)
$this->addIncludeZeroFalseField($fieldset);
}

if (($this->getAttribute()->getBackendType() == 'varchar')
|| (in_array($this->getAttribute()->getFrontendInput(), ['select', 'multiselect']))
) {
$this->addIsSpannableField($fieldset);
$this->addDisableNormsField($fieldset);
}

$this->appendFieldsDependency($subject);

return $result;
Expand Down Expand Up @@ -497,6 +506,68 @@ private function addIncludeZeroFalseField(Fieldset $fieldset)
return $this;
}

/**
* Add field allowing to configure if a field can be used for span queries.
*
* @param Fieldset $fieldset Target fieldset
*
* @return FrontPlugin
*/
private function addIsSpannableField(Fieldset $fieldset)
{
$isSpannableNote = __(
// phpcs:ignore Generic.Files.LineLength
'Default : No. If set to Yes, the engine will try to match the current query string at the beginning of this string.'
. ' Eg: when enabled on "name", if a customer search for "red dress", the engine will give an higher score to products having'
. ' a name beginning by "red dress". This requires the Span Match Boost feature to be enabled.'
);
$fieldset->addField(
'is_spannable',
'select',
[
'name' => 'is_spannable',
'label' => __('Use this field for span queries.'),
'values' => $this->booleanSource->toOptionArray(),
// phpcs:ignore Generic.Files.LineLength
'note' => $isSpannableNote,
],
'is_used_in_spellcheck'
);

return $this;
}

/**
* Add field allowing to configure if zero/false values should be indexed or ignored.
*
* @param Fieldset $fieldset Target fieldset
*
* @return FrontPlugin
*/
private function addDisableNormsField(Fieldset $fieldset)
{
$disableNormsNote = __(
// phpcs:ignore Generic.Files.LineLength
'Default : No. By default, the score of a text match in a field will vary according to the field length.'
. ' Eg: when searching for "dress", a product named "red dress" will have an higher score than a product named'
. ' "red dress with long sleeves". You can set this to "Yes" to discard this behavior.'
);
$fieldset->addField(
'norms_disabled',
'select',
[
'name' => 'norms_disabled',
'label' => __('Discard the field length for scoring.'),
'values' => $this->booleanSource->toOptionArray(),
// phpcs:ignore Generic.Files.LineLength
'note' => $disableNormsNote,
],
'is_spannable'
);

return $this;
}

/**
* Manage dependency between fields.
*
Expand All @@ -520,8 +591,14 @@ private function appendFieldsDependency($subject)
->addFieldMap('sort_order_asc_missing', 'sort_order_asc_missing')
->addFieldMap('sort_order_desc_missing', 'sort_order_desc_missing')
->addFieldMap('is_display_rel_nofollow', 'is_display_rel_nofollow')
->addFieldMap('is_spannable', 'is_spannable')
->addFieldMap('norms_disabled', 'norms_disabled')
->addFieldMap('search_weight', 'search_weight')
->addFieldDependence('is_displayed_in_autocomplete', 'is_filterable_in_search', '1')
->addFieldDependence('is_used_in_spellcheck', 'is_searchable', '1')
->addFieldDependence('is_spannable', 'is_searchable', '1')
->addFieldDependence('norms_disabled', 'is_searchable', '1')
->addFieldDependence('search_weight', 'is_searchable', '1')
->addFieldDependence('sort_order_asc_missing', 'used_for_sort_by', '1')
->addFieldDependence('sort_order_desc_missing', 'used_for_sort_by', '1')
->addFieldDependence('is_display_rel_nofollow', 'is_filterable', '1');
Expand Down
8 changes: 8 additions & 0 deletions src/module-elasticsuite-catalog/Helper/AbstractAttribute.php
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,14 @@ public function getMappingFieldOptions(AttributeInterface $attribute)
$options['sort_order_desc_missing'] = $attribute->getSortOrderDescMissing();
}

if ($attribute->getIsSpannable()) {
$options['is_spannable'] = $attribute->getIsSpannable();
}

if ($attribute->getNormsDisabled()) {
$options['norms_disabled'] = $attribute->getNormsDisabled();
}

return $options;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class AttributePlugin
'is_used_for_sort_by',
'is_used_in_spellcheck',
'include_zero_false_values',
'disable_norms',
];

/**
Expand All @@ -54,6 +55,8 @@ class AttributePlugin
EavAttributeInterface::USED_FOR_SORT_BY,
EavAttributeInterface::IS_VISIBLE_IN_ADVANCED_SEARCH,
'search_weight',
'disable_norms',
'is_spannable',
];

/**
Expand Down
52 changes: 52 additions & 0 deletions src/module-elasticsuite-catalog/Setup/CatalogSetup.php
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,58 @@ public function addIncludeZeroFalseValues(SchemaSetupInterface $setup)
);
}

/**
* Add "is_spannable" field to catalog_eav_attribute table.
*
* @param \Magento\Framework\Setup\SchemaSetupInterface $setup Schema Setup
*
* @return void
*/
public function addIsSpannableAttributeProperty(\Magento\Framework\Setup\SchemaSetupInterface $setup)
{
$connection = $setup->getConnection();
$table = $setup->getTable('catalog_eav_attribute');

// Append a column 'is_spannable' into the db.
$connection->addColumn(
$table,
'is_spannable',
[
'type' => \Magento\Framework\DB\Ddl\Table::TYPE_BOOLEAN,
'nullable' => false,
'default' => 0,
'size' => 1,
'comment' => 'Should this field be used for span queries.',
]
);
}

/**
* Add "norms_disabled" field to catalog_eav_attribute table.
*
* @param \Magento\Framework\Setup\SchemaSetupInterface $setup Schema Setup
*
* @return void
*/
public function addNormsDisabledAttributeProperty(\Magento\Framework\Setup\SchemaSetupInterface $setup)
{
$connection = $setup->getConnection();
$table = $setup->getTable('catalog_eav_attribute');

// Append a column 'norms_disabled' into the db.
$connection->addColumn(
$table,
'norms_disabled',
[
'type' => \Magento\Framework\DB\Ddl\Table::TYPE_BOOLEAN,
'nullable' => false,
'default' => 0,
'size' => 1,
'comment' => 'If this field should have norms:false in Elasticsearch.',
]
);
}

/**
* Update attribute value for an entity with a default value.
* All existing values are erased by the new value.
Expand Down
4 changes: 4 additions & 0 deletions src/module-elasticsuite-catalog/Setup/InstallSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ public function install(SchemaSetupInterface $setup, ModuleContextInterface $con
// Introduced in version 1.6.1.
$this->catalogSetup->addIncludeZeroFalseValues($setup);

// Introduced in version 1.7.0.
$this->catalogSetup->addIsSpannableAttributeProperty($setup);
$this->catalogSetup->addNormsDisabledAttributeProperty($setup);

$setup->endSetup();
}
}
5 changes: 5 additions & 0 deletions src/module-elasticsuite-catalog/Setup/UpgradeSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@ public function upgrade(
$this->catalogSetup->addIncludeZeroFalseValues($setup);
}

if (version_compare($context->getVersion(), '1.7.0', '<')) {
$this->catalogSetup->addIsSpannableAttributeProperty($setup);
$this->catalogSetup->addNormsDisabledAttributeProperty($setup);
}

$setup->endSetup();
}
}
2 changes: 1 addition & 1 deletion src/module-elasticsuite-catalog/etc/module.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Smile_ElasticsuiteCatalog" setup_version="1.6.3">
<module name="Smile_ElasticsuiteCatalog" setup_version="1.7.0">
<sequence>
<module name="Smile_ElasticsuiteCore" />
<module name="Magento_Search" />
Expand Down
14 changes: 14 additions & 0 deletions src/module-elasticsuite-core/Api/Index/Mapping/FieldInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -178,4 +178,18 @@ public function getFilterLogicalOperator();
* @return array
*/
public function getConfig();

/**
* If "norms" of the field in mapping should be set to false.
*
* @return bool
*/
public function normsDisabled();

/**
* Is the field should be used for span queries.
*
* @return boolean
*/
public function isSpannable();
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,11 @@ public function isPhoneticSearchEnabled();
* @return \Smile\ElasticsuiteCore\Api\Search\Request\Container\RelevanceConfiguration\FuzzinessConfigurationInterface|null
*/
public function getFuzzinessConfiguration();

/**
* Retrieve span match boost value if enabled.
*
* @return false|int
*/
public function getSpanMatchBoost();
}
9 changes: 9 additions & 0 deletions src/module-elasticsuite-core/Index/Mapping/Field.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class Field implements FieldInterface
'default_search_analyzer' => self::ANALYZER_STANDARD,
'filter_logical_operator' => self::FILTER_LOGICAL_OPERATOR_OR,
'norms_disabled' => false,
'is_spannable' => false,
];

/**
Expand Down Expand Up @@ -149,6 +150,14 @@ public function normsDisabled(): bool
return (bool) $this->config['norms_disabled'];
}

/**
* {@inheritDoc}
*/
public function isSpannable(): bool
{
return (bool) $this->config['is_spannable'];
}

/**
* {@inheritdoc}
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ class RelevanceConfig implements RelevanceConfigurationInterface
*/
private $enablePhoneticSearch;

/**
* @var integer|null
*/
private $spanMatchBoost;

/**
* RelevanceConfiguration constructor.
*
Expand All @@ -67,21 +72,25 @@ class RelevanceConfig implements RelevanceConfigurationInterface
* @param float $cutOffFrequency The cutoff Frequency value
* @param FuzzinessConfigurationInterface|null $fuzziness The fuzziness Configuration, or null
* @param boolean $enablePhoneticSearch The phonetic Configuration, or null
* @param int|null $spanMatchBoost The Span match boost value, or null if not
* enabled
*/
public function __construct(
$minimumShouldMatch,
$tieBreaker,
$phraseMatchBoost,
$cutOffFrequency,
FuzzinessConfigurationInterface $fuzziness = null,
$enablePhoneticSearch = false
$enablePhoneticSearch = false,
$spanMatchBoost = null
) {
$this->minimumShouldMatch = $minimumShouldMatch;
$this->tieBreaker = $tieBreaker;
$this->phraseMatchBoost = $phraseMatchBoost;
$this->cutOffFrequency = $cutOffFrequency;
$this->fuzzinessConfiguration = $fuzziness;
$this->enablePhoneticSearch = $enablePhoneticSearch;
$this->spanMatchBoost = $spanMatchBoost;
}

/**
Expand Down Expand Up @@ -145,4 +154,12 @@ public function isPhoneticSearchEnabled()
{
return (bool) $this->enablePhoneticSearch;
}

/**
* {@inheritDoc}
*/
public function getSpanMatchBoost()
{
return (int) $this->spanMatchBoost;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ class Factory
*/
const PHONETIC_CONFIG_XML_PATH = 'spellchecking/phonetic/enable';

/**
* XML node for span match configuration
*/
const SPAN_MATCH_CONFIG_XML_PREFIX = 'span_match_configuration';

/**
* @var RelevanceConfigurationInterface[]
*/
Expand Down Expand Up @@ -134,6 +139,7 @@ protected function loadConfiguration($scopeCode)
'cutOffFrequency' => $this->getCutoffFrequencyConfiguration($scopeCode),
'fuzziness' => $this->getFuzzinessConfiguration($scopeCode),
'enablePhoneticSearch' => $this->isPhoneticSearchEnabled($scopeCode),
'spanMatchBoost' => $this->getSpanMatchBoostConfiguration($scopeCode),
];

return $configurationParams;
Expand Down Expand Up @@ -281,4 +287,24 @@ private function getScopeCode($storeId, $containerName)
{
return sprintf("%s|%s", $containerName, $storeId);
}

/**
* Retrieve span boost configuration for a container.
*
* @param string $scopeCode The scope code
*
* @return bool|int
*/
private function getSpanMatchBoostConfiguration($scopeCode)
{
$path = self::BASE_RELEVANCE_CONFIG_XML_PREFIX . "/" . self::SPAN_MATCH_CONFIG_XML_PREFIX;

$boost = (bool) $this->getConfigValue($path . "/enable_span_match", $scopeCode);

if ($boost === true) {
$boost = (int) $this->getConfigValue($path . "/span_match_boost_value", $scopeCode);
}

return $boost;
}
}
Loading

0 comments on commit 58f22d9

Please sign in to comment.