Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NGSTACK-836: descendant indexing #9

Closed
wants to merge 64 commits into from
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
047fd32
NGSTACK-836 parent child indexer
Mar 22, 2024
14a56dd
NGSTACK-836 remove SearchResultExtractorTrait from ParentChildFieldMa…
Mar 25, 2024
ad0b014
NGSTACK-836 remove argument from service definition
Mar 25, 2024
0aaa15b
NGSTACK-836 add parent child indexing compiler pass
Mar 25, 2024
a57942b
NGSTACK-836 service definition changes
Mar 28, 2024
94ebafb
NGSTACK-836 FulltextFieldResolver interface and remove search from Pa…
Mar 28, 2024
0d993e7
NGSTACK-836 use persistence location handler instead of location service
Mar 28, 2024
c778868
NGSTACK-836 add content id as key to parent ids property and unset pr…
Mar 28, 2024
6ef4212
NGSTACK-836 replace persistance handler with location handler, use in…
Mar 28, 2024
1523364
NGSTACK-836: use class constant instead of FQCN
pspanja May 29, 2024
8837723
NGSTACK-836: add strict types declaration and fix CS
pspanja May 29, 2024
169c715
NGSTACK-836: add property type annotation and fix CS
pspanja May 29, 2024
c0287a8
NGSTACK-836: fix CS
pspanja May 29, 2024
5ff522e
NGSTACK-836: fix CS
pspanja May 29, 2024
ccc7a7b
NGSTACK-836: remove unused imports
pspanja May 29, 2024
f05386f
NGSTACK-836: add strict types declaration and fix CS
pspanja May 29, 2024
b5456ed
NGSTACK-836: use getters instead of magic get and fix CS
pspanja May 29, 2024
6153cd0
NGSTACK-836: fix case mismatch
pspanja May 29, 2024
30b01aa
NGSTACK-836: clean up docblocks
pspanja May 29, 2024
05bcae0
NGSTACK-836: remove unnecessary type cast
pspanja May 29, 2024
3fb1559
NGSTACK-836: clean up docblocks
pspanja May 29, 2024
1e909d7
NGSTACK-836: fix CS
pspanja May 29, 2024
5c9d207
NGSTACK-836: fix spacing
pspanja May 29, 2024
a7e1cd6
NGSTACK-836: move configuration to the bottom
pspanja May 29, 2024
84608e7
NGSTACK-836: fix CS
pspanja May 29, 2024
3f8d403
NGSTACK-836: fix typo
pspanja May 29, 2024
a203e8b
NGSTACK-836: fix async indexing tests and prepare for testing parent-…
pspanja May 29, 2024
db33d05
NGSTACK-836: check and skip root Location
pspanja May 29, 2024
2cd560d
NGSTACK-836: handle exception when resolving content type identifier
pspanja May 29, 2024
4df4ff8
NGSTACK-836: restructure message handlers
pspanja May 29, 2024
ebafc0a
NGSTACK-836: restructure parent-child indexing field mapper
pspanja May 29, 2024
939f68a
NGSTACK-836: restructure parent-child indexing ancestor resolver
pspanja May 29, 2024
3039321
NGSTACK-836: fix test service configuration
pspanja May 29, 2024
23c12b6
NGSTACK-836: add parent-child indexing test for publishing version
pspanja May 29, 2024
ddf2565
NGSTACK-836: fix CS
pspanja May 31, 2024
369e106
NGSTACK-836: update test class name
pspanja May 31, 2024
6a8efc3
NGSTACK-836: rename parent-child indexing fulltext field mapper
pspanja May 31, 2024
15116cf
NGSTACK-836: make parent-child fulltext field mapper configurable
pspanja May 31, 2024
3860b3e
NGSTACK-836: implement recursive semantic config
pspanja Jun 3, 2024
107ec4c
NGSTACK-836: extract AncestorPathGenerator with test
pspanja Jun 4, 2024
bbd3d50
NGSTACK-836: fix indenting
pspanja Jun 6, 2024
ff7083d
NGSTACK-836: fix logged message
pspanja Jun 6, 2024
1dcf42d
NGSTACK-836: add AncestorResolver unit test
pspanja Jun 6, 2024
7b4bd61
NGSTACK-836: disable hierarchical indexing on legacy and asynchronous…
pspanja Jun 6, 2024
75d231f
NGSTACK-836: fix exclude configuration
pspanja Jun 6, 2024
f156dea
NGSTACK-836: fix service configuration
pspanja Jun 6, 2024
92682c8
NGSTACK-836: fix case mismatch
pspanja Jun 6, 2024
c14730b
NGSTACK-836: update for change in configuration
pspanja Jun 6, 2024
9980813
NGSTACK-836: implement configurable Solr field handlers for descendan…
pspanja Jun 7, 2024
88b873c
NGSTACK-836: rename parent child indexing to descendant indexing
pspanja Jun 10, 2024
e09c5d9
NGSTACK-836: rename parent child indexing to descendant indexing part 2
pspanja Jun 10, 2024
16c32fa
NGSTACK-836: add Block and BlockTranslation Descendant FieldMappers
pspanja Jun 11, 2024
8408d9f
NGSTACK-836: rename cleanup
pspanja Jun 11, 2024
6e5ab5c
NGSTACK-836: fix CS
pspanja Jun 11, 2024
1bd6ec2
NGSTACK-836 tests for descendant indexing
Jul 11, 2024
9a4a88a
NGSTACK-836 AncestorIndexer and AncestorResolver changes and swap loc…
Jul 11, 2024
f26849b
NGSTACK-836 remove unused qualifiers
Jul 11, 2024
4fa5771
NGSTACK-836 exclude descendant indexing tests (not supported with leg…
Jul 11, 2024
64247b5
NGSTACK-836 update tests throws tags
Jul 11, 2024
7dfbc42
NGSTACK-836 declare test return types
Jul 12, 2024
b6fc54a
NGSTACK-836 remove unnecessary code
Jul 12, 2024
57c186b
NGSTACK-836 renaming field mappers and custom solr fields
Jul 12, 2024
f18bea2
NGSTACK-836 fix handlers configuration
Jul 18, 2024
32a5b7a
NGSTACK-836 add documentation for descendant indexing
Jul 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions bundle/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public function getConfigTreeBuilder(): TreeBuilder
$this->addIndexableFieldTypeSection($rootNode);
$this->addSearchResultExtractorSection($rootNode);
$this->addAsynchronousIndexingSection($rootNode);
$this->addParentChildIndexingSection($rootNode);


return $treeBuilder;
}
Expand Down Expand Up @@ -73,4 +75,15 @@ private function addAsynchronousIndexingSection(ArrayNodeDefinition $nodeDefinit
->end()
->end();
}

private function addParentChildIndexingSection(ArrayNodeDefinition $nodeDefinition): void
{
$nodeDefinition
->children()
->booleanNode('use_parent_child_indexing')
->info('Use parent child indexing')
->defaultFalse()
->end()
->end();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ private function processExtensionConfiguration(array $configs, ContainerBuilder
$this->processIndexableFieldTypeConfiguration($configuration, $container);
$this->processSearchResultExtractorConfiguration($configuration, $container);
$this->processAsynchronousIndexingConfiguration($configuration, $container);
$this->processParentChildIndexingConfiguration($configuration, $container);
}

private function processSearchResultExtractorConfiguration(array $configuration, ContainerBuilder $container): void
Expand Down Expand Up @@ -117,4 +118,12 @@ private function processAsynchronousIndexingConfiguration(array $configuration,
$configuration['use_asynchronous_indexing'],
);
}

private function processParentChildIndexingConfiguration(array $configuration, ContainerBuilder $container): void
{
$container->setParameter(
'netgen_ibexa_search_extra.use_parent_child_indexing',
$configuration['use_parent_child_indexing'],
);
}
}
2 changes: 2 additions & 0 deletions bundle/NetgenIbexaSearchExtraBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Netgen\Bundle\IbexaSearchExtraBundle;

use Netgen\IbexaSearchExtra\Container\Compiler;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;

Expand All @@ -21,6 +22,7 @@ public function build(ContainerBuilder $container): void
$container->addCompilerPass(new Compiler\AggregateFacetBuilderVisitorPass());
$container->addCompilerPass(new Compiler\AggregateSubdocumentQueryCriterionVisitorPass());
$container->addCompilerPass(new Compiler\AsynchronousIndexingPass());
$container->addCompilerPass(new Compiler\ParentChildIndexingPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 100);
$container->addCompilerPass(new Compiler\FieldType\RichTextIndexablePass());
$container->addCompilerPass(new Compiler\SearchResultExtractorPass());
$container->addCompilerPass(new Compiler\RawFacetBuilderDomainVisitorPass());
Expand Down
1 change: 1 addition & 0 deletions lib/Container/Compiler/AsynchronousIndexingPass.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public function process(ContainerBuilder $container): void
->setDecoratedService(CoreContentEventSubscriber::class)
->setArguments([
new Reference('netgen.ibexa_search_extra.asynchronous_indexing.messenger.bus'),
new Reference('ibexa.api.service.location'),
]);

$container
Expand Down
31 changes: 31 additions & 0 deletions lib/Container/Compiler/ParentChildIndexingPass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

namespace Netgen\IbexaSearchExtra\Container\Compiler;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

class ParentChildIndexingPass implements CompilerPassInterface
{
private const ParentChildIndexerTag = 'netgen.parent_child_indexer.message_handler';
private const MessageHandlerTag = 'messenger.message_handler';

public function process(ContainerBuilder $container) :void
{
$useParentChildIndexing = $container->getParameter(
'netgen_ibexa_search_extra.use_parent_child_indexing',
);

if ($useParentChildIndexing !== true) {
return;
}

$serviceIds =$container->findTaggedServiceIds(self::ParentChildIndexerTag);

foreach ($serviceIds as $serviceId => $tag) {
$definition = $container->getDefinition($serviceId);
$definition->addTag(self::MessageHandlerTag);
}

}
}
18 changes: 10 additions & 8 deletions lib/Core/Search/Common/EventSubscriber/ContentEventSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Ibexa\Contracts\Core\Repository\Events\Content\PublishVersionEvent;
use Ibexa\Contracts\Core\Repository\Events\Content\RevealContentEvent;
use Ibexa\Contracts\Core\Repository\Events\Content\UpdateContentMetadataEvent;
use Ibexa\Contracts\Core\Repository\LocationService;
use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Content\CopyContent;
use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Content\DeleteContent;
use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Content\DeleteTranslation;
Expand All @@ -25,8 +26,11 @@

class ContentEventSubscriber implements EventSubscriberInterface
{
private array $contentParentLocations = [];
public function __construct(
private readonly MessageBusInterface $messageBus,
private readonly LocationService $locationService,

) {}

public static function getSubscribedEvents(): array
Expand Down Expand Up @@ -55,26 +59,24 @@ public function onCopyContent(CopyContentEvent $event): void

public function onBeforeDeleteContent(BeforeDeleteContentEvent $event): void
{
$contentLocations = $this->locationService->loadLocations($event->getContentInfo());
pspanja marked this conversation as resolved.
Show resolved Hide resolved
try {
$event->getContentInfo()->getMainLocation()?->parentLocationId;
foreach ($contentLocations as $contentLocation){
$this->contentParentLocations[] = $contentLocation->parentLocationId;
pspanja marked this conversation as resolved.
Show resolved Hide resolved
}
} catch (Throwable) {
// does nothing
}
}

public function onDeleteContent(DeleteContentEvent $event): void
{
try {
$mainLocationParentLocationId = $event->getContentInfo()->getMainLocation()?->parentLocationId;
} catch (Throwable) {
$mainLocationParentLocationId = null;
}

$parentLocationIds = $this->contentParentLocations ?? [];
$this->messageBus->dispatch(
new DeleteContent(
$event->getContentInfo()->id,
$event->getLocations(),
$mainLocationParentLocationId,
$parentLocationIds,
pspanja marked this conversation as resolved.
Show resolved Hide resolved
),
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ final class DeleteContent
public function __construct(
public readonly int $contentId,
public readonly array $locationIds,
public readonly ?int $mainLocationParentLocationId,
public readonly ?array $parentLocationIds,
pspanja marked this conversation as resolved.
Show resolved Hide resolved
) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

declare(strict_types=1);

namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\ParentChildIndexing;

use Netgen\IbexaSearchExtra\Core\Search\Solr\ParentChildReindexAncestorResolver;
use Ibexa\Contracts\Core\Persistence\Content\Handler as ContentHandler;
use Ibexa\Contracts\Core\Persistence\Content\Location;
use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException;
use Ibexa\Contracts\Core\Search\VersatileHandler;

final class AncestorIndexer
{
public function __construct(
private readonly VersatileHandler $searchHandler,
private readonly ContentHandler $contentHandler,
private readonly ParentChildReindexAncestorResolver $ancestorResolver,
) {}

public function indexSingle(Location $location): void
{
$ancestor = $this->ancestorResolver->resolveAncestor($location);


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

try {
$content = $this->contentHandler->load($ancestor->contentId);
} catch (NotFoundException) {
return;
}

$this->searchHandler->indexContent($content);
$this->searchHandler->indexLocation($ancestor);
}

/**
* @param \Ibexa\Contracts\Core\Persistence\Content\Location $location
*/
public function indexSingleForDeleteContent(Location $location): void
{
$ancestor = $this->ancestorResolver->resolveAncestorForDeleteContent($location);

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

try {
$content = $this->contentHandler->load($ancestor->contentId);
} catch (NotFoundException) {
return;
}

$this->searchHandler->indexContent($content);
$this->searchHandler->indexLocation($ancestor);
}

/**
* @param \Ibexa\Contracts\Core\Persistence\Content\Location[] $locations
*/
public function indexMultiple(array $locations): void
{
foreach ($locations as $location) {
$this->indexSingle($location);
}
}

/***
* @param \Ibexa\Contracts\Core\Persistence\Content\Location[] $locations
*/
public function indexMultipleForDeleteContent(array $locations): void
{
$this->indexMultiple($locations);

foreach ($locations as $location) {
$this->indexSingleForDeleteContent($location);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\ParentChildIndexing\Content;

use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\ParentChildIndexing\AncestorIndexer;
use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as LocationHandler;
use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Content\CopyContent;

final class CopyContentHandler
{
public function __construct(
private readonly LocationHandler $locationHandler,
private readonly AncestorIndexer $ancestorIndexer,
) {}

public function __invoke(CopyContent $message): void
{
$this->ancestorIndexer->indexMultiple(
$this->locationHandler->loadLocationsByContent(
$message->contentId,
),
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

declare(strict_types=1);

namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\ParentChildIndexing\Content;

use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\ParentChildIndexing\AncestorIndexer;
use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as LocationHandler;
use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException;
use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Content\DeleteContent;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;

use function sprintf;

final class DeleteContentHandler
{
public function __construct(
private readonly LocationHandler $locationHandler,
private readonly AncestorIndexer $ancestorIndexer,
private readonly LoggerInterface $logger = new NullLogger(),
) {}

public function __invoke(DeleteContent $message): void
{
if ($message->parentLocationIds === []) {
$this->logger->info(
sprintf(
'%s: Could not find main Location parent Location ID for deleted Content #%d, aborting',
$this::class,
$message->contentId,
),
);

return;
}
$locations = [];
foreach ($message->parentLocationIds as $locationId) {
try {
$locations[] = $this->locationHandler->load($locationId);
} catch (NotFoundException) {
$this->logger->info(
sprintf(
'%s: Location #%d is gone, aborting',
$this::class,
$locationId,
),
);
}
}
$this->ancestorIndexer->indexMultipleForDeleteContent($locations);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

declare(strict_types=1);

namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\ParentChildIndexing\Content;

use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\ParentChildIndexing\AncestorIndexer;
use Ibexa\Contracts\Core\Persistence\Content\ContentInfo;
use Ibexa\Contracts\Core\Persistence\Content\Handler as ContentHandler;
use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as LocationHandler;
use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException;
use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Content\DeleteTranslation;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;

use function sprintf;

final class DeleteTranslationHandler
{
public function __construct(
private readonly ContentHandler $contentHandler,
private readonly LocationHandler $locationHandler,
private readonly AncestorIndexer $ancestorIndexer,
private readonly LoggerInterface $logger = new NullLogger(),
) {}

public function __invoke(DeleteTranslation $message): void
{
try {
$contentInfo = $this->contentHandler->loadContentInfo(
$message->contentId,
);
} catch (NotFoundException) {
$this->logger->info(
sprintf(
'%s: Content #%d is gone, aborting',
$this::class,
$message->contentId,
),
);

return;
}

if ($contentInfo->status !== ContentInfo::STATUS_PUBLISHED) {
return;
}

$this->ancestorIndexer->indexMultiple(
$this->locationHandler->loadLocationsByContent(
$message->contentId,
),
);
}
}
Loading
Loading