public function search($searchText, $params = array(), $searchTypes = array()) { if (count($searchTypes) == 0) { $searchTypes['general'] = array(); $searchTypes['subtype'] = array(); $searchTypes['and'] = array(); } else { if (!isset($searchTypes['general'])) { $searchTypes['general'] = array(); } } $allowSearch = true; if (trim($searchText) == '') { $ini = eZINI::instance(); if ($ini->variable('SearchSettings', 'AllowEmptySearch') != 'enabled') { $allowSearch = false; } if (isset($params['AllowEmptySearch'])) { $allowSearch = $params['AllowEmptySearch']; } } if ($allowSearch) { $searchText = $this->normalizeText($searchText, false); $db = eZDB::instance(); $nonExistingWordArray = array(); $searchTypeMap = array('class' => 'SearchContentClassID', 'publishdate' => 'SearchDate', 'subtree' => 'SearchSubTreeArray'); foreach ($searchTypes['general'] as $searchType) { $params[$searchTypeMap[$searchType['subtype']]] = $searchType['value']; } if (isset($params['SearchOffset'])) { $searchOffset = $params['SearchOffset']; } else { $searchOffset = 0; } if (isset($params['SearchLimit'])) { $searchLimit = $params['SearchLimit']; } else { $searchLimit = 10; } if (isset($params['SearchContentClassID'])) { $searchContentClassID = $params['SearchContentClassID']; } else { $searchContentClassID = -1; } if (isset($params['SearchSectionID'])) { $searchSectionID = $params['SearchSectionID']; } else { $searchSectionID = -1; } if (isset($params['SearchDate'])) { $searchDate = $params['SearchDate']; } else { $searchDate = -1; } if (isset($params['SearchTimestamp'])) { $searchTimestamp = $params['SearchTimestamp']; } else { $searchTimestamp = false; } if (isset($params['SearchContentClassAttributeID'])) { $searchContentClassAttributeID = $params['SearchContentClassAttributeID']; } else { $searchContentClassAttributeID = -1; } if (isset($params['SearchSubTreeArray'])) { $subTreeArray = $params['SearchSubTreeArray']; } else { $subTreeArray = array(); } if (isset($params['SortArray'])) { $sortArray = $params['SortArray']; } else { $sortArray = array(); } $ignoreVisibility = isset($params['IgnoreVisibility']) ? $params['IgnoreVisibility'] : false; // strip multiple spaces $searchText = preg_replace("(\\s+)", " ", $searchText); // find the phrases /* $numQuotes = substr_count( $searchText, "\"" ); $phraseTextArray = array(); $fullText = $searchText; $nonPhraseText =''; // $fullText = ''; $postPhraseText = $fullText; $pos = 0; if ( ( $numQuotes > 0 ) and ( ( $numQuotes % 2 ) == 0 ) ) { for ( $i = 0; $i < ( $numQuotes / 2 ); $i ++ ) { $quotePosStart = strpos( $searchText, '"', $pos ); $quotePosEnd = strpos( $searchText, '"', $quotePosStart + 1 ); $prePhraseText = substr( $searchText, $pos, $quotePosStart - $pos ); $postPhraseText = substr( $searchText, $quotePosEnd +1 ); $phraseText = substr( $searchText, $quotePosStart + 1, $quotePosEnd - $quotePosStart - 1 ); $phraseTextArray[] = $phraseText; // $fullText .= $prePhraseText; $nonPhraseText .= $prePhraseText; $pos = $quotePosEnd + 1; } } $nonPhraseText .= $postPhraseText; */ $phrasesResult = $this->getPhrases($searchText); $phraseTextArray = $phrasesResult['phrases']; $nonPhraseText = $phrasesResult['nonPhraseText']; $fullText = $phrasesResult['fullText']; $sectionQuery = ''; if (is_numeric($searchSectionID) and $searchSectionID > 0) { $sectionQuery = "ezsearch_object_word_link.section_id = '{$searchSectionID}' AND "; } else { if (is_array($searchSectionID)) { // Build query for searching in an array of sections $sectionQuery = $db->generateSQLINStatement($searchSectionID, 'ezsearch_object_word_link.section_id', false, false, 'int') . " AND "; } } $searchDateQuery = ''; if (is_numeric($searchDate) and $searchDate > 0 or $searchTimestamp) { $date = new eZDateTime(); $timestamp = $date->timeStamp(); $day = $date->attribute('day'); $month = $date->attribute('month'); $year = $date->attribute('year'); $publishedDateStop = false; if ($searchTimestamp) { if (is_array($searchTimestamp)) { $publishedDate = (int) $searchTimestamp[0]; $publishedDateStop = (int) $searchTimestamp[1]; } else { $publishedDate = (int) $searchTimestamp; } } else { switch ($searchDate) { case 1: $adjustment = 24 * 60 * 60; //seconds for one day $publishedDate = $timestamp - $adjustment; break; case 2: $adjustment = 7 * 24 * 60 * 60; //seconds for one week $publishedDate = $timestamp - $adjustment; break; case 3: $adjustment = 31 * 24 * 60 * 60; //seconds for one month $publishedDate = $timestamp - $adjustment; break; case 4: $adjustment = 3 * 31 * 24 * 60 * 60; //seconds for three months $publishedDate = $timestamp - $adjustment; break; case 5: $adjustment = 365 * 24 * 60 * 60; //seconds for one year $publishedDate = $timestamp - $adjustment; break; default: $publishedDate = $date->timeStamp(); } } $searchDateQuery = "ezsearch_object_word_link.published >= '{$publishedDate}' AND "; if ($publishedDateStop) { $searchDateQuery .= "ezsearch_object_word_link.published <= '{$publishedDateStop}' AND "; } $this->GeneralFilter['searchDateQuery'] = $searchDateQuery; } $classQuery = ""; if (is_numeric($searchContentClassID) and $searchContentClassID > 0) { // Build query for searching in one class $classQuery = "ezsearch_object_word_link.contentclass_id = '{$searchContentClassID}' AND "; $this->GeneralFilter['classAttributeQuery'] = $classQuery; } else { if (is_array($searchContentClassID)) { // Build query for searching in a number of classes $classString = $db->generateSQLINStatement($searchContentClassID, 'ezsearch_object_word_link.contentclass_id', false, false, 'int'); $classQuery = "{$classString} AND "; $this->GeneralFilter['classAttributeQuery'] = $classQuery; } } $classAttributeQuery = ""; if (is_numeric($searchContentClassAttributeID) and $searchContentClassAttributeID > 0) { $classAttributeQuery = "ezsearch_object_word_link.contentclass_attribute_id = '{$searchContentClassAttributeID}' AND "; } else { if (is_array($searchContentClassAttributeID)) { // Build query for searching in a number of attributes $classAttributeQuery = $db->generateSQLINStatement($searchContentClassAttributeID, 'ezsearch_object_word_link.contentclass_attribute_id', false, false, 'int') . ' AND '; } } // Get the total number of objects $totalObjectCount = $this->fetchTotalObjectCount(); $searchPartsArray = array(); $wordIDHash = array(); $wildCardCount = 0; if (trim($searchText) != '') { $wordIDArrays = $this->prepareWordIDArrays($searchText); $wordIDArray = $wordIDArrays['wordIDArray']; $wordIDHash = $wordIDArrays['wordIDHash']; $wildIDArray = $wordIDArrays['wildIDArray']; $wildCardCount = $wordIDArrays['wildCardCount']; $searchPartsArray = $this->buildSearchPartArray($phraseTextArray, $nonPhraseText, $wordIDHash, $wildIDArray); } /// OR search, not used in this version $doOrSearch = false; if ($doOrSearch == true) { // build fulltext search SQL part $searchWordArray = $this->splitString($fullText); $fullTextSQL = ""; if (count($searchWordArray) > 0) { $i = 0; // Build the word query string foreach ($searchWordArray as $searchWord) { $wordID = null; if (isset($wordIDHash[$searchWord])) { $wordID = $wordIDHash[$searchWord]['id']; } if (is_numeric($wordID) and $wordID > 0) { if ($i == 0) { $fullTextSQL .= "ezsearch_object_word_link.word_id='{$wordID}' "; } else { $fullTextSQL .= " OR ezsearch_object_word_link.word_id='{$wordID}' "; } } else { $nonExistingWordArray[] = $searchWord; } $i++; } $fullTextSQL = " ( {$fullTextSQL} ) AND "; } } // Search only in specific sub trees $subTreeSQL = ""; $subTreeTable = ""; if (count($subTreeArray) > 0) { // Fetch path_string value to use when searching subtrees $i = 0; $doSubTreeSearch = false; $subTreeNodeSQL = ''; foreach ($subTreeArray as $nodeID) { if (is_numeric($nodeID) and $nodeID > 0) { $subTreeNodeSQL .= " {$nodeID}"; if (isset($subTreeArray[$i + 1]) and is_numeric($subTreeArray[$i + 1])) { $subTreeNodeSQL .= ", "; } $doSubTreeSearch = true; } $i++; } if ($doSubTreeSearch == true) { $subTreeNodeSQL = "( " . $subTreeNodeSQL; // $subTreeTable = ", ezcontentobject_tree "; $subTreeTable = ''; $subTreeNodeSQL .= " ) "; $nodeQuery = "SELECT node_id, path_string FROM ezcontentobject_tree WHERE node_id IN {$subTreeNodeSQL}"; // Build SQL subtre search query $subTreeSQL = " ( "; $nodeArray = $db->arrayQuery($nodeQuery); $i = 0; foreach ($nodeArray as $node) { $pathString = $node['path_string']; $subTreeSQL .= " ezcontentobject_tree.path_string like '{$pathString}%' "; if ($i < count($nodeArray) - 1) { $subTreeSQL .= " OR "; } $i++; } $subTreeSQL .= " ) AND "; $this->GeneralFilter['subTreeTable'] = $subTreeTable; $this->GeneralFilter['subTreeSQL'] = $subTreeSQL; } } $limitation = false; if (isset($params['Limitation'])) { $limitation = $params['Limitation']; } $limitationList = eZContentObjectTreeNode::getLimitationList($limitation); $sqlPermissionChecking = eZContentObjectTreeNode::createPermissionCheckingSQL($limitationList); $this->GeneralFilter['sqlPermissionChecking'] = $sqlPermissionChecking; $versionNameJoins = " AND " . eZContentLanguage::sqlFilter('ezcontentobject_name', 'ezcontentobject'); /// Only support AND search at this time // build fulltext search SQL part $searchWordArray = $this->splitString($fullText); $searchWordCount = count($searchWordArray); $fullTextSQL = ""; $stopWordArray = array(); $ini = eZINI::instance(); $tmpTableCount = 0; $i = 0; foreach ($searchTypes['and'] as $searchType) { $methodName = $this->constructMethodName($searchType); $intermediateResult = $this->callMethod($methodName, array($searchType)); if ($intermediateResult == false) { // cleanup temp tables $db->dropTempTableList($sqlPermissionChecking['temp_tables']); return array("SearchResult" => array(), "SearchCount" => 0, "StopWordArray" => array()); } } // Do not execute search if site.ini:[SearchSettings]->AllowEmptySearch is enabled, but no conditions are set. if (!$searchDateQuery && !$sectionQuery && !$classQuery && !$classAttributeQuery && !$searchPartsArray && !$subTreeSQL) { // cleanup temp tables $db->dropTempTableList($sqlPermissionChecking['temp_tables']); return array("SearchResult" => array(), "SearchCount" => 0, "StopWordArray" => array()); } $i = $this->TempTablesCount; // Loop every word and insert result in temporary table // Determine whether we should search invisible nodes. $showInvisibleNodesCond = eZContentObjectTreeNode::createShowInvisibleSQLString(!$ignoreVisibility); foreach ($searchPartsArray as $searchPart) { $stopWordThresholdValue = 100; if ($ini->hasVariable('SearchSettings', 'StopWordThresholdValue')) { $stopWordThresholdValue = $ini->variable('SearchSettings', 'StopWordThresholdValue'); } $stopWordThresholdPercent = 60; if ($ini->hasVariable('SearchSettings', 'StopWordThresholdPercent')) { $stopWordThresholdPercent = $ini->variable('SearchSettings', 'StopWordThresholdPercent'); } $searchThresholdValue = $totalObjectCount; if ($totalObjectCount > $stopWordThresholdValue) { $searchThresholdValue = (int) ($totalObjectCount * ($stopWordThresholdPercent / 100)); } // do not search words that are too frequent if ($searchPart['object_count'] < $searchThresholdValue) { $tmpTableCount++; $searchPartText = $searchPart['sql_part']; if ($i == 0) { $table = $db->generateUniqueTempTableName('ezsearch_tmp_%', 0); $this->saveCreatedTempTableName(0, $table); $db->createTempTable("CREATE TEMPORARY TABLE {$table} ( contentobject_id int primary key not null, published int )"); $db->query("INSERT INTO {$table} SELECT DISTINCT ezsearch_object_word_link.contentobject_id, ezsearch_object_word_link.published\n FROM ezcontentobject\n INNER JOIN ezsearch_object_word_link ON (ezsearch_object_word_link.contentobject_id = ezcontentobject.id)\n {$subTreeTable}\n INNER JOIN ezcontentclass ON (ezcontentclass.id = ezcontentobject.contentclass_id)\n INNER JOIN ezcontentobject_tree ON (ezcontentobject_tree.contentobject_id = ezcontentobject.id)\n {$sqlPermissionChecking['from']}\n WHERE\n {$searchDateQuery}\n {$sectionQuery}\n {$classQuery}\n {$classAttributeQuery}\n {$searchPartText}\n {$subTreeSQL}\n ezcontentclass.version = '0' AND\n ezcontentobject_tree.node_id = ezcontentobject_tree.main_node_id\n {$showInvisibleNodesCond}\n {$sqlPermissionChecking['where']}", eZDBInterface::SERVER_SLAVE); } else { $table = $db->generateUniqueTempTableName('ezsearch_tmp_%', $i); $this->saveCreatedTempTableName($i, $table); $tmpTable0 = $this->getSavedTempTableName(0); $db->createTempTable("CREATE TEMPORARY TABLE {$table} ( contentobject_id int primary key not null, published int )"); $db->query("INSERT INTO {$table} SELECT DISTINCT ezsearch_object_word_link.contentobject_id, ezsearch_object_word_link.published\n FROM\n ezcontentobject\n INNER JOIN ezsearch_object_word_link ON (ezsearch_object_word_link.contentobject_id = ezcontentobject.id)\n {$subTreeTable}\n INNER JOIN ezcontentclass ON (ezcontentclass.id = ezcontentobject.contentclass_id)\n INNER JOIN ezcontentobject_tree ON (ezcontentobject_tree.contentobject_id = ezcontentobject.id)\n INNER JOIN {$tmpTable0} ON ({$tmpTable0}.contentobject_id = ezsearch_object_word_link.contentobject_id)\n {$sqlPermissionChecking['from']}\n WHERE\n {$searchDateQuery}\n {$sectionQuery}\n {$classQuery}\n {$classAttributeQuery}\n {$searchPartText}\n {$subTreeSQL}\n ezcontentclass.version = '0' AND\n ezcontentobject_tree.node_id = ezcontentobject_tree.main_node_id\n {$showInvisibleNodesCond}\n {$sqlPermissionChecking['where']}", eZDBInterface::SERVER_SLAVE); } $i++; } else { $stopWordArray[] = array('word' => $searchPart['text']); } } if (count($searchPartsArray) === 0 && $this->TempTablesCount == 0) { $table = $db->generateUniqueTempTableName('ezsearch_tmp_%', 0); $this->saveCreatedTempTableName(0, $table); $db->createTempTable("CREATE TEMPORARY TABLE {$table} ( contentobject_id int primary key not null, published int )"); $db->query("INSERT INTO {$table} SELECT DISTINCT ezsearch_object_word_link.contentobject_id, ezsearch_object_word_link.published\n FROM ezcontentobject\n INNER JOIN ezsearch_object_word_link ON (ezsearch_object_word_link.contentobject_id = ezcontentobject.id)\n {$subTreeTable}\n INNER JOIN ezcontentclass ON (ezcontentclass.id = ezcontentobject.contentclass_id)\n INNER JOIN ezcontentobject_tree ON (ezcontentobject_tree.contentobject_id = ezcontentobject.id)\n {$sqlPermissionChecking['from']}\n WHERE\n {$searchDateQuery}\n {$sectionQuery}\n {$classQuery}\n {$classAttributeQuery}\n {$subTreeSQL}\n ezcontentclass.version = '0' AND\n ezcontentobject_tree.node_id = ezcontentobject_tree.main_node_id\n {$showInvisibleNodesCond}\n {$sqlPermissionChecking['where']}", eZDBInterface::SERVER_SLAVE); $this->TempTablesCount = 1; $i = $this->TempTablesCount; } $nonExistingWordCount = count(array_unique($searchWordArray)) - count($wordIDHash) - $wildCardCount; $excludeWordCount = $searchWordCount - count($stopWordArray); if (count($stopWordArray) + $nonExistingWordCount == $searchWordCount && $this->TempTablesCount == 0) { // No words to search for, return empty result // cleanup temp tables $db->dropTempTableList($sqlPermissionChecking['temp_tables']); $db->dropTempTableList($this->getSavedTempTablesList()); return array("SearchResult" => array(), "SearchCount" => 0, "StopWordArray" => $stopWordArray); } $tmpTablesFrom = ""; $tmpTablesWhere = ""; /// tmp tables $tmpTableCount = $i; for ($i = 0; $i < $tmpTableCount; $i++) { $tmpTablesFrom .= $this->getSavedTempTableName($i); if ($i < $tmpTableCount - 1) { $tmpTablesFrom .= ", "; } } $tmpTablesSeparator = ''; if ($tmpTableCount > 0) { $tmpTablesSeparator = ', '; } $tmpTable0 = $this->getSavedTempTableName(0); for ($i = 1; $i < $tmpTableCount; $i++) { $tmpTableI = $this->getSavedTempTableName($i); $tmpTablesWhere .= " {$tmpTable0}.contentobject_id={$tmpTableI}.contentobject_id "; if ($i < $tmpTableCount - 1) { $tmpTablesWhere .= " AND "; } } $tmpTablesWhereExtra = ''; if ($tmpTableCount > 0) { $tmpTablesWhereExtra = "ezcontentobject.id={$tmpTable0}.contentobject_id AND"; } $and = ""; if ($tmpTableCount > 1) { $and = " AND "; } // Generate ORDER BY SQL $orderBySQLArray = $this->buildSortSQL($sortArray); $orderByFieldsSQL = $orderBySQLArray['sortingFields']; $sortWhereSQL = $orderBySQLArray['whereSQL']; $sortFromSQL = $orderBySQLArray['fromSQL']; $sortSelectSQL = $orderBySQLArray['selectSQL']; // Fetch data from table $searchQuery = ''; $searchQuery = "SELECT DISTINCT ezcontentobject.*, ezcontentclass.serialized_name_list as class_serialized_name_list,\n ezcontentobject_tree.*, ezcontentobject_name.name as name,\n ezcontentobject_name.real_translation {$sortSelectSQL}\n FROM\n {$tmpTablesFrom} {$tmpTablesSeparator}\n ezcontentobject\n INNER JOIN ezcontentclass ON (ezcontentclass.id = ezcontentobject.contentclass_id )\n INNER JOIN ezcontentobject_tree ON (ezcontentobject_tree.contentobject_id = ezcontentobject.id)\n INNER JOIN ezcontentobject_name ON (\n ezcontentobject_name.contentobject_id = ezcontentobject_tree.contentobject_id AND\n ezcontentobject_name.content_version = ezcontentobject_tree.contentobject_version\n )\n {$sortFromSQL}\n WHERE\n {$tmpTablesWhere} {$and}\n {$tmpTablesWhereExtra}\n ezcontentclass.version = '0' AND\n ezcontentobject_tree.node_id = ezcontentobject_tree.main_node_id AND\n " . eZContentLanguage::sqlFilter('ezcontentobject_name', 'ezcontentobject') . "\n {$showInvisibleNodesCond}\n {$sortWhereSQL}\n ORDER BY {$orderByFieldsSQL}"; // Count query $languageCond = eZContentLanguage::languagesSQLFilter('ezcontentobject'); if ($tmpTableCount == 0) { $searchCountQuery = "SELECT count( DISTINCT ezcontentobject.id ) AS count\n FROM\n ezcontentobject,\n ezcontentobject_tree\n WHERE\n ezcontentobject.id = ezcontentobject_tree.contentobject_id and\n ezcontentobject_tree.node_id = ezcontentobject_tree.main_node_id\n AND {$languageCond}\n {$showInvisibleNodesCond}"; } else { $searchCountQuery = "SELECT count( DISTINCT ezcontentobject.id ) AS count\n FROM {$tmpTablesFrom} {$tmpTablesSeparator}\n ezcontentobject\n WHERE {$tmpTablesWhere} {$and}\n {$tmpTablesWhereExtra}\n {$languageCond}"; } $objectRes = array(); $searchCount = 0; if ($nonExistingWordCount <= 0) { // execute search query $objectResArray = $db->arrayQuery($searchQuery, array("limit" => $searchLimit, "offset" => $searchOffset), eZDBInterface::SERVER_SLAVE); // execute search count query $objectCountRes = $db->arrayQuery($searchCountQuery, array(), eZDBInterface::SERVER_SLAVE); $objectRes = eZContentObjectTreeNode::makeObjectsArray($objectResArray); $searchCount = $objectCountRes[0]['count']; } else { $objectRes = array(); } // Drop tmp tables $db->dropTempTableList($sqlPermissionChecking['temp_tables']); $db->dropTempTableList($this->getSavedTempTablesList()); return array("SearchResult" => $objectRes, "SearchCount" => $searchCount, "StopWordArray" => $stopWordArray); } else { return array("SearchResult" => array(), "SearchCount" => 0, "StopWordArray" => array()); } }