Skip to content

Commit

Permalink
feat: initial implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
sitepark-veltrup committed Nov 29, 2023
1 parent fbd5913 commit 702b9d2
Show file tree
Hide file tree
Showing 62 changed files with 3,111 additions and 0 deletions.
20 changes: 20 additions & 0 deletions bin/console
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env php
<?php
// application.php

require __DIR__.'/../vendor/autoload.php';

use Symfony\Component\Config\FileLocator;
use Atoolo\Search\Console\Application;

$container = new Symfony\Component\DependencyInjection\ContainerBuilder();
$loader = new Symfony\Component\DependencyInjection\Loader\YamlFileLoader(
$container,
new FileLocator(__DIR__ . '/../config'));

$loader->load('services.yml');
$container->compile();

$application = $container->get(Application::class);

$application->run();
15 changes: 15 additions & 0 deletions config/services.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
services:
_defaults:
autowire: true
autoconfigure: true
_instanceof:
Symfony\Component\Console\Command\Command:
tags: ['command']

Atoolo\Search\Console\:
resource: '../src/Console'

Atoolo\Search\Console\Application:
public: true
arguments:
- !tagged command
18 changes: 18 additions & 0 deletions src/Console/Application.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace Atoolo\Search\Console;

use Symfony\Component\Console\Application as BaseApplication;

class Application extends BaseApplication
{
public function __construct(iterable $commands = [])

Check failure on line 11 in src/Console/Application.php

View workflow job for this annotation

GitHub Actions / verify / verify

Method Atoolo\Search\Console\Application::__construct() has parameter $commands with no value type specified in iterable type iterable.
{
parent::__construct();
foreach ($commands as $command) {
$this->add($command);
}
}
}
132 changes: 132 additions & 0 deletions src/Console/Command/Indexer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<?php

declare(strict_types=1);

namespace Atoolo\Search\Console\Command;

use Atoolo\Resource\Exception\InvalidResourceException;
use Atoolo\Resource\Loader\SiteKitLoader;
use Atoolo\Resource\Loader\SiteKitNavigationHierarchyLoader;
use Atoolo\Search\Console\Command\Io\IndexerProgressProgressBar;
use Atoolo\Search\Dto\Indexer\IndexerParameter;
use Atoolo\Search\Service\Indexer\SiteKit\DefaultSchema21DocumentEnricher;
use Atoolo\Search\Service\Indexer\SolrIndexer;
use Atoolo\Search\Service\SolrParameterClientFactory;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

#[AsCommand(
name: 'atoolo:indexer',
description: 'Fill a search index'
)]
class Indexer extends Command
{
private IndexerProgressProgressBar $progressBar;
private SymfonyStyle $io;
private string $resourceDir;

protected function configure(): void
{
$this
->setHelp('Command to fill a search index')
->addArgument(
'solr-core',
InputArgument::REQUIRED,
'Solr core to be used.'
)
->addArgument(
'resource-dir',
InputArgument::REQUIRED,
'Resource directory whose data is to be indexed.'
)
->addArgument(
'directories',
InputArgument::OPTIONAL | InputArgument::IS_ARRAY,
'Resources or directories of the resource to be indexed.'
)
->addOption(
'cleanup-threshold',
null,
InputArgument::OPTIONAL,
'Specifies the number of indexed documents from ' .
'which indexing is considered successful and old entries ' .
'can be deleted. Is only used for full indexing.',
0
)
;
}

protected function execute(
InputInterface $input,
OutputInterface $output
): int {

$this->io = new SymfonyStyle($input, $output);
$this->progressBar = new IndexerProgressProgressBar($output);
$this->resourceDir = $input->getArgument('resource-dir');

Check failure on line 70 in src/Console/Command/Indexer.php

View workflow job for this annotation

GitHub Actions / verify / verify

Property Atoolo\Search\Console\Command\Indexer::$resourceDir (string) does not accept mixed.
$directories = $input->getArgument('directories');

$cleanupThreshold = empty($directories)
? $input->getArgument('cleanup-threshold')
: 0;

if (empty($directories)) {
$this->io->title('Index all resources');
} else {
$this->io->title('Index resources subdirectories');
$this->io->listing($directories);

Check failure on line 81 in src/Console/Command/Indexer.php

View workflow job for this annotation

GitHub Actions / verify / verify

Parameter #1 $elements of method Symfony\Component\Console\Style\SymfonyStyle::listing() expects array, mixed given.
}

$parameter = new IndexerParameter(
$input->getArgument('solr-core'),

Check failure on line 85 in src/Console/Command/Indexer.php

View workflow job for this annotation

GitHub Actions / verify / verify

Parameter #1 $coreId of class Atoolo\Search\Dto\Indexer\IndexerParameter constructor expects string, mixed given.
$this->resourceDir,

Check failure on line 86 in src/Console/Command/Indexer.php

View workflow job for this annotation

GitHub Actions / verify / verify

Parameter #2 $basePath of class Atoolo\Search\Dto\Indexer\IndexerParameter constructor expects string, mixed given.
$cleanupThreshold,

Check failure on line 87 in src/Console/Command/Indexer.php

View workflow job for this annotation

GitHub Actions / verify / verify

Parameter #3 $cleanupThreshold of class Atoolo\Search\Dto\Indexer\IndexerParameter constructor expects int, mixed given.
$directories

Check failure on line 88 in src/Console/Command/Indexer.php

View workflow job for this annotation

GitHub Actions / verify / verify

Parameter #4 $directories of class Atoolo\Search\Dto\Indexer\IndexerParameter constructor expects array, mixed given.
);

$indexer = $this->createIndexer();
$indexer->index($parameter);

$this->errorReport();

return Command::SUCCESS;
}

protected function errorReport(): void
{
foreach ($this->progressBar->getErrors() as $error) {
if ($error instanceof InvalidResourceException) {
$this->io->error(
$error->getLocation() . ': ' .
$error->getMessage()
);
} else {
$this->io->error($error->getMessage());
}
}
}

protected function createIndexer(): SolrIndexer
{
$resourceLoader = new SiteKitLoader($this->resourceDir);
$navigationLoader = new SiteKitNavigationHierarchyLoader(
$resourceLoader
);
$schema21 = new DefaultSchema21DocumentEnricher(
$navigationLoader
);

$clientFactory = new SolrParameterClientFactory();
return new SolrIndexer(
[$schema21],
$this->progressBar,
$resourceLoader,
$clientFactory,
'internal'
);
}
}
64 changes: 64 additions & 0 deletions src/Console/Command/Io/IndexerProgressProgressBar.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

declare(strict_types=1);

namespace Atoolo\Search\Console\Command\Io;

use Atoolo\Search\Service\Indexer\IndexerProgressHandler;
use Exception;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Output\OutputInterface;

class IndexerProgressProgressBar implements IndexerProgressHandler
{
private OutputInterface $output;
private ProgressBar $progressBar;

private array $errors = [];

Check failure on line 17 in src/Console/Command/Io/IndexerProgressProgressBar.php

View workflow job for this annotation

GitHub Actions / verify / verify

Property Atoolo\Search\Console\Command\Io\IndexerProgressProgressBar::$errors type has no value type specified in iterable type array.

public function __construct(OutputInterface $output)
{
$this->output = $output;
}

public function start(int $total): void
{
$this->progressBar = new ProgressBar($this->output, $total);
$this->formatProgressBar('green');
}

public function advance(int $step): void
{
$this->progressBar->advance($step);
}

private function formatProgressBar(string $color): void
{
$this->progressBar->setBarCharacter('<fg=' . $color . '>•</>');
$this->progressBar->setEmptyBarCharacter('<fg=' . $color . '>⚬</>');
$this->progressBar->setProgressCharacter('<fg=' . $color . '>➤</>');
$this->progressBar->setFormat(
"%current%/%max% [%bar%] %percent:3s%%\n" .
" %estimated:-20s% %memory:20s%"
);
}

public function error(Exception $exception): void
{
$this->formatProgressBar('red');
$this->errors[] = $exception;
}

public function finish(): void
{
$this->progressBar->finish();
}

/**
* @return array<Exception>
*/
public function getErrors(): array
{
return $this->errors;
}
}
114 changes: 114 additions & 0 deletions src/Console/Command/MoreLikeThis.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
<?php

declare(strict_types=1);

namespace Atoolo\Search\Console\Command;

use Atoolo\Resource\Loader\SiteKitLoader;
use Atoolo\Search\Dto\Search\Query\MoreLikeThisQuery;
use Atoolo\Search\Dto\Search\Result\ResourceSearchResult;
use Atoolo\Search\Service\Search\ExternalResourceFactory;
use Atoolo\Search\Service\Search\InternalMediaResourceFactory;
use Atoolo\Search\Service\Search\InternalResourceFactory;
use Atoolo\Search\Service\Search\SolrMoreLikeThis;
use Atoolo\Search\Service\Search\SolrResultToResourceResolver;
use Atoolo\Search\Service\SolrParameterClientFactory;
use Psr\Log\NullLogger;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

#[AsCommand(
name: 'atoolo:mlt',
description: 'Performs a more-like-this search'
)]
class MoreLikeThis extends Command
{
private SymfonyStyle $io;
private string $solrCore;
private string $resourceDir;

protected function configure(): void
{
$this
->setHelp('Command to performs a more-like-this search')
->addArgument(
'solr-core',
InputArgument::REQUIRED,
'Solr core to be used.'
)
->addArgument(
'resource-dir',
InputArgument::REQUIRED,
'Resource directory whose data is to be indexed.'
)
->addArgument(
'location',
InputArgument::REQUIRED,
'Resource directory whose data is to be indexed.'
)
;
}

protected function execute(
InputInterface $input,
OutputInterface $output
): int {

$this->io = new SymfonyStyle($input, $output);

$this->solrCore = $input->getArgument('solr-core');

Check failure on line 63 in src/Console/Command/MoreLikeThis.php

View workflow job for this annotation

GitHub Actions / verify / verify

Property Atoolo\Search\Console\Command\MoreLikeThis::$solrCore (string) does not accept mixed.
$this->resourceDir = $input->getArgument('resource-dir');

Check failure on line 64 in src/Console/Command/MoreLikeThis.php

View workflow job for this annotation

GitHub Actions / verify / verify

Property Atoolo\Search\Console\Command\MoreLikeThis::$resourceDir (string) does not accept mixed.
$location = $input->getArgument('location');

$searcher = $this->createSearcher();
$query = $this->buildQuery($location);
$result = $searcher->moreLikeThis($query);
$this->outputResult($result);

return Command::SUCCESS;
}

protected function createSearcher(): SolrMoreLikeThis
{
$resourceLoader = new SiteKitLoader($this->resourceDir);
$clientFactory = new SolrParameterClientFactory();
$resourceFactoryList = [
new ExternalResourceFactory(),
new InternalResourceFactory($resourceLoader),
new InternalMediaResourceFactory($resourceLoader)
];
$solrResultToResourceResolver = new SolrResultToResourceResolver(
$resourceFactoryList
);

return new SolrMoreLikeThis(
$clientFactory,
$solrResultToResourceResolver
);
}

protected function buildQuery(string $location): MoreLikeThisQuery
{
$filterList = [];
return new MoreLikeThisQuery(
$this->solrCore,
$location,
$filterList,
5,
['content']
);
}

protected function outputResult(ResourceSearchResult $result): void
{
$this->io->text($result->getTotal() . " Results:");
foreach ($result as $resource) {
$this->io->text($resource->getLocation());
}
$this->io->text('Query-Time: ' . $result->getQueryTime() . 'ms');
}
}
Loading

0 comments on commit 702b9d2

Please sign in to comment.