TYPO3 Indexed Search mit
Segmentierung/Indexierung

In diesem Blogbeitrag will ich Euch den Einsatz der Such-Erweiterung "indexed_search" zeigen und anhand eines Beispiels näher bringen. Wir werden uns dabei sowohl die Backend-Seitige Konfiguration, wie auch die Entwicklung, den Indexer und Konfiguration ansehen. Seit TYPO3 9 ist "indexed_search" teil des TYPO3-Cores. 

Mithilfe der Erweiterung können einfache (https://metallmaschinen.at/suche)  bis hin zu sehr komplexen Suchen (https://www.vkbimmobilien.at/suchergebnisse/#q=neu) auf TYPO3-Websites realisiert werden. Auf letzerer Seite haben wir eine Fragmentierung in 3 Bereiche:

  • Immobilien (Extbase-Objekte)
  • Inhaltsseiten (Normale Seiten)
  • Blog (Unsere Blogartikel sind auch als normale Seiten umgesetzt)

Die Unterscheidung gelingt anhand FreeIndexUid, damit werden die verschiedenen Typen segmentiert.

TYPO3 Indexed Search mit Segmentierung/Indexierung

Zuerst muss die Indizierung aktiviert werden:

config {  index_enable = 1  index_externals = 1  index_metatags = 1}

Nun wird indexed_search konfiguriert:

####################################################-INDEXED SEARCH CONFIGURATION-##################################################### plugin {  tx_indexedsearch {    view{      templateRootPaths.20 = EXT:xxx/Resources/Private/Templates/IndexedSearch/      partialRootPaths.20 = EXT:xxx/Resources/Private/Partials/IndexedSearch/      partialRootPaths.40 = EXT:xxx/Resources/Private/Partials/      layoutRootPaths.20 = EXT:xxx/Resources/Private/Layouts/    }     settings{      displayLevel1Sections = 0      defaultFreeIndexUidList = 1,2,3      rootPidList = 1      displayRules = 0       results {        titleCropAfter = 50        summaryCropAfter = 100        markupSW_summaryMax = 200      }       blind {        defaultOperand = 1        searchType = 1        sections = 1        freeIndexUid = 0        .......      }    }  }}

Crawler configuration (Indizierungseinstellungen)

Im TYPO3-Backend müssen nun die Indizierungseinstellungen erstellt werden (auf der HOME-Seite):

TYPO3 Indexed Search mit Segmentierung/Indexierung

Und auch noch die Indizierungseinstellung für die Artikelseiten. 

TYPO3 Indexed Search mit Segmentierung/Indexierung

Nun müssen wir die Seiten noch aufrufen, damit der Index auch richtig aufgebaut wird. Dies kann entweder über die crawler Erweiterung erfolgen oder aber über einen eigenen Task. Die crawler-Extension (Site Crawler) ist zurzeit leider noch nicht TYPO3 9 kompatibel und auch fragwürdig, ob das überhaupt noch passiert. Theoretisch wird der Index auch aufgebaut, wenn man die Seite aufruft (Achtung: Dies passiert nicht, wenn man im Backend angemeldet ist).  

UPDATE (02.06.2020): Die crawler-Extension ist mittlerweise für TYPO3 9 und 10 erschienen: https://extensions.typo3.org/extension/crawler.

Eigener Indexer/Crawler

Wir machen es immer so, dass wir einmal in der Nacht alle caches/indexes löschen und den gesamten Index/Cache wieder neu aufbauen. Dazu haben wir uns einen eigenen Task für den Scheduler erstellt. 

public function execute() {    $objectManager =        \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\ObjectManager::class);    $propertyRepository = $objectManager->get(\Varioous\VaVkbImmo\Domain\Repository\PropertyRepository::class);    $persistenceManager = $objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager::class);             $baseUrl = self::getBaseUrl();     //empty cache tables    GeneralUtility::makeInstance(ConnectionPool::class)        ->getConnectionForTable('cache_treelist')        ->truncate('cache_treelist');    GeneralUtility::makeInstance(ConnectionPool::class)        ->getConnectionForTable('cf_cache_hash')        ->truncate('cf_cache_hash');    GeneralUtility::makeInstance(ConnectionPool::class)        ->getConnectionForTable('cf_cache_hash_tags')        ->truncate('cf_cache_hash_tags');    GeneralUtility::makeInstance(ConnectionPool::class)        ->getConnectionForTable('cf_cache_imagesizes')        ->truncate('cf_cache_imagesizes');    GeneralUtility::makeInstance(ConnectionPool::class)        ->getConnectionForTable('cf_cache_imagesizes_tags')        ->truncate('cf_cache_imagesizes_tags');    GeneralUtility::makeInstance(ConnectionPool::class)        ->getConnectionForTable('cf_cache_pages')        ->truncate('cf_cache_pages');    GeneralUtility::makeInstance(ConnectionPool::class)        ->getConnectionForTable('cf_cache_pagesection')        ->truncate('cf_cache_pagesection');    GeneralUtility::makeInstance(ConnectionPool::class)        ->getConnectionForTable('cf_cache_pagesection_tags')        ->truncate('cf_cache_pagesection_tags');    GeneralUtility::makeInstance(ConnectionPool::class)        ->getConnectionForTable('cf_cache_pages_tags')        ->truncate('cf_cache_pages_tags');    GeneralUtility::makeInstance(ConnectionPool::class)        ->getConnectionForTable('cf_cache_rootline')        ->truncate('cf_cache_rootline');    GeneralUtility::makeInstance(ConnectionPool::class)        ->getConnectionForTable('cf_cache_rootline_tags')        ->truncate('cf_cache_rootline_tags');    GeneralUtility::makeInstance(ConnectionPool::class)        ->getConnectionForTable('cf_extbase_datamapfactory_datamap')        ->truncate('cf_extbase_datamapfactory_datamap');    GeneralUtility::makeInstance(ConnectionPool::class)        ->getConnectionForTable('cf_extbase_datamapfactory_datamap_tags')        ->truncate('cf_extbase_datamapfactory_datamap_tags');    GeneralUtility::makeInstance(ConnectionPool::class)        ->getConnectionForTable('cf_vhs_main')        ->truncate('cf_vhs_main');    GeneralUtility::makeInstance(ConnectionPool::class)        ->getConnectionForTable('cf_vhs_main_tags')        ->truncate('cf_vhs_main_tags');    GeneralUtility::makeInstance(ConnectionPool::class)        ->getConnectionForTable('cf_vhs_markdown')        ->truncate('cf_vhs_markdown');    GeneralUtility::makeInstance(ConnectionPool::class)        ->getConnectionForTable('cf_vhs_markdown_tags')        ->truncate('cf_vhs_markdown_tags');    GeneralUtility::makeInstance(ConnectionPool::class)        ->getConnectionForTable('tx_vavkbimmo_domain_model_propertycache')        ->truncate('tx_vavkbimmo_domain_model_propertycache');     //index tables    GeneralUtility::makeInstance(ConnectionPool::class)        ->getConnectionForTable('index_fulltext')        ->truncate('index_fulltext');    GeneralUtility::makeInstance(ConnectionPool::class)        ->getConnectionForTable('index_grlist')        ->truncate('index_grlist');    GeneralUtility::makeInstance(ConnectionPool::class)        ->getConnectionForTable('index_phash')        ->truncate('index_phash');    GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('index_rel')->truncate('index_rel');    GeneralUtility::makeInstance(ConnectionPool::class)        ->getConnectionForTable('index_section')        ->truncate('index_section');    GeneralUtility::makeInstance(ConnectionPool::class)        ->getConnectionForTable('index_stat_search')        ->truncate('index_stat_search');    GeneralUtility::makeInstance(ConnectionPool::class)        ->getConnectionForTable('index_stat_word')        ->truncate('index_stat_word');    GeneralUtility::makeInstance(ConnectionPool::class)        ->getConnectionForTable('index_words')        ->truncate('index_words');     //call all pages    $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');    $result = $queryBuilder->select('uid')->from('pages')->execute();    while ($row = $result->fetch()) {        self::crawlPage(intval($row['uid']), $baseUrl);    }     //call all properties    $queryBuilderProperty = GeneralUtility::makeInstance(ConnectionPool::class)        ->getQueryBuilderForTable('tx_vavkbimmo_domain_model_property');     //index all properties    $crawlerHook = GeneralUtility::makeInstance(\TYPO3\CMS\IndexedSearch\Hook\CrawlerHook::class);    $queryBuilderIndexcfg =        GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_config');    $resultIndexConfigs =        $queryBuilderIndexcfg->select('*')->from('index_config')->where($queryBuilderIndexcfg->expr()            ->eq('uid', $queryBuilderIndexcfg->createNamedParameter(2, \PDO::PARAM_INT)))->execute();     $resultProperties =        $queryBuilderProperty->select('*')->from('tx_vavkbimmo_domain_model_property')->where($queryBuilder->expr()            ->eq('remove_marked', 0))->execute();     $cfgRecProperties = null;    while ($cfgRec = $resultIndexConfigs->fetch()) {        $cfgRecProperties = $cfgRec;    }     while ($propertyRec = $resultProperties->fetch()) {        if (!is_null($cfgRecProperties)) {            $crawlerHook->indexSingleRecord($propertyRec, $cfgRecProperties);        }    }     $resultPropertiesAll =        $queryBuilderProperty->select('*')->from('tx_vavkbimmo_domain_model_property')->execute();    while ($propertyRec = $resultPropertiesAll->fetch()) {        self::crawlProperty($propertyRec['slug'], $baseUrl);    }         return true;}     private static function crawlPage($pageUid, $baseUrl){    $curl = curl_init();    curl_setopt_array($curl, array(        CURLOPT_URL => $baseUrl . 'index.php?id=' . intval($pageUid),        CURLOPT_RETURNTRANSFER => true,        CURLOPT_ENCODING => "",        CURLOPT_SSL_VERIFYHOST => 0,        CURLOPT_SSL_VERIFYPEER => 0,        CURLOPT_MAXREDIRS => 10,        CURLOPT_TIMEOUT => 30,        CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,        CURLOPT_CUSTOMREQUEST => "GET",        CURLOPT_POSTFIELDS => "",        CURLOPT_HTTPHEADER => array(            "cache-control: no-cache"        ),    ));     curl_exec($curl);    curl_close($curl);} private static function crawlProperty($slug, $baseUrl){    $curl = curl_init();    curl_setopt_array($curl, array(        CURLOPT_URL => $baseUrl . 'expose/' . $slug,        CURLOPT_RETURNTRANSFER => true,        CURLOPT_ENCODING => "",        CURLOPT_SSL_VERIFYHOST => 0,        CURLOPT_SSL_VERIFYPEER => 0,        CURLOPT_MAXREDIRS => 10,        CURLOPT_TIMEOUT => 30,        CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,        CURLOPT_CUSTOMREQUEST => "GET",        CURLOPT_POSTFIELDS => "",        CURLOPT_HTTPHEADER => array(            "cache-control: no-cache"        ),    ));     curl_exec($curl);    curl_close($curl);} private static function getBaseUrl(){    //get base url    $siteFinder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Site\SiteFinder::class);    $site = $siteFinder->getSiteByPageId(1);    return $site->getConfiguration()['base'];}

Indexer

Nun gibt es noch ein Problem mit dem Indexer. Dieser setzt bei mir immer die falsche FreeIndexUid. Deshalb habe ich mittels XClass die Methode submitPage des Indexeres überschrieben. Dazu muss in der ext_localconf.php folgendes registriert werden:

/************************************************************************ * XCLASS ************************************************************************/$GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects']['TYPO3\\CMS\\IndexedSearch\\Indexer'] = array(    'className' => 'Varioous\\XXXXXXXX\\Xclass\\Indexer');

Und hier nun noch die Indexer-Klasse:

class Indexer extends \TYPO3\CMS\IndexedSearch\Indexer{     public function submitPage()    {        // get page repository        $objectManager =            \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Object\\ObjectManager');         $pageRepository = $objectManager->get('TYPO3\\CMS\\Frontend\\Page\\PageRepository');         //get database        $connectionPool =            \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class);        $queryBuilder = $connectionPool->getQueryBuilderForTable('index_config');         //get all index configuration        $runningIndexingConfigurations = $queryBuilder->select('*')->from('index_config')->where($queryBuilder->expr()            ->neq('uid', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)))->execute()->fetchAll();         //save index configurations        $indexconfigs = array();        foreach ($runningIndexingConfigurations as $cfgRec) {            $indexconfigs[intval($cfgRec['uid'])] = intval($cfgRec['alternative_source_pid']);        }         //get current page and start here        $currentPage = intval($this->conf['id']);         //traverse through page tree        while ($currentPage > 0) {            if (self::isPageUidInIndex($currentPage)) {                //set correct freeIndexUid                $this->conf['freeIndexUid'] = self::isPageUidInIndex($currentPage);                break;            }             $currentPageObject = $pageRepository->getRecordsByField('pages', 'uid', intval($currentPage));            if ($currentPageObject && !is_null($currentPageObject)) {                if (count($currentPageObject) == 1) {                    $currentPage = intval($currentPageObject[0]['pid']);                } else {                    break;                }            } else {                break;            }        }        parent::submitPage();    }     public static function isPageUidInIndex($page)    {        //check if property        if (intval($page)            == intval(\Varioous\VaBase\Configuration\ExtensionConfiguration::get('config.pid.property',                'xxxxx'))) {            //property            return intval(\Varioous\VaBase\Configuration\ExtensionConfiguration::get('config.search.immoFreeIndexUid',                'xxxxx'));        }         // get page repository        $objectManager =            \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Object\\ObjectManager');        $pageRepository = $objectManager->get('TYPO3\\CMS\\Frontend\\Page\\PageRepository');        $pageObject = $pageRepository->getRecordsByField('pages', 'uid', intval($page));         //check if article        if (intval($pageObject[0]['pid'])            == intval(\Varioous\VaBase\Configuration\ExtensionConfiguration::get('config.pid.article',                'xxxxx'))) {            return intval(\Varioous\VaBase\Configuration\ExtensionConfiguration::get('config.search.articleFreeIndexUid',                'xxxxx'));        }         //it must be normal content page        return intval(\Varioous\VaBase\Configuration\ExtensionConfiguration::get('config.search.pageFreeIndexUid',            'xxxxx'));    }}

Wenn man nun den Task ausführt oder die Website über das Frontend aufruft, müssen Index Einträge erstellt werden. Dies kann man am besten direkt in der DB überprüft werden, dazu einfach in der Tabelle index_phash nachsehen:

TYPO3 Indexed Search mit Segmentierung/Indexierung

Tipp: Wenn man den Cache nicht aller Extbase-Objekte auf einmal leeren will, sondern nur von einzelnen, ist dies mit einem kleinen Trick möglich. Weitere Informationen dazu findest du in diesem Blogbeitrag.

Weitere Informationen / Links

Wir entwickeln digitale Lösungen mit Leidenschaft

Warum wir das tun? Weil die Verwirklichung Ihrer Vision unser größter Anspruch und die schönste Anerkennung ist. Deshalb nehmen wir uns gerne ausreichend Zeit für die Realisierung Ihres digitalen Projekts.

Kontaktieren Sie uns, wir sind gerne für Ihre Fragen da:

Passend zu diesem Thema:

Das war das TYPO3camp München 2019

Das war das TYPO3camp München 2019

Vom 13. – 15. September fand das TYPO3camp 2019 in München statt und auch varioous war vor Ort um sich mit anderen TYPO3-Entwicklern und Unternehmen z…

TYPO3 Tipps und Tricks: Manueller/Programmatischer Login in Controller-Action (TYPO3 9)

TYPO3 Tipps und Tricks: Manueller/Programmatischer Login in Controller-Action (T…

Manchmal gibt es bei Web-Projekten die Notwendigkeit einen Login programmatisch (manuell, im PHP-Code) durchzuführen. Ein Kunde hat vor kurzem folgend…

Warum TYPO3?

Warum TYPO3?

Warum sollten Sie eigentlich auf TYPO3 und nicht auf ein anderes CMS wie Drupal, Wordpress, Contao, Joomla oder ähnliches setzen, bzw. warum empfehlen…