src/module-elasticsuite-core/Search/Request/ContainerConfiguration/BaseConfig/Converter.php
<?php
/**
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade Smile ElasticSuite to newer
* versions in the future.
*
* @category Smile
* @package Smile\ElasticsuiteCore
* @author Aurelien FOUCRET <aurelien.foucret@smile.fr>
* @copyright 2020 Smile
* @license Open Software License ("OSL") v. 3.0
*/
namespace Smile\ElasticsuiteCore\Search\Request\ContainerConfiguration\BaseConfig;
/**
* ElasticSuite search requests XML converter.
*
* @category Smile
* @package Smile\ElasticsuiteCore
* @author Aurelien FOUCRET <aurelien.foucret@smile.fr>
*/
class Converter extends \Magento\Framework\Search\Request\Config\Converter
{
/**
* @var string
*/
const FILTERS_PATH = 'filters/filter';
/**
* @var string
*/
const AGGREGATIONS_PATH = 'aggregations/aggregation';
/**
* @var string
*/
const AGGREGATIONS_PROVIDERS_PATH = 'aggregations/provider';
/**
* @var string
*/
const AGGREGATION_FILTERS_PATH = 'filters/filter';
/**
* @var string
*/
const METRICS_PATH = 'metrics/metric';
/**
* @var string
*/
const PIPELINES_PATH = 'pipelines/pipeline';
/**
* @var string
*/
const PIPELINE_SIMPLE_BUCKETS_PATH = 'bucketsPath/path';
/**
* @var string
*/
const PIPELINE_COMPLEX_BUCKETS_PATH = 'bucketsPath/mapping';
/**
* Convert config.
*
* @param \DOMDocument $source XML file read.
*
* @return array
*/
public function convert($source)
{
// Due to SimpleXML not deleting comment we have to strip them before using the source.
$source = $this->stripComments($source);
/** @var \DOMNodeList $requestNodes */
$requestNodes = $source->getElementsByTagName('request');
$xpath = new \DOMXPath($source);
$requests = [];
foreach ($requestNodes as $requestNode) {
$simpleXmlNode = simplexml_import_dom($requestNode);
/** @var \DOMElement $requestNode */
$name = $requestNode->getAttribute('name');
$request = $this->mergeAttributes((array) $simpleXmlNode);
$request['filters'] = $this->parseFilters($xpath, $requestNode);
$request['aggregations'] = $this->parseAggregations($xpath, $requestNode);
$request['aggregationsProviders'] = $this->parseAggregationsProviders($xpath, $requestNode);
$requests[$name] = $request;
}
return $requests;
}
/**
* This method remove all comments of an XML document.
*
* @param \DOMDocument $source Document to be cleansed.
*
* @return \DOMDocument
*/
private function stripComments(\DOMDocument $source)
{
$xpath = new \DOMXPath($source);
foreach ($xpath->query('//comment()') as $commentNode) {
$commentNode->parentNode->removeChild($commentNode);
}
return $source;
}
/**
* Parse filters from request node configuration.
*
* @param \DOMXPath $xpath XPath access to the document parsed.
* @param \DOMNode $requestRootNode Request node to be parsed.
*
* @return array
*/
private function parseFilters(\DOMXPath $xpath, \DOMNode $requestRootNode)
{
$filters = [];
foreach ($xpath->query(self::FILTERS_PATH, $requestRootNode) as $filterNode) {
$filters[$filterNode->getAttribute('name')] = $filterNode->nodeValue;
}
return $filters;
}
/**
* Parse aggregations from request node configuration.
*
* @param \DOMXPath $xpath XPath access to the document parsed.
* @param \DOMNode $rootNode Request node to be parsed.
*
* @return array
*/
private function parseAggregations(\DOMXPath $xpath, \DOMNode $rootNode)
{
$aggs = [];
foreach ($xpath->query(self::AGGREGATIONS_PATH, $rootNode) as $aggNode) {
$bucketName = $aggNode->getAttribute('name');
$bucketConfig = [];
foreach ($aggNode->attributes as $attribute) {
$bucketConfig[$attribute->name] = $attribute->value;
}
$aggFilters = $this->parseAggregationFilters($xpath, $aggNode);
if (!empty($aggFilters)) {
$bucketConfig['filters'] = $aggFilters;
}
$bucketConfig['childBuckets'] = $this->parseAggregations($xpath, $aggNode);
$bucketConfig['metrics'] = $this->parseMetrics($xpath, $aggNode);
$bucketConfig['pipelines'] = $this->parsePipelines($xpath, $aggNode);
$aggs[$bucketName] = $bucketConfig;
}
return $aggs;
}
/**
* Parse aggregations providers from request node configuration.
*
* @param \DOMXPath $xpath XPath access to the document parsed.
* @param \DOMNode $rootNode Request node to be parsed.
*
* @return array
*/
private function parseAggregationsProviders(\DOMXPath $xpath, \DOMNode $rootNode)
{
$providers = [];
foreach ($xpath->query(self::AGGREGATIONS_PROVIDERS_PATH, $rootNode) as $providerNode) {
$providers[$providerNode->getAttribute('name')] = $providerNode->nodeValue;
}
return $providers;
}
/**
* Parse filters from a bucket.
*
* @param \DOMXPath $xpath XPath access to the document parsed.
* @param \DOMNode $rootNode Aggregation node to be parsed.
*
* @return null|string
*/
private function parseAggregationFilters(\DOMXPath $xpath, \DOMNode $rootNode)
{
$filters = [];
foreach ($xpath->query(self::AGGREGATION_FILTERS_PATH, $rootNode) as $filterNode) {
$filters[$filterNode->getAttribute('name')] = $filterNode->nodeValue;
}
return $filters;
}
/**
* Parse metrics from a bucket.
*
* @param \DOMXPath $xpath XPath access to the document parsed.
* @param \DOMNode $rootNode Aggregation node to be parsed.
*
* @return array
*/
private function parseMetrics(\DOMXPath $xpath, \DOMNode $rootNode)
{
$metrics = [];
foreach ($xpath->query(self::METRICS_PATH, $rootNode) as $metricNode) {
$metric = [];
foreach ($metricNode->attributes as $attribute) {
$metric[$attribute->name] = $attribute->value;
}
$metrics[$metric['name']] = $metric;
}
return $metrics;
}
/**
* Parse pipelines from a bucket.
*
* @param \DOMXPath $xpath XPath access to the document parsed.
* @param \DOMNode $rootNode Aggregation node to be parsed for pipeline aggregations.
*
* @return array
*/
private function parsePipelines(\DOMXPath $xpath, \DOMNode $rootNode)
{
$pipelines = [];
foreach ($xpath->query(self::PIPELINES_PATH, $rootNode) as $pipelineNode) {
$pipeline = [];
foreach ($pipelineNode->attributes as $attribute) {
$pipeline[$attribute->name] = $attribute->value;
}
$pipeline['bucketsPath'] = $this->parsePipelineBucketsPath($xpath, $pipelineNode);
$pipelines[$pipeline['name']] = $pipeline;
}
return $pipelines;
}
/**
* Parse the buckets path of a pipeline aggregation.
*
* @param \DOMXPath $xpath XPath access to the document parsed.
* @param \DOMNode $pipelineNode Pipeline aggregation node to be parsed for buckets path.
*
* @return null|array|string
*/
private function parsePipelineBucketsPath(\DOMXPath $xpath, \DOMNode $pipelineNode)
{
$bucketsPath = null;
foreach ($xpath->query(self::PIPELINE_COMPLEX_BUCKETS_PATH, $pipelineNode) as $bucketsPathMappingNode) {
$paramName = $bucketsPathMappingNode->getAttribute('paramName');
$path = $bucketsPathMappingNode->textContent;
if (empty($paramName) || empty($path)) {
continue;
}
$bucketsPath[$paramName] = $path;
}
if (empty($bucketsPath)) {
foreach ($xpath->query(self::PIPELINE_SIMPLE_BUCKETS_PATH, $pipelineNode) as $bucketsPathNode) {
$bucketsPath = $bucketsPathNode->nodeValue;
}
}
return $bucketsPath;
}
}