[BUGFIX] Indexed_search extbase plugin support for mysql fulltext search 30/38130/8
authorTymoteusz Motylewski <t.motylewski@gmail.com>
Tue, 24 Mar 2015 19:08:49 +0000 (20:08 +0100)
committerTymoteusz Motylewski <t.motylewski@gmail.com>
Thu, 16 Apr 2015 16:41:48 +0000 (18:41 +0200)
Add three getters for IndexSearchRepository and SearchFormController
to have a common api for accessing data needed by FulltextIndexHook.

Unify the processing of $page_where as in one class it
was expected that it returns statement with 'AND' and in other without.

Unify the formatting of select statements in both classes to make
differences easier to spot (fix missing $wordSel statement
in the extbase plugin in the process).

Resolves: #65989
Resolves: #52277
Releases: master, 6.2
Change-Id: I9fcb86d44208e8fe6d205667f344022b0ad62505
Reviewed-on: http://review.typo3.org/38130
Reviewed-by: Tymoteusz Motylewski <t.motylewski@gmail.com>
Tested-by: Tymoteusz Motylewski <t.motylewski@gmail.com>
typo3/sysext/indexed_search/Classes/Controller/SearchController.php
typo3/sysext/indexed_search/Classes/Controller/SearchFormController.php
typo3/sysext/indexed_search/Classes/Domain/Repository/IndexSearchRepository.php
typo3/sysext/indexed_search/Configuration/TypoScript/setup.txt
typo3/sysext/indexed_search_mysql/Classes/Hook/MysqlFulltextIndexHook.php

index 1adb003..caa436d 100644 (file)
@@ -36,9 +36,15 @@ class SearchController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionControlle
 
        protected $searchData;
 
-       // This is the id of the site root.
-       // This value may be a commalist of integer (prepared for this)
-       // Root-page PIDs to search in (rl0 field where clause, see initialize() function)
+       /**
+        * This is the id of the site root.
+        * This value may be a commalist of integer (prepared for this)
+        * Root-page PIDs to search in (rl0 field where clause, see initialize() function)
+        *
+        * If this value is set to less than zero (eg. -1) searching will happen
+        * in ALL of the page tree with no regard to branches at all.
+        * @var int|string
+        */
        protected $searchRootPageIdList = 0;
 
        protected $defaultResultNumber = 10;
@@ -759,7 +765,7 @@ class SearchController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionControlle
                        $sWordArray = $hookObj->getSearchWords_splitSWords($searchWords, $defaultOperator);
                } else {
                        // sentence
-                       if ($this->searchDat['searchType'] == 20) {
+                       if ($this->searchData['searchType'] == 20) {
                                $sWordArray = array(
                                        array(
                                                'sword' => trim($searchWords),
@@ -871,8 +877,8 @@ class SearchController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionControlle
                        $this->view->assign('allNumberOfResults', $allNumberOfResults);
                        $allGroups = $this->getAllAvailableGroupOptions();
                        $this->view->assign('allGroups', $allGroups);
-                       $this->view->assign('searchParams', $searchData);
                }
+               $this->view->assign('searchParams', $searchData);
        }
 
        /****************************************
index 6f0a47c..cc694be 100644 (file)
@@ -1022,9 +1022,9 @@ class SearchFormController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlugin {
        }
 
        /**
-        * Returns AND statement for selection of langauge
+        * Returns AND statement for selection of language
         *
-        * @return string AND statement for selection of langauge
+        * @return string AND statement for selection of language
         */
        public function languageWhere() {
                if ($this->piVars['lang'] >= 0) {
@@ -1083,8 +1083,6 @@ class SearchFormController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlugin {
                // Setting up methods of filtering results based on page types, access, etc.
                $page_join = '';
                $page_where = '';
-               // Indexing configuration clause:
-               $freeIndexUidClause = $this->freeIndexUidWhere($freeIndexUid);
                // Calling hook for alternative creation of page ID list
                if ($hookObj = $this->hookRequest('execFinalQuery_idList')) {
                        $page_where = $hookObj->execFinalQuery_idList($list);
@@ -1104,11 +1102,13 @@ class SearchFormController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlugin {
                        foreach ($siteIdNumbers as $rootId) {
                                $id_list[] = $this->cObj->getTreeList(-1 * $rootId, 9999);
                        }
-                       $page_where = ' ISEC.page_id IN (' . implode(',', $id_list) . ')';
+                       $page_where = 'ISEC.page_id IN (' . implode(',', $id_list) . ')';
                } else {
                        // Disable everything... (select all)
-                       $page_where = ' 1=1 ';
+                       $page_where = '1=1';
                }
+               // Indexing configuration clause:
+               $freeIndexUidClause = $this->freeIndexUidWhere($freeIndexUid);
                // If any of the ranking sortings are selected, we must make a join with the word/rel-table again, because we need to calculate ranking based on all search-words found.
                if (substr($this->piVars['order'], 0, 5) == 'rank_') {
                        switch ($this->piVars['order']) {
@@ -2342,4 +2342,32 @@ class SearchFormController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlugin {
                return $result;
        }
 
+       /**
+        * Search type
+        * e.g. sentence (20), any part of the word (1)
+        *
+        * @return int
+        */
+       public function getSearchType() {
+               return (int)$this->piVars['type'];
+       }
+
+       /**
+        * A list of integer which should be root-pages to search from
+        *
+        * @return int[]
+        */
+       public function getSearchRootPageIdList() {
+               return \TYPO3\CMS\Core\Utility\GeneralUtility::intExplode(',', $this->wholeSiteIdList);
+       }
+
+       /**
+        * Getter for join_pages flag
+        * enabled through $this->conf['search.']['skipExtendToSubpagesChecking']
+        *
+        * @return bool
+        */
+       public function getJoinPagesForQuery() {
+               return (bool)$this->join_pages;
+       }
 }
index 8543751..bd2af6f 100644 (file)
@@ -58,11 +58,19 @@ class IndexSearchRepository {
        // formally known as $this->piVars['result']
        protected $numberOfResults = 10;
 
-       // list of all root pages that will be used
+       /**
+        * list of all root pages that will be used
+        * If this value is set to less than zero (eg. -1) searching will happen
+        * in ALL of the page tree with no regard to branches at all.
+        */
        protected $searchRootPageIdList;
 
-       // formally known as $conf['search.']['searchSkipExtendToSubpagesChecking']
-       // enabled through settings.searchSkipExtendToSubpagesChecking
+       /**
+        * formally known as $conf['search.']['searchSkipExtendToSubpagesChecking']
+        * enabled through settings.searchSkipExtendToSubpagesChecking
+        *
+        * @var bool
+        */
        protected $joinPagesForQuery = FALSE;
 
        // Select clauses for individual words,
@@ -481,9 +489,9 @@ class IndexSearchRepository {
        }
 
        /**
-        * Returns AND statement for selection of langauge
+        * Returns AND statement for selection of language
         *
-        * @return string AND statement for selection of langauge
+        * @return string AND statement for selection of language
         */
        public function languageWhere() {
                // -1 is the same as ALL language.
@@ -550,13 +558,12 @@ class IndexSearchRepository {
                // Calling hook for alternative creation of page ID list
                if ($hookObj = $this->hookRequest('execFinalQuery_idList')) {
                        $page_where = $hookObj->execFinalQuery_idList($list);
-               }
-               // Alternative to getting all page ids by ->getTreeList() where
-               // "excludeSubpages" is NOT respected.
-               if ($this->joinPagesForQuery) {
+               } elseif ($this->joinPagesForQuery) {
+                       // Alternative to getting all page ids by ->getTreeList() where
+                       // "excludeSubpages" is NOT respected.
                        $page_join = ',
                                pages';
-                       $page_where = ' AND pages.uid = ISEC.page_id
+                       $page_where = 'pages.uid = ISEC.page_id
                                ' . $this->enableFields('pages') . '
                                AND pages.no_search=0
                                AND pages.doktype<200
@@ -570,7 +577,10 @@ class IndexSearchRepository {
                        foreach ($siteIdNumbers as $rootId) {
                                $pageIdList[] = $GLOBALS['TSFE']->cObj->getTreeList(-1 * $rootId, 9999);
                        }
-                       $page_where = ' AND ISEC.page_id IN (' . implode(',', $pageIdList) . ')';
+                       $page_where = 'ISEC.page_id IN (' . implode(',', $pageIdList) . ')';
+               } else {
+                       // Disable everything... (select all)
+                       $page_where = '1=1';
                }
                // otherwise select all / disable everything
                // If any of the ranking sortings are selected, we must make a
@@ -601,13 +611,24 @@ class IndexSearchRepository {
                                        $grsel = 'SUM(IR.freq) AS order_val';
                                        $orderBy = 'order_val' . $this->getDescendingSortOrderFlag();
                        }
-                       $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('ISEC.*, IP.*, ' . $grsel, 'index_words IW,
-                                                       index_rel IR,
-                                                       index_section ISEC,
-                                                       index_phash IP' . $page_join, 'IP.phash IN (' . $list . ') ' . $this->mediaTypeWhere() . $this->languageWhere() . $freeIndexUidClause . '
-                                                       AND IW.wid=IR.wid
-                                                       AND ISEC.phash = IR.phash
-                                                       AND IP.phash = IR.phash' . $page_where, 'IP.phash,ISEC.phash,ISEC.phash_t3,ISEC.rl0,ISEC.rl1,ISEC.rl2 ,ISEC.page_id,ISEC.uniqid,IP.phash_grouping,IP.data_filename ,IP.data_page_id ,IP.data_page_reg1,IP.data_page_type,IP.data_page_mp,IP.gr_list,IP.item_type,IP.item_title,IP.item_description,IP.item_mtime,IP.tstamp,IP.item_size,IP.contentHash,IP.crdate,IP.parsetime,IP.sys_language_uid,IP.item_crdate,IP.cHashParams,IP.externalUrl,IP.recordUid,IP.freeIndexUid,IP.freeIndexSetId', $orderBy);
+                       // So, words are imploded into an OR statement (no "sentence search" should be done here - may deselect results)
+                       $wordSel = '(' . implode(' OR ', $this->wSelClauses) . ') AND ';
+                       $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
+                               'ISEC.*, IP.*, ' . $grsel,
+                               'index_words IW,
+                                       index_rel IR,
+                                       index_section ISEC,
+                                       index_phash IP' . $page_join,
+                               $wordSel .
+                               'IP.phash IN (' . $list . ') ' .
+                                       $this->mediaTypeWhere() . ' ' . $this->languageWhere() . $freeIndexUidClause . '
+                                       AND IW.wid=IR.wid
+                                       AND ISEC.phash = IR.phash
+                                       AND IP.phash = IR.phash
+                                       AND ' . $page_where,
+                               'IP.phash,ISEC.phash,ISEC.phash_t3,ISEC.rl0,ISEC.rl1,ISEC.rl2 ,ISEC.page_id,ISEC.uniqid,IP.phash_grouping,IP.data_filename ,IP.data_page_id ,IP.data_page_reg1,IP.data_page_type,IP.data_page_mp,IP.gr_list,IP.item_type,IP.item_title,IP.item_description,IP.item_mtime,IP.tstamp,IP.item_size,IP.contentHash,IP.crdate,IP.parsetime,IP.sys_language_uid,IP.item_crdate,IP.cHashParams,IP.externalUrl,IP.recordUid,IP.freeIndexUid,IP.freeIndexSetId',
+                               $orderBy
+                       );
                } else {
                        // Otherwise, if sorting are done with the pages table or other fields,
                        // there is no need for joining with the rel/word tables:
@@ -768,4 +789,32 @@ class IndexSearchRepository {
                }
        }
 
+       /**
+        * Search type
+        * e.g. sentence (20), any part of the word (1)
+        *
+        * @return int
+        */
+       public function getSearchType() {
+               return (int)$this->searchType;
+       }
+
+       /**
+        * A list of integer which should be root-pages to search from
+        *
+        * @return int[]
+        */
+       public function getSearchRootPageIdList() {
+               return \TYPO3\CMS\Core\Utility\GeneralUtility::intExplode(',', $this->searchRootPageIdList);
+       }
+
+       /**
+        * Getter for joinPagesForQuery flag
+        * enabled through TypoScript 'settings.skipExtendToSubpagesChecking'
+        *
+        * @return bool
+        */
+       public function getJoinPagesForQuery() {
+               return $this->joinPagesForQuery;
+       }
 }
index edb77d2..a6d97d9 100644 (file)
@@ -85,6 +85,7 @@ plugin.tx_indexedsearch {
                        sortOrder = rank_flag
                        languageUid = -1
                        sortDesc = 1
+                       searchType = 1
                }
 
        }
index 5d225b0..109261d 100644 (file)
@@ -22,7 +22,7 @@ namespace TYPO3\CMS\IndexedSearchMysql\Hook;
 class MysqlFulltextIndexHook {
 
        /**
-        * @var \TYPO3\CMS\IndexedSearch\Controller\SearchFormController
+        * @var \TYPO3\CMS\IndexedSearch\Controller\SearchFormController|\TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository
         */
        public $pObj;
 
@@ -61,14 +61,16 @@ class MysqlFulltextIndexHook {
        public function getSearchString($searchWordArray) {
                // Initialize variables:
                $count = 0;
-               $searchBoolean = FALSE;
                // Change this to TRUE to force BOOLEAN SEARCH MODE (useful if fulltext index is still empty)
+               $searchBoolean = FALSE;
                $fulltextIndex = 'index_fulltext.fulltextdata';
-               $naturalSearchString = '';
                // This holds the result if the search is natural (doesn't contain any boolean operators)
-               $booleanSearchString = '';
+               $naturalSearchString = '';
                // This holds the result if the search is boolen (contains +/-/| operators)
-               $searchType = (string)$this->pObj->piVars['type'];
+               $booleanSearchString = '';
+
+               $searchType = (string)$this->pObj->getSearchType();
+
                // Traverse searchwords and prefix them with corresponding operator
                foreach ($searchWordArray as $searchWordData) {
                        // Making the query for a single search word based on the search-type
@@ -145,22 +147,24 @@ class MysqlFulltextIndexHook {
                // Indexing configuration clause:
                $freeIndexUidClause = $this->pObj->freeIndexUidWhere($freeIndexUid);
                // Calling hook for alternative creation of page ID list
+               $searchRootPageIdList = $this->pObj->getSearchRootPageIdList();
                if ($hookObj = &$this->pObj->hookRequest('execFinalQuery_idList')) {
                        $pageWhere = $hookObj->execFinalQuery_idList('');
-               } elseif ($this->pObj->join_pages) {
+               } elseif ($this->pObj->getJoinPagesForQuery()) {
                        // Alternative to getting all page ids by ->getTreeList() where "excludeSubpages" is NOT respected.
                        $pageJoin = ',
                                pages';
                        $pageWhere = 'pages.uid = ISEC.page_id
-                               ' . $this->pObj->cObj->enableFields('pages') . '
+                               ' . $GLOBALS['TSFE']->cObj->enableFields('pages') . '
                                AND pages.no_search=0
                                AND pages.doktype<200
                        ';
-               } elseif ($this->pObj->wholeSiteIdList >= 0) {
-                       // Collecting all pages IDs in which to search; filtering out ALL pages that are not accessible due to enableFields. Does NOT look for "no_search" field!
-                       $siteIdNumbers = \TYPO3\CMS\Core\Utility\GeneralUtility::intExplode(',', $this->pObj->wholeSiteIdList);
+               } elseif ($searchRootPageIdList[0] >= 0) {
+
+                       // Collecting all pages IDs in which to search;
+                       // filtering out ALL pages that are not accessible due to enableFields. Does NOT look for "no_search" field!
                        $idList = array();
-                       foreach ($siteIdNumbers as $rootId) {
+                       foreach ($searchRootPageIdList as $rootId) {
                                /** @var \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer $cObj */
                                $cObj = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::class);
                                $idList[] = $cObj->getTreeList( -1 * $rootId, 9999);
@@ -174,10 +178,17 @@ class MysqlFulltextIndexHook {
                if ($searchData['searchBoolean']) {
                        $searchBoolean = ' IN BOOLEAN MODE';
                }
-               $resource = $GLOBALS['TYPO3_DB']->exec_SELECTquery('index_fulltext.*, ISEC.*, IP.*', 'index_fulltext, index_section ISEC, index_phash IP' . $pageJoin, 'MATCH (' . $searchData['fulltextIndex'] . ') AGAINST (' . $GLOBALS['TYPO3_DB']->fullQuoteStr($searchData['searchString'], 'index_fulltext') . $searchBoolean . ') ' . $this->pObj->mediaTypeWhere() . ' ' . $this->pObj->languageWhere() . $freeIndexUidClause . '
-                                       AND index_fulltext.phash = IP.phash
-                                       AND ISEC.phash = IP.phash
-                                       AND ' . $pageWhere, 'IP.phash,ISEC.phash,ISEC.phash_t3,ISEC.rl0,ISEC.rl1,ISEC.rl2,ISEC.page_id,ISEC.uniqid,IP.phash_grouping,IP.data_filename ,IP.data_page_id ,IP.data_page_reg1,IP.data_page_type,IP.data_page_mp,IP.gr_list,IP.item_type,IP.item_title,IP.item_description,IP.item_mtime,IP.tstamp,IP.item_size,IP.contentHash,IP.crdate,IP.parsetime,IP.sys_language_uid,IP.item_crdate,IP.cHashParams,IP.externalUrl,IP.recordUid,IP.freeIndexUid,IP.freeIndexSetId');
+               $resource = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
+                       'index_fulltext.*, ISEC.*, IP.*',
+                       'index_fulltext, index_section ISEC, index_phash IP' . $pageJoin,
+                       'MATCH (' . $searchData['fulltextIndex'] . ')
+                               AGAINST (' . $GLOBALS['TYPO3_DB']->fullQuoteStr($searchData['searchString'], 'index_fulltext') . $searchBoolean . ') ' .
+                               $this->pObj->mediaTypeWhere() . ' ' . $this->pObj->languageWhere() . $freeIndexUidClause . '
+                               AND index_fulltext.phash = IP.phash
+                               AND ISEC.phash = IP.phash
+                               AND ' . $pageWhere,
+                       'IP.phash,ISEC.phash,ISEC.phash_t3,ISEC.rl0,ISEC.rl1,ISEC.rl2,ISEC.page_id,ISEC.uniqid,IP.phash_grouping,IP.data_filename ,IP.data_page_id ,IP.data_page_reg1,IP.data_page_type,IP.data_page_mp,IP.gr_list,IP.item_type,IP.item_title,IP.item_description,IP.item_mtime,IP.tstamp,IP.item_size,IP.contentHash,IP.crdate,IP.parsetime,IP.sys_language_uid,IP.item_crdate,IP.cHashParams,IP.externalUrl,IP.recordUid,IP.freeIndexUid,IP.freeIndexSetId'
+               );
                return $resource;
        }