Skip to content

Commit

Permalink
Merge pull request #14 from netgen/xmltext_richtext_string_limit
Browse files Browse the repository at this point in the history
Implement local Indexables for XmlText and RichText
  • Loading branch information
pspanja committed Jan 16, 2019
2 parents b59828b + 4aec874 commit 0ce76b3
Show file tree
Hide file tree
Showing 15 changed files with 576 additions and 5 deletions.
6 changes: 5 additions & 1 deletion .travis.yml
Expand Up @@ -11,14 +11,18 @@ cache:
matrix:
fast_finish: true
include:
- php: 7.1
env: TEST_CONFIG="phpunit.xml"
- php: 7.2
env: TEST_CONFIG="phpunit.xml"
- php: 7.1
env: TEST_CONFIG="phpunit-integration-legacy.xml"
- php: 7.2
env: TEST_CONFIG="phpunit-integration-legacy.xml"
- php: 7.1
env: TEST_CONFIG="phpunit-integration-solr.xml" SOLR_VERSION="6.6.5" CORES_SETUP="single" SOLR_CORES="collection1"
- php: 7.1
env: TEST_CONFIG="phpunit-integration-solr.xml" SOLR_VERSION="6.6.5" CORES_SETUP="shared" COMPOSER_REQUIRE="ezsystems/ezpublish-kernel:~6.7.4@dev"
env: TEST_CONFIG="phpunit-integration-solr.xml" SOLR_VERSION="6.6.5" CORES_SETUP="shared" COMPOSER_REQUIRE="ezsystems/ezpublish-kernel:~6.13.6@dev"
- php: 7.2
env: TEST_CONFIG="phpunit-integration-solr.xml" SOLR_VERSION="6.6.5" CORES_SETUP="dedicated"
- php: 7.2
Expand Down
2 changes: 2 additions & 0 deletions README.md
Expand Up @@ -49,6 +49,8 @@ for more details.
- [`Random`](https://github.com/netgen/ezplatform-search-extra/blob/master/lib/API/Values/Content/Query/SortClause/Random.php) sort clause (`solr`)

Provides a way to sort Content randomly.

- [`Indexable`](https://github.com/netgen/ezplatform-search-extra/blob/master/lib/Core/FieldType/XmlText/Indexable.php) implementation for `XmlText` that shortens text indexed as keyword to 256 characters, to avoid failure

## Installation

Expand Down
66 changes: 66 additions & 0 deletions bundle/DependencyInjection/Configuration.php
@@ -0,0 +1,66 @@
<?php

namespace Netgen\Bundle\EzPlatformSearchExtraBundle\DependencyInjection;

use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;

class Configuration implements ConfigurationInterface
{
/**
* @var string
*/
protected $rootNodeName;

/**
* @param string $rootNodeName
*/
public function __construct($rootNodeName)
{
$this->rootNodeName = $rootNodeName;
}

public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root($this->rootNodeName);

$this->addIndexableFieldTypeSection($rootNode);

return $treeBuilder;
}

private function addIndexableFieldTypeSection(ArrayNodeDefinition $nodeDefinition)
{
$nodeDefinition
->children()
->arrayNode('indexable_field_type')
->info('Configure override for field type Indexable interface implementation')
->addDefaultsIfNotSet()
->children()
->arrayNode('ezxmltext')
->addDefaultsIfNotSet()
->canBeDisabled()
->children()
->integerNode('short_text_limit')
->info("Maximum number of characters for the indexed short text ('value' string type field)")
->defaultValue(256)
->end()
->end()
->end()
->arrayNode('ezrichtext')
->addDefaultsIfNotSet()
->canBeDisabled()
->children()
->integerNode('short_text_limit')
->info("Maximum number of characters for the indexed short text ('value' string type field)")
->defaultValue(256)
->end()
->end()
->end()
->end()
->end()
->end();
}
}
Expand Up @@ -9,6 +9,16 @@

class NetgenEzPlatformSearchExtraExtension extends Extension
{
public function getAlias()
{
return 'netgen_ez_platform_search_extra';
}

public function getConfiguration(array $config, ContainerBuilder $container)
{
return new Configuration($this->getAlias());
}

/**
* @param array $configs
* @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
Expand All @@ -34,5 +44,35 @@ public function load(array $configs, ContainerBuilder $container)

$loader->load('search/common.yml');
$loader->load('persistence.yml');

$this->processExtensionConfiguration($configs, $container);
}

private function processExtensionConfiguration(array $configs, ContainerBuilder $container)
{
$configuration = $this->getConfiguration($configs, $container);

if ($configuration === null) {
return;
}

$configuration = $this->processConfiguration($configuration, $configs);

$container->setParameter(
'netgen_ez_platform_search_extra.indexable_field_type.ezrichtext.enabled',
$configuration['indexable_field_type']['ezrichtext']['enabled']
);
$container->setParameter(
'netgen_ez_platform_search_extra.indexable_field_type.ezrichtext.short_text_limit',
$configuration['indexable_field_type']['ezrichtext']['short_text_limit']
);
$container->setParameter(
'netgen_ez_platform_search_extra.indexable_field_type.ezxmltext.enabled',
$configuration['indexable_field_type']['ezxmltext']['enabled']
);
$container->setParameter(
'netgen_ez_platform_search_extra.indexable_field_type.ezxmltext.short_text_limit',
$configuration['indexable_field_type']['ezxmltext']['short_text_limit']
);
}
}
2 changes: 2 additions & 0 deletions bundle/NetgenEzPlatformSearchExtraBundle.php
Expand Up @@ -18,5 +18,7 @@ public function build(ContainerBuilder $containerBuilder)
$containerBuilder->addCompilerPass(new Compiler\AggregateContentSubdocumentMapperPass());
$containerBuilder->addCompilerPass(new Compiler\AggregateContentTranslationSubdocumentMapperPass());
$containerBuilder->addCompilerPass(new Compiler\AggregateSubdocumentQueryCriterionVisitorPass());
$containerBuilder->addCompilerPass(new Compiler\FieldType\RichTextIndexablePass());
$containerBuilder->addCompilerPass(new Compiler\FieldType\XmlTextIndexablePass());
}
}
7 changes: 5 additions & 2 deletions composer.json
Expand Up @@ -11,10 +11,12 @@
],
"require": {
"ezsystems/ezpublish-kernel": "^5.4.11||^6.0||^7.0",
"ext-json": "*"
"ext-json": "*",
"ext-dom": "*"
},
"require-dev": {
"ezsystems/ezplatform-solr-search-engine": "^1.4",
"ezsystems/ezplatform-xmltext-fieldtype": "^1.7",
"roave/security-advisories": "dev-master",
"phpunit/phpunit": "^5.7||^6.0||^7.0",
"matthiasnoback/symfony-dependency-injection-test": "~1.0||~3.0"
Expand All @@ -31,7 +33,8 @@
},
"autoload-dev": {
"psr-4": {
"Netgen\\EzPlatformSearchExtra\\Tests\\": "tests/lib"
"Netgen\\EzPlatformSearchExtra\\Tests\\": "tests/lib",
"Netgen\\EzPlatformSearchExtraBundle\\Tests\\": "tests/bundle"
}
},
"extra": {
Expand Down
55 changes: 55 additions & 0 deletions lib/Container/Compiler/FieldType/RichTextIndexablePass.php
@@ -0,0 +1,55 @@
<?php

namespace Netgen\EzPlatformSearchExtra\Container\Compiler\FieldType;

use EzSystems\EzPlatformRichText\eZ\FieldType\RichText\SearchField;
use Netgen\EzPlatformSearchExtra\Core\FieldType\XmlText\Indexable as IndexableXmlText;
use RuntimeException;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

class RichTextIndexablePass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$enabled = $container->getParameter('netgen_ez_platform_search_extra.indexable_field_type.ezrichtext.enabled');
$shortTextLimit = $container->getParameter('netgen_ez_platform_search_extra.indexable_field_type.ezrichtext.short_text_limit');

if ($enabled === true) {
$this->redefineIndexableImplementation($container, $shortTextLimit);
}
}

private function redefineIndexableImplementation(ContainerBuilder $container, $shortTextLimit)
{
try {
$originalServiceId = $this->getOriginalServiceId($container);
} catch (RuntimeException $e) {
return;
}

$definition = $container->findDefinition($originalServiceId);

$definition->setClass(IndexableXmlText::class);
$definition->setArgument(0, $shortTextLimit);
$definition->addTag('ezpublish.fieldType.indexable', ['alias' => 'ezrichtext']);

$container->setDefinition($originalServiceId, $definition);
}

private function getOriginalServiceId(ContainerBuilder $container)
{
$newServiceId = SearchField::class;
$oldServiceId = 'ezpublish.fieldType.indexable.ezrichtext';

if ($container->has($newServiceId)) {
return $newServiceId;
}

if ($container->has($oldServiceId)) {
return $oldServiceId;
}

throw new RuntimeException('Could not find Indexable service');
}
}
37 changes: 37 additions & 0 deletions lib/Container/Compiler/FieldType/XmlTextIndexablePass.php
@@ -0,0 +1,37 @@
<?php

namespace Netgen\EzPlatformSearchExtra\Container\Compiler\FieldType;

use Netgen\EzPlatformSearchExtra\Core\FieldType\XmlText\Indexable as IndexableXmlText;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

class XmlTextIndexablePass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$enabled = $container->getParameter('netgen_ez_platform_search_extra.indexable_field_type.ezxmltext.enabled');
$shortTextLimit = $container->getParameter('netgen_ez_platform_search_extra.indexable_field_type.ezxmltext.short_text_limit');

if ($enabled === true) {
$this->redefineIndexableImplementation($container, $shortTextLimit);
}
}

private function redefineIndexableImplementation(ContainerBuilder $container, $shortTextLimit)
{
$originalServiceId = 'ezpublish.fieldType.indexable.ezxmltext';

if (!$container->has($originalServiceId)) {
return;
}

$definition = $container->findDefinition($originalServiceId);

$definition->setClass(IndexableXmlText::class);
$definition->setArgument(0, $shortTextLimit);
$definition->addTag('ezpublish.fieldType.indexable', ['alias' => 'ezxmltext']);

$container->setDefinition($originalServiceId, $definition);
}
}
98 changes: 98 additions & 0 deletions lib/Core/FieldType/RichText/Indexable.php
@@ -0,0 +1,98 @@
<?php

namespace Netgen\EzPlatformSearchExtra\Core\FieldType\RichText;

use eZ\Publish\SPI\Persistence\Content\Field;
use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition;
use eZ\Publish\SPI\FieldType\Indexable as IndexableInterface;
use eZ\Publish\SPI\Search;
use DOMDocument;
use DOMNode;

/**
* Indexable definition for RichText field type.
*/
final class Indexable implements IndexableInterface
{
/**
* @var int
*/
private $shortTextMaxLength;

public function __construct($shortTextMaxLength = 256)
{
$this->shortTextMaxLength = $shortTextMaxLength;
}

public function getIndexData(Field $field, FieldDefinition $fieldDefinition)
{
$document = new DOMDocument();
$document->loadXML($field->value->data);
$text = $this->extractText($document->documentElement);
$shortText = $this->shortenText($text);

return [
new Search\Field(
'fulltext',
$text,
new Search\FieldType\FullTextField()
),
new Search\Field(
'value',
$shortText,
new Search\FieldType\StringField()
),
];
}

/**
* Extracts text content of the given $node.
*
* @param \DOMNode $node
*
* @return string
*/
private function extractText(DOMNode $node)
{
$text = '';

if ($node->childNodes) {
foreach ($node->childNodes as $child) {
$text .= $this->extractText($child);
}
} else {
$text .= $node->nodeValue . ' ';
}

return $text;
}

/**
* Shorten text from the given $text.
*
* @param string $text
*
* @return string
*/
private function shortenText($text)
{
return mb_substr(trim(strtok($text, "\r\n")), 0, $this->shortTextMaxLength);
}

public function getIndexDefinition()
{
return [
'value' => new Search\FieldType\StringField(),
];
}

public function getDefaultMatchField()
{
return 'value';
}

public function getDefaultSortField()
{
return $this->getDefaultMatchField();
}
}

0 comments on commit 0ce76b3

Please sign in to comment.