Skip to content

Commit

Permalink
[TASK] Initial release
Browse files Browse the repository at this point in the history
  • Loading branch information
georgringer committed Oct 15, 2024
1 parent 3a38933 commit e6a9876
Show file tree
Hide file tree
Showing 31 changed files with 2,309 additions and 2 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/Todos.md
/Resources/Private/Backup
33 changes: 33 additions & 0 deletions Classes/Api/Client.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);

namespace StudioMitte\TypesenseSearch\Api;

use StudioMitte\TypesenseSearch\Configuration\ConfigurationManager;
use Symfony\Component\HttpClient\HttplugClient;
use TYPO3\CMS\Core\Site\Entity\Site;

class Client
{


public function get(Site $site): \Typesense\Client
{
$configurationManager = new ConfigurationManager();
$authentication = $configurationManager->getAuthentication($site);
return new \Typesense\Client(
[
'api_key' => $authentication->apiKeyWrite,
'nodes' => [
[
'host' => $authentication->host,
'port' => $authentication->port,
'protocol' => $authentication->protocol,
],
],
'client' => new HttplugClient(),
]
);
}

}
110 changes: 110 additions & 0 deletions Classes/Backend/ItemsProcFunc.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?php
declare(strict_types=1);

namespace StudioMitte\TypesenseSearch\Backend;

use StudioMitte\TypesenseSearch\Api\Client;
use StudioMitte\TypesenseSearch\Configuration\ConfigurationManager;
use TYPO3\CMS\Core\Site\Entity\Site;
use TYPO3\CMS\Core\Utility\GeneralUtility;

class ItemsProcFunc
{
protected readonly ConfigurationManager $configurationManager;

public function __construct()
{
$this->configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
}

public function getAllProfilesOfSite(array &$config): void
{
$site = $this->getSite($config);

$configurations = $this->configurationManager->getAllBySite($site);
foreach ($configurations as $configuration) {
$additionalProfile = [
$configuration->profile->getFullLabel(),
$configuration->profile->identifier,
];
$config['items'][] = $additionalProfile;
}
}

public function getQueryByFieldsOfCollection(array &$config): void
{
$collection = $this->getCollection($config);
if (!$collection) {
return;
}

$fields = array_filter($collection['fields'], static function ($facet) {
return ($facet['index'] === true && !in_array($facet['name'], ['pid', 'url', 'embedding', '_geoloc', 'sitelanguage', 'languageid', 'site', 'uid', 'site'], true) && !str_starts_with($facet['name'], '.*'));
});
foreach ($fields as $field) {
$config['items'][] = [
sprintf('%s [%s]', $field['name'], $field['type']),
$field['name'],
];
}
}

public function getAllFacetFieldsOfCollection(array &$config): void
{
$collection = $this->getCollection($config);
if (!$collection) {
return;
}

$fields = array_filter($collection['fields'], static function ($facet) {
return ($facet['facet'] === true && !str_starts_with($facet['name'], '.*'));
});
foreach ($fields as $field) {
$config['items'][] = [
sprintf('%s [%s]', $field['name'], $field['type']),
$field['name'],
];
}
}

private function getSite(array $config): Site
{
/** @var Site $site */
$site = $config['site'] ?? null;
if (!$site) {
// todo error handling: flash message, logging
die('no site!');
}
return $site;
}

private function getProfile(array $config): ?string
{
$selectedProfile = null;
if (isset($config['row']['settings.profile'])) {
if (is_array($config['row']['settings.profile'])) {
$selectedProfile = $config['row']['settings.profile'][0] ?? '';
} else {
$selectedProfile = $config['row']['settings.profile'] ?? '';
}
}
return $selectedProfile;
}

private function getCollection(array $config): array
{
$site = $this->getSite($config);
$selectedProfile = $this->getProfile($config);

if (!$site || !$selectedProfile) {
// todo flash msg
return [];
}

$configuration = $this->configurationManager->getBySite($site, $selectedProfile);

$client = (new Client())->get($site);
$collection = $client->collections[$configuration->profile->collection]->retrieve();
return $collection;
}
}
191 changes: 191 additions & 0 deletions Classes/Command/BasicSetupCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
<?php

declare(strict_types=1);

namespace StudioMitte\TypesenseSearch\Command;

use StudioMitte\TypesenseSearch\Api\Client;
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 TYPO3\CMS\Core\Site\SiteFinder;
use TYPO3\CMS\Core\Utility\GeneralUtility;

class BasicSetupCommand extends Command
{
protected function configure()
{
$this
->addArgument(
'siteIdentifier',
InputArgument::REQUIRED,
'SiteIdentifier',
);
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$site = GeneralUtility::makeInstance(SiteFinder::class)->getSiteByIdentifier($input->getArgument('siteIdentifier'));
$client = new Client();
$typesense = $client->get($site);
$this->addGlobalGollection($typesense);
return 0;
}

private function addGlobalGollection(\Typesense\Client $client)
{
try {
$client->collections['global']->delete();
} catch (\Exception $e) {
// Ignore
}
$schema = [
'name' => 'global',
'fields' => [
// core fields
[
'name' => 'uid',
'type' => 'int64',
],
[
'name' => 'pid',
'type' => 'int64',
],
[
'name' => 'site',
'type' => 'string',
'facet' => true,
],
[
'name' => 'sitelanguage',
'type' => 'string',
'facet' => true,
],
[
'name' => 'languageid',
'type' => 'int32',
'facet' => true,
'optional' => true,
],
[
'name' => 'table',
'type' => 'string',
'facet' => true,
],
// content
[
'name' => 'title',
'type' => 'string',
'optional' => true,
],
[
'name' => 'url',
'type' => 'string',
'optional' => true,
],
[
'name' => 'teaser',
'type' => 'string',
'optional' => true,
],
[
'name' => 'content',
'type' => 'string',
'optional' => true,
],
[
'name' => 'content_full',
'type' => 'string',
'optional' => true,
'index' => false,
],
// basic helpful
[
'name' => 'tree.lvl0',
'type' => 'string',
'optional' => true,
'facet' => true,
],
[
'name' => 'tree.lvl1',
'type' => 'string',
'optional' => true,
'facet' => true,
],
[
'name' => 'tree.lvl2',
'type' => 'string',
'optional' => true,
'facet' => true,
],
[
'name' => 'tree.lvl3',
'type' => 'string',
'optional' => true,
'facet' => true,
],
// dynamic fields
[
'name' => '.*_string_facet',
'type' => 'string',
'optional' => true,
'facet' => true,
],
[
'name' => '.*_string',
'type' => 'string',
'optional' => true,
],
[
'name' => '.*_int_facet',
'type' => 'int64',
'optional' => true,
'facet' => true,
],
[
'name' => '.*_int',
'type' => 'int64',
'optional' => true,
],
[
'name' => '.*_bool_facet',
'type' => 'bool',
'optional' => true,
'facet' => true,
],
[
'name' => '.*_bool',
'type' => 'bool',
'optional' => true,
],
// embedding
[
'name' => 'embedding',
'type' => 'float[]',
'embed' => [
'from' => [
'title',
'teaser',
'content',
],
'model_config' => [
'model_name' => 'ts/e5-small',
],
],
],
[
'name' => '_geoloc',
'type' => 'geopoint',
'optional' => true,
'facet' => true,
],
],
// 'default_sorting_field' => 'num_employees',
];

$response = $client->collections->create($schema);
// $response = $client->collections['products']->update($schema);
// print_R($response);
}
}
51 changes: 51 additions & 0 deletions Classes/Command/SitemapFetchCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

declare(strict_types=1);

namespace StudioMitte\TypesenseSearch\Command;

use StudioMitte\TypesenseSearch\Indexer\SitemapIndexer;
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;
use TYPO3\CMS\Core\Site\SiteFinder;
use TYPO3\CMS\Core\Utility\GeneralUtility;

class SitemapFetchCommand extends Command
{
protected function configure()
{
$this
->addArgument(
'siteIdentifier',
InputArgument::REQUIRED,
'SiteIdentifier',
)
->addArgument(
'url',
InputArgument::REQUIRED,
'Sitemap URL',
)
->addArgument(
'token',
InputArgument::OPTIONAL,
'Token (not yet used)'
);

}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$site = GeneralUtility::makeInstance(SiteFinder::class)->getSiteByIdentifier($input->getArgument('siteIdentifier'));

$sitemapIndexer = GeneralUtility::makeInstance(SitemapIndexer::class);
$count = $sitemapIndexer->index($site, $input->getArgument('url'), $input->getArgument('token'));
$io = new SymfonyStyle($input, $output);
$io->success(sprintf('Indexed %d pages', $count));

return 0;
}

}
Loading

0 comments on commit e6a9876

Please sign in to comment.