Skip to content

Commit

Permalink
Store block values in one field (#168)
Browse files Browse the repository at this point in the history
  • Loading branch information
thomasduenser authored Oct 28, 2022
1 parent 1c82ed9 commit 1693932
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 64 deletions.
8 changes: 4 additions & 4 deletions Search/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,18 +79,18 @@ public function createMetadataField($name)
return new MetadataField($name);
}

public function createMetadataProperty($path)
public function createMetadataProperty($path, $condition = null)
{
return new Property($path);
return new Property($path, $condition);
}

public function createMetadataValue($value)
{
return new Value($value);
}

public function createMetadataExpression($expression)
public function createMetadataExpression($expression, $condition = null)
{
return new Expression($expression);
return new Expression($expression, $condition);
}
}
13 changes: 12 additions & 1 deletion Search/Metadata/Field/Expression.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,18 @@ class Expression implements FieldInterface
*/
private $expression;

/**
* @var string|null
*/
private $condition;

/**
* @param string $expression
*/
public function __construct($expression)
public function __construct($expression, $condition = null)
{
$this->expression = $expression;
$this->condition = $condition;
}

/**
Expand All @@ -41,4 +47,9 @@ public function getExpression()
{
return $this->expression;
}

public function getCondition()
{
return $this->condition;
}
}
13 changes: 12 additions & 1 deletion Search/Metadata/Field/Property.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,18 @@ class Property implements FieldInterface
*/
private $property;

/**
* @var mixed|null
*/
private $condition;

/**
* @param mixed $property
*/
public function __construct($property)
public function __construct($property, $condition = null)
{
$this->property = $property;
$this->condition = $condition;
}

/**
Expand All @@ -41,4 +47,9 @@ public function getProperty()
{
return $this->property;
}

public function getCondition()
{
return $this->condition;
}
}
12 changes: 12 additions & 0 deletions Search/Metadata/FieldEvaluator.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,18 @@ public function getValue($object, FieldInterface $field)
));
}

public function evaluateCondition($object, string $condition)
{
try {
return $this->expressionLanguage->evaluate($condition, $object);
} catch (\Exception $e) {
throw new \RuntimeException(\sprintf(
'Error encountered when evaluating expression "%s"',
$condition
), null, $e);
}
}

/**
* Evaluate a property (using PropertyAccess).
*
Expand Down
165 changes: 108 additions & 57 deletions Search/ObjectToDocumentConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use Massive\Bundle\SearchBundle\Search\Converter\ConverterManagerInterface;
use Massive\Bundle\SearchBundle\Search\Metadata\FieldEvaluator;
use Massive\Bundle\SearchBundle\Search\Metadata\FieldInterface;
use Massive\Bundle\SearchBundle\Search\Metadata\IndexMetadata;

/**
Expand Down Expand Up @@ -110,7 +111,14 @@ public function objectToDocument(IndexMetadata $metadata, $object)
$document->setLocale($locale);
}

$this->populateDocument($document, $object, $fieldMapping);
$blockValues = [];
$this->populateDocument($document, $object, $fieldMapping, $blockValues);

foreach ($blockValues as $name => $values) {
$mapping = $this->addMappingOptions();
$values = \implode(' ', $values);
$this->addDocumentField($document, $name, $values, $mapping, Field::TYPE_STRING);
}

return $document;
}
Expand All @@ -122,35 +130,23 @@ public function objectToDocument(IndexMetadata $metadata, $object)
* @param Document $document
* @param mixed $object
* @param array $fieldMapping
* @param string[] $blockValues
* @param string $prefix Prefix the document field name (used when called recursively)
*
* @throws \InvalidArgumentException
*/
private function populateDocument($document, $object, $fieldMapping, $prefix = '')
{
foreach ($fieldMapping as $fieldName => $mapping) {
$requiredMappings = ['field', 'type'];

foreach ($requiredMappings as $requiredMapping) {
if (!isset($mapping[$requiredMapping])) {
throw new \RuntimeException(
\sprintf(
'Mapping for "%s" does not have "%s" key',
\get_class($document),
$requiredMapping
)
);
}
}
private function populateDocument(
$document,
$object,
$fieldMapping,
&$blockValues = [],
$prefix = ''
) {
$isBlockScope = '' !== $prefix;

$mapping = \array_merge(
[
'stored' => true,
'aggregate' => false,
'indexed' => true,
],
$mapping
);
foreach ($fieldMapping as $fieldName => $mapping) {
$this->hasRequiredMapping($document, $mapping);
$mapping = $this->addMappingOptions($mapping);

if ('complex' == $mapping['type']) {
if (!isset($mapping['mapping'])) {
Expand All @@ -174,15 +170,25 @@ private function populateDocument($document, $object, $fieldMapping, $prefix = '
$document,
$childObject,
$mapping['mapping']->getFieldMapping(),
$prefix . $fieldName . $i
$blockValues,
$prefix . $fieldName . '_'
);
}

continue;
}

$type = $mapping['type'];
$value = $this->fieldEvaluator->getValue($object, $mapping['field']);
/** @var FieldInterface $mappingField */
$mappingField = $mapping['field'];
$condition = method_exists($mappingField, 'getCondition') ? $mappingField->getCondition() : null;
$validField = $condition ? $this->fieldEvaluator->evaluateCondition($object, $condition) : true;

if (false === $validField) {
continue;
}

$value = $this->fieldEvaluator->getValue($object, $mappingField);

if (Field::TYPE_STRING !== $type && Field::TYPE_ARRAY !== $type) {
$value = $this->converterManager->convert($value, $type, $document);
Expand All @@ -199,25 +205,17 @@ private function populateDocument($document, $object, $fieldMapping, $prefix = '
);
}

if (\is_array($value) && (isset($value['value']) || isset($value['fields']))) {
if (!$isBlockScope && \is_array($value) && (isset($value['value']) || isset($value['fields']))) {
if (isset($value['value'])) {
$document->addField(
$this->factory->createField(
$prefix . $fieldName,
$value['value'],
$this->getValueType($value['value']),
$mapping['stored'],
$mapping['indexed'],
$mapping['aggregate']
)
);
$valueType = $this->getValueType($value['value']);
$this->addDocumentField($document, $fieldName, $value['value'], $mapping, $valueType);
}

if (isset($value['fields'])) {
/** @var Field $field */
foreach ($value['fields'] as $field) {
$field = clone $field;
$field->setName($prefix . $fieldName . '#' . $field->getName());
$field->setName($fieldName . '#' . $field->getName());
$document->addField($field);
}
}
Expand All @@ -226,29 +224,82 @@ private function populateDocument($document, $object, $fieldMapping, $prefix = '
}

if ('complex' !== $mapping['type']) {
$document->addField(
$this->factory->createField(
$prefix . $fieldName,
$value,
$type,
$mapping['stored'],
$mapping['indexed'],
$mapping['aggregate']
)
);
if ($isBlockScope && $value && Field::TYPE_STRING === $type) {
$blockValues[$prefix . $fieldName][] = $value;
} elseif (!$isBlockScope) {
$this->addDocumentField($document, $fieldName, $value, $mapping, $type);
}

continue;
}

foreach ($value as $key => $itemValue) {
$document->addField(
$this->factory->createField(
$prefix . $fieldName . $key,
$itemValue,
$mapping['type'],
$mapping['stored'],
$mapping['indexed'],
$mapping['aggregate']
$itemType = $mapping['type'];

if ($isBlockScope && $itemValue && Field::TYPE_STRING === $itemType) {
$blockValues[$prefix . $fieldName . $key][] = $itemValue;
} elseif (!$isBlockScope) {
$this->addDocumentField($document, $fieldName . $key, $itemValue, $mapping, $itemType);
}
}
}
}

/**
* Adds some default mapping options to the given array.
*/
private function addMappingOptions($mapping = []): array
{
return \array_merge(
[
'stored' => true,
'aggregate' => false,
'indexed' => true,
],
$mapping
);
}

/**
* Adds a search field to the Document.
*
* @param Document $document
* @param string $fieldName
* @param mixed $value
* @param array $mapping
* @param string $type
*/
private function addDocumentField($document, $fieldName, $value, $mapping, $type): void
{
$document->addField(
$this->factory->createField(
$fieldName,
$value,
$type,
$mapping['stored'],
$mapping['indexed'],
$mapping['aggregate']
)
);
}

/**
* Checks if all mandatory options are available in the given mapping.
*
* @param Document $document
* @param array $mapping
*/
private function hasRequiredMapping($document, $mapping): void
{
$requiredMappings = ['field', 'type'];

foreach ($requiredMappings as $requiredMapping) {
if (!isset($mapping[$requiredMapping])) {
throw new \RuntimeException(
\sprintf(
'Mapping for "%s" does not have "%s" key',
\get_class($document),
$requiredMapping
)
);
}
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@

"behat/behat": "^3.4.2",

"webmozart/assert": "^1.7"
"webmozart/assert": "^1.7",
"phpspec/prophecy": "^1.15"
},
"suggest": {
"sensio/distribution-bundle": "Required if the SearchScriptHandler is used",
Expand Down

0 comments on commit 1693932

Please sign in to comment.