| [ Index ] |
|
Code source de eZ Publish 3.9.0 |
1 <?php 2 // 3 // Definition of eZSearchEngine class 4 // 5 // Created on: <25-Jun-2002 13:09:57 bf> 6 // 7 // SOFTWARE NAME: eZ publish 8 // SOFTWARE RELEASE: 3.9.0 9 // BUILD VERSION: 17785 10 // COPYRIGHT NOTICE: Copyright (C) 1999-2006 eZ systems AS 11 // SOFTWARE LICENSE: GNU General Public License v2.0 12 // NOTICE: > 13 // This program is free software; you can redistribute it and/or 14 // modify it under the terms of version 2.0 of the GNU General 15 // Public License as published by the Free Software Foundation. 16 // 17 // This program is distributed in the hope that it will be useful, 18 // but WITHOUT ANY WARRANTY; without even the implied warranty of 19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 // GNU General Public License for more details. 21 // 22 // You should have received a copy of version 2.0 of the GNU General 23 // Public License along with this program; if not, write to the Free 24 // Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 25 // MA 02110-1301, USA. 26 // 27 // 28 29 /*! 30 \class eZSearchEngine ezsearch.php 31 32 */ 33 34 include_once ( "lib/ezdb/classes/ezdb.php" ); 35 include_once ( "kernel/classes/ezcontentobject.php" ); 36 include_once ( "lib/ezlocale/classes/ezdatetime.php" ); 37 include_once ( "kernel/classes/ezcontentobject.php" ); 38 include_once ( 'kernel/classes/ezcontentlanguage.php' ); 39 40 class eZSearchEngine 41 { 42 /*! 43 */ 44 function eZSearchEngine() 45 { 46 $generalFilter = array( 'subTreeTable' => '', 47 'searchDateQuery' => '', 48 'sectionQuery' => '', 49 'classQuery' => '', 50 'classAttributeQuery' => '', 51 'searchPartText' => '', 52 'subTreeSQL' => '', 53 'sqlPermissionChecking' => array( 'from' => '', 54 'where' => '' ) ); 55 $this->GeneralFilter = $generalFilter; 56 } 57 58 /*! 59 Adds an object to the search database. 60 */ 61 function addObject( &$contentObject, $uri ) 62 { 63 $contentObjectID = $contentObject->attribute( 'id' ); 64 $currentVersion =& $contentObject->currentVersion(); 65 66 $indexArray = array(); 67 $indexArrayOnlyWords = array(); 68 69 $wordCount = 0; 70 $placement = 0; 71 $previousWord = ''; 72 73 eZContentObject::recursionProtectionStart(); 74 foreach ( $currentVersion->contentObjectAttributes() as $attribute ) 75 { 76 $metaData = array(); 77 $classAttribute =& $attribute->contentClassAttribute(); 78 if ( $classAttribute->attribute( "is_searchable" ) == 1 ) 79 { 80 // Fetch attribute translations 81 $attributeTranslations = $attribute->fetchAttributeTranslations(); 82 83 foreach ( $attributeTranslations as $translation ) 84 { 85 $tmpMetaData = $translation->metaData(); 86 if( ! is_array( $tmpMetaData ) ) 87 { 88 $tmpMetaData = array( array( 'id' => $attribute->attribute( 'contentclass_attribute_identifier' ), 89 'text' => $tmpMetaData ) ); 90 } 91 $metaData = array_merge( $metaData, $tmpMetaData ); 92 } 93 94 foreach( $metaData as $metaDataPart ) 95 { 96 $text = eZSearchEngine::normalizeText( strip_tags( $metaDataPart['text'] ), true ); 97 98 // Split text on whitespace 99 if ( is_numeric( trim( $text ) ) ) 100 { 101 $integerValue = (int) $text; 102 } 103 else 104 { 105 $integerValue = 0; 106 } 107 $wordArray = split( " ", $text ); 108 109 foreach ( $wordArray as $word ) 110 { 111 if ( trim( $word ) != "" ) 112 { 113 $indexArray[] = array( 'Word' => $word, 114 'ContentClassAttributeID' => $attribute->attribute( 'contentclassattribute_id' ), 115 'identifier' => $metaDataPart['id'], 116 'integer_value' => $integerValue ); 117 $indexArrayOnlyWords[] = $word; 118 $wordCount++; 119 //if we have "www." before word than 120 //treat it as url and add additional entry to the index 121 if ( substr( strtolower($word), 0, 4 ) == 'www.' ) 122 { 123 $additionalUrlWord = substr( $word, 4 ); 124 $indexArray[] = array( 'Word' => $additionalUrlWord, 125 'ContentClassAttributeID' => $attribute->attribute( 'contentclassattribute_id' ), 126 'identifier' => $metaDataPart['id'], 127 'integer_value' => $integerValue ); 128 $indexArrayOnlyWords[] = $additionalUrlWord; 129 $wordCount++; 130 } 131 } 132 } 133 } 134 } 135 } 136 eZContentObject::recursionProtectionEnd(); 137 138 $indexArrayOnlyWords = array_unique( $indexArrayOnlyWords ); 139 140 $wordIDArray = $this->buildWordIDArray( $indexArrayOnlyWords ); 141 142 $db =& eZDB::instance(); 143 $db->begin(); 144 for( $arrayCount = 0; $arrayCount < $wordCount; $arrayCount += 1000 ) 145 { 146 $placement = $this->indexWords( $contentObject, array_slice( $indexArray, $arrayCount, 1000 ), $wordIDArray, $placement ); 147 } 148 $db->commit(); 149 } 150 151 /*! 152 \private 153 154 Build WordIDArray and update ezsearch_word table 155 156 \params words for object to add 157 158 \return wordIDArray 159 */ 160 function buildWordIDArray( &$indexArrayOnlyWords ) 161 { 162 $db =& eZDB::instance(); 163 164 // Initialize transformation system 165 include_once ( 'lib/ezi18n/classes/ezchartransform.php' ); 166 $trans =& eZCharTransform::instance(); 167 168 $wordCount = count( $indexArrayOnlyWords ); 169 $wordIDArray = array(); 170 $wordArray = array(); 171 // store the words in the index and remember the ID 172 $dbName = $db->databaseName(); 173 if ( $dbName == 'mysql' ) 174 { 175 $db->begin(); 176 for( $arrayCount = 0; $arrayCount < $wordCount; $arrayCount += 500 ) 177 { 178 // Fetch already indexed words from database 179 $wordArrayChuck = array_slice( $indexArrayOnlyWords, $arrayCount, 500 ); 180 $wordsString = implode( '\',\'', $wordArrayChuck ); 181 $wordRes = $db->arrayQuery( "SELECT * FROM ezsearch_word WHERE word IN ( '$wordsString' ) " ); 182 183 // Build a has of the existing words 184 $wordResCount = count( $wordRes ); 185 $existingWordArray = array(); 186 for ( $i = 0; $i < $wordResCount; $i++ ) 187 { 188 $wordIDArray[] = $wordRes[$i]['id']; 189 $existingWordArray[] = $wordRes[$i]['word']; 190 $wordArray[$wordRes[$i]['word']] = $wordRes[$i]['id']; 191 } 192 193 // Update the object count of existing words by one 194 $wordIDString = implode( ',', $wordIDArray ); 195 if ( count( $wordIDArray ) > 0 ) 196 $db->query( "UPDATE ezsearch_word SET object_count=( object_count + 1 ) WHERE id IN ( $wordIDString )" ); 197 198 // Insert if there is any news words 199 $newWordArray = array_diff( $wordArrayChuck, $existingWordArray ); 200 if ( count ($newWordArray) > 0 ) 201 { 202 $newWordString = implode( "', '1' ), ('", $newWordArray ); 203 $db->query( "INSERT INTO 204 ezsearch_word ( word, object_count ) 205 VALUES ('$newWordString', '1' )" ); 206 $newWordString = implode( "','", $newWordArray ); 207 $newWordRes = $db->arrayQuery( "select id,word from ezsearch_word where word in ( '$newWordString' ) " ); 208 $newWordCount = count( $newWordRes ); 209 for ( $i=0;$i<$newWordCount;++$i ) 210 { 211 $wordLowercase = $trans->transformByGroup( $newWordRes[$i]['word'], 'lowercase' ); 212 $wordArray[$newWordRes[$i]['word']] = $newWordRes[$i]['id']; 213 } 214 } 215 } 216 $db->commit(); 217 } 218 else 219 { 220 $db->begin(); 221 foreach ( $indexArrayOnlyWords as $indexWord ) 222 { 223 $indexWord = $trans->transformByGroup( $indexWord, 'lowercase' ); 224 $indexWord = $db->escapeString( $indexWord ); 225 226 // Store word if it does not exist. 227 $wordRes = array(); 228 229 $wordRes = $db->arrayQuery( "SELECT * FROM ezsearch_word WHERE word='$indexWord'" ); 230 231 if ( count( $wordRes ) == 1 ) 232 { 233 $wordID = $wordRes[0]["id"]; 234 $db->query( "UPDATE ezsearch_word SET object_count=( object_count + 1 ) WHERE id='$wordID'" ); 235 } 236 else 237 { 238 $db->query( "INSERT INTO 239 ezsearch_word ( word, object_count ) 240 VALUES ( '$indexWord', '1' )" ); 241 $wordID = $db->lastSerialID( "ezsearch_word", "id" ); 242 } 243 244 $wordArray[$indexWord] = $wordID; 245 } 246 $db->commit(); 247 } 248 249 return $wordArray; 250 } 251 252 /*! 253 \private 254 255 \param contentObject 256 \param indexArray 257 \param wordIDArray 258 \param placement 259 260 \return last placement 261 Index wordIndex 262 */ 263 function indexWords( &$contentObject, &$indexArray, &$wordIDArray, $placement = 0 ) 264 { 265 $db =& eZDB::instance(); 266 267 $contentObjectID = $contentObject->attribute( 'id' ); 268 269 // Count the total words in index text 270 $totalWordCount = count( $indexArray ); 271 272 /* // Needs to be rewritten 273 274 // Count the number of instances of each word 275 $wordCountArray = array_count_values( $indexArray ); 276 277 // Strip double words 278 $uniqueIndexArray = array_unique( $indexArray ); 279 280 // Count unique words 281 $uniqueWordCount = count( $uniqueIndexArray ); 282 */ 283 284 // Initialize transformation system 285 include_once ( 'lib/ezi18n/classes/ezchartransform.php' ); 286 $trans =& eZCharTransform::instance(); 287 288 $prevWordID = 0; 289 $nextWordID = 0; 290 $classID = $contentObject->attribute( 'contentclass_id' ); 291 $sectionID = $contentObject->attribute( 'section_id' ); 292 $published = $contentObject->attribute( 'published' ); 293 $valuesStringList = array(); 294 for ( $i = 0; $i < count( $indexArray ); $i++ ) 295 { 296 $indexWord = $indexArray[$i]['Word']; 297 $contentClassAttributeID = $indexArray[$i]['ContentClassAttributeID']; 298 $identifier = $indexArray[$i]['identifier']; 299 $integerValue = $indexArray[$i]['integer_value']; 300 $indexWord = $trans->transformByGroup( $indexWord, 'lowercase' ); 301 $wordID = $wordIDArray[$indexWord]; 302 303 if ( isset( $indexArray[$i+1] ) ) 304 { 305 $nextIndexWord = $trans->transformByGroup( $indexArray[$i+1]['Word'], 'lowercase' ); 306 $nextWordID = $wordIDArray[$nextIndexWord]; 307 } 308 else 309 $nextWordID = 0; 310 // print( "indexing word : $indexWord <br> " ); 311 312 // Calculate the relevans ranking for this word 313 // $frequency = ( $wordCountArray[$indexWord] / $totalWordCount ); 314 $frequency = 0; 315 $valuesStringList[] = " ( '$wordID', '$contentObjectID', '$frequency', '$placement', '$nextWordID', '$prevWordID', '$classID', '$contentClassAttributeID', '$published', '$sectionID', '$identifier', '$integerValue' ) "; 316 317 $prevWordID = $wordID; 318 $placement++; 319 } 320 $dbName = $db->databaseName(); 321 322 if ( $dbName == 'mysql' ) 323 { 324 if ( count( $valuesStringList ) > 0 ) 325 { 326 $valuesString = implode( ',', $valuesStringList ); 327 $db->query( "INSERT INTO 328 ezsearch_object_word_link 329 ( word_id, 330 contentobject_id, 331 frequency, 332 placement, 333 next_word_id, 334 prev_word_id, 335 contentclass_id, 336 contentclass_attribute_id, 337 published, 338 section_id, 339 identifier, 340 integer_value ) 341 VALUES $valuesString" ); 342 } 343 } 344 else 345 { 346 $db->begin(); 347 foreach ( array_keys( $valuesStringList ) as $key ) 348 { 349 $valuesString = $valuesStringList[$key]; 350 $db->query("INSERT INTO 351 ezsearch_object_word_link 352 ( word_id, 353 contentobject_id, 354 frequency, 355 placement, 356 next_word_id, 357 prev_word_id, 358 contentclass_id, 359 contentclass_attribute_id, 360 published, 361 section_id, 362 identifier, 363 integer_value ) 364 VALUES $valuesString" ); 365 } 366 $db->commit(); 367 } 368 369 return $placement; 370 } 371 372 /*! 373 \static 374 */ 375 function removeObject( $contentObject ) 376 { 377 $db =& eZDB::instance(); 378 $objectID = $contentObject->attribute( "id" ); 379 $doDelete = false; 380 $db->begin(); 381 382 if ( $db->databaseName() == 'mysql' ) 383 { 384 // fetch all the words and decrease the object count on all the words 385 $wordArray = $db->arrayQuery( "SELECT word_id FROM ezsearch_object_word_link WHERE contentobject_id='$objectID'" ); 386 $wordIDList = array(); 387 foreach ( $wordArray as $word ) 388 $wordIDList[] = $word["word_id"]; 389 if ( count( $wordIDList ) > 0 ) 390 { 391 $wordIDString = implode( ',', $wordIDList ); 392 $db->query( "UPDATE ezsearch_word SET object_count=( object_count - 1 ) WHERE id in ( $wordIDString )" ); 393 $doDelete = true; 394 } 395 } 396 else 397 { 398 $cnt = $db->arrayQuery( "SELECT COUNT( word_id ) AS cnt FROM ezsearch_object_word_link WHERE contentobject_id='$objectID'" ); 399 if ( $cnt[0]['cnt'] > 0 ) 400 { 401 $db->query( "UPDATE ezsearch_word SET object_count=( object_count - 1 ) WHERE id in ( SELECT word_id FROM ezsearch_object_word_link WHERE contentobject_id='$objectID' )" ); 402 $doDelete = true; 403 } 404 } 405 406 if ( $doDelete ) 407 { 408 $db->query( "DELETE FROM ezsearch_word WHERE object_count='0'" ); 409 $db->query( "DELETE FROM ezsearch_object_word_link WHERE contentobject_id='$objectID'" ); 410 } 411 $db->commit(); 412 } 413 414 /*! 415 Saves name of a temporary that has just been created, 416 for us to know its name when it's time to drop the table. 417 */ 418 function saveCreatedTempTableName( $index, $tableName ) 419 { 420 if ( isset( $this->CreatedTempTablesNames[$index] ) ) 421 eZDebug::writeWarning( "CreatedTempTablesNames[\$index] already exists " . 422 "and contains '" . $this->CreatedTempTablesNames[$index] . "'" ); 423 $this->CreatedTempTablesNames[$index] = $tableName; 424 } 425 426 /*! 427 \return Given table name from the list of saved temporary tables names by its index. 428 \see saveCreatedTempTableName() 429 */ 430 function getSavedTempTableName( $index ) 431 { 432 return $this->CreatedTempTablesNames[$index]; 433 } 434 435 /*! 436 \return List of saved temporary tables names. 437 \see saveCreatedTempTableName() 438 */ 439 function getSavedTempTablesList() 440 { 441 return $this->CreatedTempTablesNames; 442 } 443 444 /*! 445 Runs a query to the search engine. 446 */ 447 function search( $searchText, $params = array(), $searchTypes = array() ) 448 { 449 if ( count( $searchTypes ) == 0 ) 450 { 451 $searchTypes['general'] = array(); 452 $searchTypes['subtype'] = array(); 453 $searchTypes['and'] = array(); 454 } 455 else if ( !isset( $searchTypes['general'] ) ) 456 { 457 $searchTypes['general'] = array(); 458 } 459 $allowSearch = true; 460 if ( trim( $searchText ) == '' ) 461 { 462 $ini =& eZINI::instance(); 463 if ( $ini->variable( 'SearchSettings', 'AllowEmptySearch' ) != 'enabled' ) 464 $allowSearch = false; 465 if ( isset( $params['AllowEmptySearch'] ) ) 466 $allowSearch = $params['AllowEmptySearch']; 467 } 468 if ( $allowSearch ) 469 { 470 $searchText = $this->normalizeText( $searchText, false ); 471 $db =& eZDB::instance(); 472 473 $nonExistingWordArray = array(); 474 $searchTypeMap = array( 'class' => 'SearchContentClassID', 475 'publishdate' => 'SearchDate', 476 'subtree' => 'SearchSubTreeArray' ); 477 478 foreach ( $searchTypes['general'] as $searchType ) 479 { 480 $params[$searchTypeMap[$searchType['subtype']]] = $searchType['value']; 481 } 482 483 if ( isset( $params['SearchOffset'] ) ) 484 $searchOffset = $params['SearchOffset']; 485 else 486 $searchOffset = 0; 487 488 if ( isset( $params['SearchLimit'] ) ) 489 $searchLimit = $params['SearchLimit']; 490 else 491 $searchLimit = 10; 492 493 if ( isset( $params['SearchContentClassID'] ) ) 494 $searchContentClassID = $params['SearchContentClassID']; 495 else 496 $searchContentClassID = -1; 497 498 if ( isset( $params['SearchSectionID'] ) ) 499 $searchSectionID = $params['SearchSectionID']; 500 else 501 $searchSectionID = -1; 502 503 if ( isset( $params['SearchDate'] ) ) 504 $searchDate = $params['SearchDate']; 505 else 506 $searchDate = -1; 507 508 if ( isset( $params['SearchTimestamp'] ) ) 509 $searchTimestamp = $params['SearchTimestamp']; 510 else 511 $searchTimestamp = false; 512 513 if ( isset( $params['SearchContentClassAttributeID'] ) ) 514 $searchContentClassAttributeID = $params['SearchContentClassAttributeID']; 515 else 516 $searchContentClassAttributeID = -1; 517 518 if ( isset( $params['SearchSubTreeArray'] ) ) 519 $subTreeArray = $params['SearchSubTreeArray']; 520 else 521 $subTreeArray = array(); 522 523 if ( isset( $params['SortArray'] ) ) 524 $sortArray = $params['SortArray']; 525 else 526 $sortArray = array(); 527 528 $ignoreVisibility = isset( $params['IgnoreVisibility'] ) ? $params['IgnoreVisibility'] : false; 529 530 // strip multiple spaces 531 $searchText = preg_replace( "(\s+)", " ", $searchText ); 532 533 // find the phrases 534 /* $numQuotes = substr_count( $searchText, "\"" ); 535 536 $phraseTextArray = array(); 537 $fullText = $searchText; 538 $nonPhraseText =''; 539 // $fullText = ''; 540 $postPhraseText = $fullText; 541 $pos = 0; 542 if ( ( $numQuotes > 0 ) and ( ( $numQuotes % 2 ) == 0 ) ) 543 { 544 for ( $i = 0; $i < ( $numQuotes / 2 ); $i ++ ) 545 { 546 $quotePosStart = strpos( $searchText, '"', $pos ); 547 $quotePosEnd = strpos( $searchText, '"', $quotePosStart + 1 ); 548 549 $prePhraseText = substr( $searchText, $pos, $quotePosStart - $pos ); 550 $postPhraseText = substr( $searchText, $quotePosEnd +1 ); 551 $phraseText = substr( $searchText, $quotePosStart + 1, $quotePosEnd - $quotePosStart - 1 ); 552 553 $phraseTextArray[] = $phraseText; 554 // $fullText .= $prePhraseText; 555 $nonPhraseText .= $prePhraseText; 556 $pos = $quotePosEnd + 1; 557 } 558 } 559 $nonPhraseText .= $postPhraseText; 560 */ 561 $phrasesResult = $this->getPhrases( $searchText ); 562 $phraseTextArray = $phrasesResult['phrases']; 563 $nonPhraseText = $phrasesResult['nonPhraseText']; 564 $fullText = $phrasesResult['fullText']; 565 566 $sectionQuery = ''; 567 if ( is_numeric( $searchSectionID ) and $searchSectionID > 0 ) 568 { 569 $sectionQuery = "ezsearch_object_word_link.section_id = '$searchSectionID' AND "; 570 } 571 else if ( is_array( $searchSectionID ) ) 572 { 573 // Build query for searching in an array of sections 574 $sectionString = implode( ', ', $searchSectionID ); 575 $sectionQuery = "ezsearch_object_word_link.section_id IN ( $sectionString ) AND "; 576 } 577 578 $searchDateQuery = ''; 579 if ( ( is_numeric( $searchDate ) and $searchDate > 0 ) or 580 $searchTimestamp ) 581 { 582 $date = new eZDateTime(); 583 $timestamp = $date->timeStamp(); 584 $day = $date->attribute('day'); 585 $month = $date->attribute('month'); 586 $year = $date->attribute('year'); 587 $publishedDateStop = false; 588 if ( $searchTimestamp ) 589 { 590 if ( is_array( $searchTimestamp ) ) 591 { 592 $publishedDate = $searchTimestamp[0]; 593 $publishedDateStop = $searchTimestamp[1]; 594 } 595 else 596 $publishedDate = $searchTimestamp; 597 } 598 else 599 { 600 switch ( $searchDate ) 601 { 602 case 1: 603 { 604 $adjustment = 24*60*60; //seconds for one day 605 $publishedDate = $timestamp - $adjustment; 606 } break; 607 case 2: 608 { 609 $adjustment = 7*24*60*60; //seconds for one week 610 $publishedDate = $timestamp - $adjustment; 611 } break; 612 case 3: 613 { 614 $adjustment = 31*24*60*60; //seconds for one month 615 $publishedDate = $timestamp - $adjustment; 616 } break; 617 case 4: 618 { 619 $adjustment = 3*31*24*60*60; //seconds for three months 620 $publishedDate = $timestamp - $adjustment; 621 } break; 622 case 5: 623 { 624 $adjustment = 365*24*60*60; //seconds for one year 625 $publishedDate = $timestamp - $adjustment; 626 } break; 627 default: 628 { 629 $publishedDate = $date->timeStamp(); 630 } 631 } 632 } 633 $searchDateQuery = "ezsearch_object_word_link.published >= '$publishedDate' AND "; 634 if ( $publishedDateStop ) 635 $searchDateQuery .= "ezsearch_object_word_link.published <= '$publishedDateStop' AND "; 636 $this->GeneralFilter['searchDateQuery'] = $searchDateQuery; 637 } 638 639 $classQuery = ""; 640 if ( is_numeric( $searchContentClassID ) and $searchContentClassID > 0 ) 641 { 642 // Build query for searching in one class 643 $classQuery = "ezsearch_object_word_link.contentclass_id = '$searchContentClassID' AND "; 644 $this->GeneralFilter['classAttributeQuery'] = $classQuery; 645 } 646 else if ( is_array( $searchContentClassID ) ) 647 { 648 // Build query for searching in a number of classes 649 $classString = $db->implodeWithTypeCast( ', ', $searchContentClassID, 'int' ); 650 $classQuery = "ezsearch_object_word_link.contentclass_id IN ( $classString ) AND "; 651 $this->GeneralFilter['classAttributeQuery'] = $classQuery; 652 } 653 654 $classAttributeQuery = ""; 655 if ( is_numeric( $searchContentClassAttributeID ) and $searchContentClassAttributeID > 0 ) 656 { 657 $classAttributeQuery = "ezsearch_object_word_link.contentclass_attribute_id = '$searchContentClassAttributeID' AND "; 658 } 659 else if ( is_array( $searchContentClassAttributeID ) ) 660 { 661 // Build query for searching in a number of attributes 662 $attributeString = implode( ', ', $searchContentClassAttributeID ); 663 $classAttributeQuery = "ezsearch_object_word_link.contentclass_attribute_id IN ( $attributeString ) AND "; 664 } 665 666 // Get the total number of objects 667 $totalObjectCount = $this->fetchTotalObjectCount(); 668 669 $searchPartsArray = array(); 670 $wordIDHash = array(); 671 $wildCardCount = 0; 672 if ( trim( $searchText ) != '' ) 673 { 674 $wordIDArrays = $this->prepareWordIDArrays( $searchText ); 675 $wordIDArray = $wordIDArrays['wordIDArray']; 676 $wordIDHash = $wordIDArrays['wordIDHash']; 677 $wildIDArray = $wordIDArrays['wildIDArray']; 678 $wildCardCount = $wordIDArrays['wildCardCount']; 679 $searchPartsArray = $this->buildSearchPartArray( $phraseTextArray, $nonPhraseText, $wordIDHash, $wildIDArray ); 680 } 681 682 /// OR search, not used in this version 683 $doOrSearch = false; 684 685 if ( $doOrSearch == true ) 686 { 687 // build fulltext search SQL part 688 $searchWordArray = $this->splitString( $fullText ); 689 $fullTextSQL = ""; 690 if ( count( $searchWordArray ) > 0 ) 691 { 692 $i = 0; 693 // Build the word query string 694 foreach ( $searchWordArray as $searchWord ) 695 { 696 $wordID = null; 697 if ( isset( $wordIDHash[$searchWord] ) ) 698 $wordID = $wordIDHash[$searchWord]['id']; 699 700 if ( is_numeric( $wordID ) and ( $wordID > 0 ) ) 701 { 702 if ( $i == 0 ) 703 $fullTextSQL .= "ezsearch_object_word_link.word_id='$wordID' "; 704 else 705 $fullTextSQL .= " OR ezsearch_object_word_link.word_id='$wordID' "; 706 } 707 else 708 { 709 $nonExistingWordArray[] = $searchWord; 710 } 711 $i++; 712 } 713 $fullTextSQL = " ( $fullTextSQL ) AND "; 714 } 715 } 716 717 // Search only in specific sub trees 718 $subTreeSQL = ""; 719 $subTreeTable = ""; 720 if ( count( $subTreeArray ) > 0 ) 721 { 722 // Fetch path_string value to use when searching subtrees 723 $i = 0; 724 $doSubTreeSearch = false; 725 $subTreeNodeSQL = ''; 726 foreach ( $subTreeArray as $nodeID ) 727 { 728 if ( is_numeric( $nodeID ) and ( $nodeID > 0 ) ) 729 { 730 $subTreeNodeSQL .= " $nodeID"; 731 732 if ( isset( $subTreeArray[$i + 1] ) and 733 is_numeric( $subTreeArray[$i + 1] ) ) 734 $subTreeNodeSQL .= ", "; 735 736 $doSubTreeSearch = true; 737 } 738 $i++; 739 } 740 741 if ( $doSubTreeSearch == true ) 742 { 743 744 $subTreeNodeSQL = "( " . $subTreeNodeSQL; 745 // $subTreeTable = ", ezcontentobject_tree "; 746 $subTreeTable = ''; 747 $subTreeNodeSQL .= " ) "; 748 $nodeQuery = "SELECT node_id, path_string FROM ezcontentobject_tree WHERE node_id IN $subTreeNodeSQL"; 749 750 // Build SQL subtre search query 751 $subTreeSQL = " ( "; 752 753 $nodeArray = $db->arrayQuery( $nodeQuery ); 754 $i = 0; 755 foreach ( $nodeArray as $node ) 756 { 757 $pathString = $node['path_string']; 758 759 $subStringString = $db->subString( 'ezcontentobject_tree.path_string', 1, strlen( $pathString ) ); 760 761 $subTreeSQL .= " $subStringString = '$pathString' "; 762 763 if ( $i < ( count( $nodeArray ) -1 ) ) 764 $subTreeSQL .= " OR "; 765 $i++; 766 } 767 $subTreeSQL .= " ) AND "; 768 $this->GeneralFilter['subTreeTable'] = $subTreeTable; 769 $this->GeneralFilter['subTreeSQL'] = $subTreeSQL; 770 771 } 772 } 773 774 $limitation = false; 775 if ( isset( $params['Limitation'] ) ) 776 { 777 $limitation = $params['Limitation']; 778 } 779 780 $limitationList = eZContentObjectTreeNode::getLimitationList( $limitation ); 781 $sqlPermissionChecking = eZContentObjectTreeNode::createPermissionCheckingSQL( $limitationList ); 782 $this->GeneralFilter['sqlPermissionChecking'] = $sqlPermissionChecking; 783 784 $useVersionName = true; 785 if ( $useVersionName ) 786 { 787 $versionNameTables = ', ezcontentobject_name '; 788 $versionNameTargets = ', ezcontentobject_name.name as name, ezcontentobject_name.real_translation '; 789 790 $versionNameJoins = " and ezcontentobject_tree.contentobject_id = ezcontentobject_name.contentobject_id and 791 ezcontentobject_tree.contentobject_version = ezcontentobject_name.content_version and "; 792 $versionNameJoins .= eZContentLanguage::sqlFilter( 'ezcontentobject_name', 'ezcontentobject' ); 793 } 794 795 /// Only support AND search at this time 796 // build fulltext search SQL part 797 $searchWordArray = $this->splitString( $fullText ); 798 $searchWordCount = count( $searchWordArray ); 799 $fullTextSQL = ""; 800 $stopWordArray = array( ); 801 $ini =& eZINI::instance(); 802 803 $tmpTableCount = 0; 804 $i = 0; 805 foreach ( $searchTypes['and'] as $searchType ) 806 { 807 $methodName = $this->constructMethodName( $searchType ); 808 $intermediateResult = $this->callMethod( $methodName, array( $searchType ) ); 809 if ( $intermediateResult == false ) 810 { 811 return array( "SearchResult" => array(), 812 "SearchCount" => 0, 813 "StopWordArray" => array() ); 814 } 815 } 816 817 // Do not execute search if site.ini:[SearchSettings]->AllowEmptySearch is enabled, but no conditions are set. 818 if ( !$searchDateQuery && 819 !$sectionQuery && 820 !$classQuery && 821 !$classAttributeQuery && 822 !$searchPartsArray && 823 !$subTreeSQL ) 824 { 825 return array( "SearchResult" => array(), 826 "SearchCount" => 0, 827 "StopWordArray" => array() ); 828 } 829 830 $i = $this->TempTablesCount; 831 832 // Loop every word and insert result in temporary table 833 834 // Determine whether we should search invisible nodes. 835 include_once ( 'kernel/classes/ezcontentobjecttreenode.php' ); 836 $showInvisibleNodesCond = eZContentObjectTreeNode::createShowInvisibleSQLString( !$ignoreVisibility ); 837 838 foreach ( $searchPartsArray as $searchPart ) 839 { 840 $stopWordThresholdValue = 100; 841 if ( $ini->hasVariable( 'SearchSettings', 'StopWordThresholdValue' ) ) 842 $stopWordThresholdValue = $ini->variable( 'SearchSettings', 'StopWordThresholdValue' ); 843 844 $stopWordThresholdPercent = 60; 845 if ( $ini->hasVariable( 'SearchSettings', 'StopWordThresholdPercent' ) ) 846 $stopWordThresholdPercent = $ini->variable( 'SearchSettings', 'StopWordThresholdPercent' ); 847 848 $searchThresholdValue = $totalObjectCount; 849 if ( $totalObjectCount > $stopWordThresholdValue ) 850 { 851 $searchThresholdValue = (int)( $totalObjectCount * ( $stopWordThresholdPercent / 100 ) ); 852 } 853 854 // do not search words that are too frequent 855 if ( $searchPart['object_count'] < $searchThresholdValue ) 856 { 857 $tmpTableCount++; 858 $searchPartText = $searchPart['sql_part']; 859 if ( $i == 0 ) 860 { 861 $table = $db->generateUniqueTempTableName( 'ezsearch_tmp_%_0' ); 862 $this->saveCreatedTempTableName( 0, $table ); 863 $db->createTempTable( "CREATE TEMPORARY TABLE $table ( contentobject_id int primary key not null, published int )" ); 864 $db->query( "INSERT INTO $table SELECT DISTINCT ezsearch_object_word_link.contentobject_id, ezsearch_object_word_link.published 865 FROM ezcontentobject, 866 ezsearch_object_word_link 867 $subTreeTable, 868 ezcontentclass, 869 ezcontentobject_tree 870 $sqlPermissionChecking[from] 871 WHERE 872 $searchDateQuery 873 $sectionQuery 874 $classQuery 875 $classAttributeQuery 876 $searchPartText 877 $subTreeSQL 878 ezcontentobject.id=ezsearch_object_word_link.contentobject_id and 879 ezcontentobject.contentclass_id = ezcontentclass.id and 880 ezcontentclass.version = '0' and 881 ezcontentobject.id = ezcontentobject_tree.contentobject_id and 882 ezcontentobject_tree.node_id = ezcontentobject_tree.main_node_id 883 $showInvisibleNodesCond 884 $sqlPermissionChecking[where]" ); 885 } 886 else 887 { 888 $table = $db->generateUniqueTempTableName( "ezsearch_tmp_%_$i" ); 889 $this->saveCreatedTempTableName( $i, $table ); 890 891 $tmpTable0 = $this->getSavedTempTableName( 0 ); 892 $db->createTempTable( "CREATE TEMPORARY TABLE $table ( contentobject_id int primary key not null, published int )" ); 893 $db->query( "INSERT INTO $table SELECT DISTINCT ezsearch_object_word_link.contentobject_id, ezsearch_object_word_link.published 894 FROM 895 ezcontentobject, 896 ezsearch_object_word_link 897 $subTreeTable, 898 ezcontentclass, 899 ezcontentobject_tree, 900 $tmpTable0 901 $sqlPermissionChecking[from] 902 WHERE 903 $tmpTable0.contentobject_id=ezsearch_object_word_link.contentobject_id AND 904 $searchDateQuery 905 $sectionQuery 906 $classQuery 907 $classAttributeQuery 908 $searchPartText 909 $subTreeSQL 910 ezcontentobject.id=ezsearch_object_word_link.contentobject_id and 911 ezcontentobject.contentclass_id = ezcontentclass.id and 912 ezcontentclass.version = '0' and 913 ezcontentobject.id = ezcontentobject_tree.contentobject_id and 914 ezcontentobject_tree.node_id = ezcontentobject_tree.main_node_id 915 $showInvisibleNodesCond 916 $sqlPermissionChecking[where]" ); 917 } 918 $i++; 919 } 920 else 921 { 922 $stopWordArray[] = array( 'word' => $searchPart['text'] ); 923 } 924 } 925 926 if ( $searchPartsArray === null && $this->TempTablesCount == 0 ) 927 { 928 $table = $db->generateUniqueTempTableName( 'ezsearch_tmp_%_0' ); 929 $this->saveCreatedTempTableName( 0, $table ); 930 $db->createTempTable( "CREATE TEMPORARY TABLE $table ( contentobject_id int primary key not null, published int )" ); 931 $db->query( "INSERT INTO $table SELECT DISTINCT ezsearch_object_word_link.contentobject_id, ezsearch_object_word_link.published 932 FROM ezcontentobject, 933 ezsearch_object_word_link 934 $subTreeTable, 935 ezcontentclass, 936 ezcontentobject_tree 937 $sqlPermissionChecking[from] 938 WHERE 939 $searchDateQuery 940 $sectionQuery 941 $classQuery 942 $classAttributeQuery 943 $subTreeSQL 944 ezcontentobject.id=ezsearch_object_word_link.contentobject_id and 945 ezcontentobject.contentclass_id = ezcontentclass.id and 946 ezcontentclass.version = '0' and 947 ezcontentobject.id = ezcontentobject_tree.contentobject_id and 948 ezcontentobject_tree.node_id = ezcontentobject_tree.main_node_id 949 $showInvisibleNodesCond 950 $sqlPermissionChecking[where]" ); 951 $this->TempTablesCount = 1; 952 $i = $this->TempTablesCount; 953 } 954 955 $nonExistingWordCount = count( array_unique( $searchWordArray ) ) - count( $wordIDHash ) - $wildCardCount; 956 $excludeWordCount = $searchWordCount - count( $stopWordArray ); 957 958 if ( ( count( $stopWordArray ) + $nonExistingWordCount ) == $searchWordCount && $this->TempTablesCount == 0 ) 959 { 960 // No words to search for, return empty result 961 return array( "SearchResult" => array(), 962 "SearchCount" => 0, 963 "StopWordArray" => $stopWordArray ); 964 } 965 $tmpTablesFrom = ""; 966 $tmpTablesWhere = ""; 967 /// tmp tables 968 $tmpTableCount = $i; 969 for ( $i = 0; $i < $tmpTableCount; $i++ ) 970 { 971 $tmpTablesFrom .= $this->getSavedTempTableName( $i ); 972 if ( $i < ( $tmpTableCount - 1 ) ) 973 $tmpTablesFrom .= ", "; 974 975 } 976 $tmpTablesSeparator = ''; 977 if ( $tmpTableCount > 0 ) 978 { 979 $tmpTablesSeparator = ','; 980 } 981 982 $tmpTable0 = $this->getSavedTempTableName( 0 ); 983 for ( $i = 1; $i < $tmpTableCount; $i++ ) 984 { 985 $tmpTableI = $this->getSavedTempTableName( $i ); 986 $tmpTablesWhere .= " $tmpTable0.contentobject_id=$tmpTableI.contentobject_id "; 987 if ( $i < ( $tmpTableCount - 1 ) ) 988 $tmpTablesWhere .= " AND "; 989 } 990 $tmpTablesWhereExtra = ''; 991 if ( $tmpTableCount > 0 ) 992 { 993 $tmpTablesWhereExtra = "ezcontentobject.id=$tmpTable0.contentobject_id AND"; 994 } 995 996 $and = ""; 997 if ( $tmpTableCount > 1 ) 998 $and = " AND "; 999 1000 // Generate ORDER BY SQL 1001 $orderBySQLArray = $this->buildSortSQL( $sortArray ); 1002 $orderByFieldsSQL = $orderBySQLArray['sortingFields']; 1003 $sortWhereSQL = $orderBySQLArray['whereSQL']; 1004 $sortFromSQL = $orderBySQLArray['fromSQL']; 1005 1006 // Fetch data from table 1007 $searchQuery =''; 1008 $dbName = $db->databaseName(); 1009 if ( $dbName == 'mysql' ) 1010 { 1011 $searchQuery = "SELECT DISTINCT ezcontentobject.*, ezcontentclass.serialized_name_list as class_serialized_name_list, ezcontentobject_tree.* 1012 $versionNameTargets 1013 FROM 1014 $tmpTablesFrom $tmpTablesSeparator 1015 ezcontentobject, 1016 ezcontentclass, 1017 ezcontentobject_tree 1018 $versionNameTables 1019 $sortFromSQL 1020 WHERE 1021 $tmpTablesWhere $and 1022 $tmpTablesWhereExtra 1023 ezcontentobject.contentclass_id = ezcontentclass.id and 1024 ezcontentclass.version = '0' and 1025 ezcontentobject.id = ezcontentobject_tree.contentobject_id and 1026 ezcontentobject_tree.node_id = ezcontentobject_tree.main_node_id 1027 $versionNameJoins 1028 $showInvisibleNodesCond 1029 $sortWhereSQL 1030 ORDER BY $orderByFieldsSQL"; 1031 if ( $tmpTableCount == 0 ) 1032 { 1033 $searchCountQuery = "SELECT count( DISTINCT ezcontentobject.id ) AS count 1034 FROM 1035 ezcontentobject, 1036 ezcontentclass, 1037 ezcontentobject_tree 1038 $versionNameTables 1039 WHERE 1040 $emptyWhere 1041 ezcontentobject.contentclass_id = ezcontentclass.id and 1042 ezcontentclass.version = '0' and 1043 ezcontentobject.id = ezcontentobject_tree.contentobject_id and 1044 ezcontentobject_tree.node_id = ezcontentobject_tree.main_node_id 1045 $versionNameJoins 1046 $showInvisibleNodesCond 1047 $sortWhereSQL"; 1048 } 1049 } 1050 else 1051 { 1052 $searchQuery = "SELECT DISTINCT ezcontentobject.*, ezcontentclass.serialized_name_list as class_serialized_name_list, ezcontentobject_tree.* 1053 $versionNameTargets 1054 FROM 1055 $tmpTablesFrom $tmpTablesSeparator 1056 ezcontentobject, 1057 ezcontentclass, 1058 ezcontentobject_tree 1059 $versionNameTables 1060 WHERE 1061 $tmpTablesWhere $and 1062 $tmpTablesWhereExtra 1063 ezcontentobject.contentclass_id = ezcontentclass.id and 1064 ezcontentclass.version = '0' and 1065 ezcontentobject.id = ezcontentobject_tree.contentobject_id and 1066 ezcontentobject_tree.node_id = ezcontentobject_tree.main_node_id 1067 $versionNameJoins 1068 "; 1069 if ( $tmpTableCount == 0 ) 1070 { 1071 $searchCountQuery = "SELECT count( DISTINCT ezcontentobject.id ) AS count 1072 FROM 1073 ezcontentobject, 1074 ezcontentclass, 1075 ezcontentobject_tree 1076 $versionNameTables 1077 WHERE 1078 ezcontentobject.contentclass_id = ezcontentclass.id and 1079 ezcontentclass.version = '0' and 1080 ezcontentobject.id = ezcontentobject_tree.contentobject_id and 1081 ezcontentobject_tree.node_id = ezcontentobject_tree.main_node_id 1082 $versionNameJoins 1083 "; 1084 } 1085 } 1086 // Count query 1087 $where = "WHERE"; 1088 if ( $tmpTableCount == 1 ) 1089 $where = ""; 1090 if ( $tmpTableCount > 0 ) 1091 { 1092 $searchCountQuery = "SELECT count( * ) AS count FROM $tmpTablesFrom $where $tmpTablesWhere "; 1093 } 1094 1095 $objectRes = array(); 1096 $searchCount = 0; 1097 1098 if ( $nonExistingWordCount <= 0 ) 1099 { 1100 // execute search query 1101 $objectResArray = $db->arrayQuery( $searchQuery, array( "limit" => $searchLimit, "offset" => $searchOffset ) ); 1102 // execute search count query 1103 $objectCountRes = $db->arrayQuery( $searchCountQuery ); 1104 $objectRes = eZContentObjectTreeNode::makeObjectsArray( $objectResArray ); 1105 $searchCount = $objectCountRes[0]['count']; 1106 } 1107 else 1108 $objectRes = array(); 1109 1110 // Drop tmp tables 1111 foreach ( $this->getSavedTempTablesList() as $table ) 1112 $db->dropTempTable( "DROP TABLE $table" ); 1113 1114 return array( "SearchResult" => $objectRes, 1115 "SearchCount" => $searchCount, 1116 "StopWordArray" => $stopWordArray ); 1117 } 1118 else 1119 { 1120 return array( "SearchResult" => array(), 1121 "SearchCount" => 0, 1122 "StopWordArray" => array() ); 1123 } 1124 ini_set( "OCI_COMMIT_ON_SUCCESS", 1 ); 1125 } 1126 1127 /*! 1128 \private 1129 \return an array of ORDER BY SQL 1130 */ 1131 function buildSortSQL( $sortArray ) 1132 { 1133 $sortCount = 0; 1134 $sortList = false; 1135 if ( isset( $sortArray ) and 1136 is_array( $sortArray ) and 1137 count( $sortArray ) > 0 ) 1138 { 1139 $sortList = $sortArray; 1140 if ( count( $sortList ) > 1 and 1141 !is_array( $sortList[0] ) ) 1142 { 1143 $sortList = array( $sortList ); 1144 } 1145 } 1146 $attributeJoinCount = 0; 1147 $attributeFromSQL = ""; 1148 $attributeWereSQL = ""; 1149 if ( $sortList !== false ) 1150 { 1151 $sortingFields = ''; 1152 foreach ( $sortList as $sortBy ) 1153 { 1154 if ( is_array( $sortBy ) and count( $sortBy ) > 0 ) 1155 { 1156 if ( $sortCount > 0 ) 1157 $sortingFields .= ', '; 1158 $sortField = $sortBy[0]; 1159 switch ( $sortField ) 1160 { 1161 case 'path': 1162 { 1163 $sortingFields .= 'path_string'; 1164 } break; 1165 case 'published': 1166 { 1167 $sortingFields .= 'ezcontentobject.published'; 1168 } break; 1169 case 'modified': 1170 { 1171 $sortingFields .= 'ezcontentobject.modified'; 1172 } break; 1173 case 'section': 1174 { 1175 $sortingFields .= 'ezcontentobject.section_id'; 1176 } break; 1177 case 'depth': 1178 { 1179 $sortingFields .= 'depth'; 1180 } break; 1181 case 'class_identifier': 1182 { 1183 $sortingFields .= 'ezcontentclass.identifier'; 1184 } break; 1185 case 'class_name': 1186 { 1187 include_once( 'kernel/classes/ezcontentobjectname.php' ); 1188 $classNameFilter = eZContentClassName::sqlFilter(); 1189 $sortingFields .= $classNameFilter['nameField']; 1190 $attributeFromSQL .= ", $classNameFilter[from]"; 1191 $attributeWhereSQL .= "$classNameFilter[where] AND "; 1192 } break; 1193 case 'priority': 1194 { 1195 $sortingFields .= 'ezcontentobject_tree.priority'; 1196 } break; 1197 case 'name': 1198 { 1199 $sortingFields .= 'ezcontentobject_name.name'; 1200 } break; 1201 case 'attribute': 1202 { 1203 $sortClassID = $sortBy[2]; 1204 // Look up datatype for sorting 1205 $sortDataType = eZContentObjectTreeNode::sortKeyByClassAttributeID( $sortClassID ); 1206 1207 $sortKey = false; 1208 if ( $sortDataType == 'string' ) 1209 { 1210 $sortKey = 'sort_key_string'; 1211 } 1212 else 1213 { 1214 $sortKey = 'sort_key_int'; 1215 } 1216 1217 $sortingFields .= "a$attributeJoinCount.$sortKey"; 1218 $attributeFromSQL .= ", ezcontentobject_attribute as a$attributeJoinCount"; 1219 $attributeWereSQL .= " AND a$attributeJoinCount.contentobject_id = ezcontentobject.id AND 1220 a$attributeJoinCount.contentclassattribute_id = $sortClassID AND 1221 a$attributeJoinCount.version = ezcontentobject_name.content_version"; 1222 1223 $attributeJoinCount++; 1224 }break; 1225 1226 default: 1227 { 1228 eZDebug::writeWarning( 'Unknown sort field: ' . $sortField, 'eZContentObjectTreeNode::subTree' ); 1229 continue; 1230 }; 1231 } 1232 $sortOrder = true; // true is ascending 1233 if ( isset( $sortBy[1] ) ) 1234 $sortOrder = $sortBy[1]; 1235 $sortingFields .= $sortOrder ? " ASC" : " DESC"; 1236 ++$sortCount; 1237 } 1238 } 1239 } 1240 1241 // Should we sort? 1242 if ( $sortCount == 0 ) 1243 { 1244 $sortingFields = " ezcontentobject.published ASC"; 1245 } 1246 1247 return array( 'sortingFields' => $sortingFields, 1248 'fromSQL' => $attributeFromSQL, 1249 'whereSQL' => $attributeWereSQL ); 1250 } 1251 1252 /*! 1253 \private 1254 \return Returns an sql query part for one word 1255 */ 1256 function buildSqlPartForWord( $wordID, $identifier = false ) 1257 { 1258 $fullTextSQL = "ezsearch_object_word_link.word_id='$wordID' AND "; 1259 if ( $identifier ) 1260 { 1261 $fullTextSQL .= "ezsearch_object_word_link.identifier='$identifier' AND "; 1262 } 1263 1264 return $fullTextSQL; 1265 } 1266 1267 /*! 1268 \private 1269 \return Returns an sql query part for a phrase 1270 */ 1271 1272 function buildPhraseSqlQueryPart( $phraseIDArray, $identifier = false ) 1273 { 1274 $phraseSearchSQLArray = array(); 1275 $wordCount = count( $phraseIDArray ); 1276 for ( $i = 0; $i < $wordCount; $i++ ) 1277 { 1278 $wordID = $phraseIDArray[$i]; 1279 $phraseSearchSQL = ""; 1280 if ( is_numeric( $wordID ) and ( $wordID > 0 ) ) 1281 { 1282 $phraseSearchSQL = " ( ezsearch_object_word_link.word_id='$wordID' "; 1283 if ( $i < ( $wordCount - 1 ) ) 1284 { 1285 $nextWordID = $phraseIDArray[$i+1]; 1286 $phraseSearchSQL .= " AND ezsearch_object_word_link.next_word_id='$nextWordID' "; 1287 } 1288 if ( $i > 0 ) 1289 { 1290 $prevWordID = $phraseIDArray[$i-1]; 1291 $phraseSearchSQL .= " AND ezsearch_object_word_link.prev_word_id='$prevWordID' "; 1292 } 1293 if ( $identifier ) 1294 { 1295 $phraseSearchSQL .= " AND ezsearch_object_word_link.identifier='$identifier' "; 1296 } 1297 $phraseSearchSQL .= " ) "; 1298 } 1299 else 1300 { 1301 $nonExistingWordArray[] = $searchWord; 1302 } 1303 $prevWord = $wordID; 1304 $phraseSearchSQLArray[] = $phraseSearchSQL; 1305 } 1306 1307 return $phraseSearchSQLArray; 1308 } 1309 1310 /*! 1311 \private 1312 \return Returns an array of words created from the input string. 1313 */ 1314 function splitString( $text ) 1315 { 1316 // strip quotes 1317 $text = preg_replace("#'#", "", $text ); 1318 $text = preg_replace( "#\"#", "", $text ); 1319 1320 // Strip multiple whitespace 1321 $text = trim( $text ); 1322 $text = preg_replace("(\s+)", " ", $text ); 1323 1324 // Split text on whitespace 1325 $wordArray = split( " ", $text ); 1326 1327 $retArray = array(); 1328 foreach ( $wordArray as $word ) 1329 { 1330 if ( trim( $word ) != "" ) 1331 { 1332 $retArray[] = trim( $word ); 1333 } 1334 } 1335 1336 return $retArray; 1337 } 1338 1339 /*! 1340 Normalizes the text \a $text so that it is easily parsable 1341 \param $isMetaData If \c true then it expects the text to be meta data from objects, 1342 if not it is the search text and needs special handling. 1343 */ 1344 function normalizeText( $text, $isMetaData = false ) 1345 { 1346 include_once ( 'lib/ezi18n/classes/ezchartransform.php' ); 1347 $trans =& eZCharTransform::instance(); 1348 $text = $trans->transformByGroup( $text, 'search' ); 1349 1350 // Remove quotes and asterix when not handling search text by end-user 1351 if ( $isMetaData ) 1352 { 1353 $text = str_replace( array( "\"", "*" ), array( " ", " " ), $text ); 1354 } 1355 1356 return $text; 1357 } 1358 1359 /*! 1360 \static 1361 \return Returns an array describing the supported search types in thie search engine. 1362 \note It has been renamed. In eZ publish 3.4 and older it was (wrongly) named suportedSearchTypes(). 1363 */ 1364 function supportedSearchTypes() 1365 { 1366 $searchTypes = array( array( 'type' => 'attribute', 1367 'subtype' => 'fulltext', 1368 'params' => array( 'classattribute_id', 'value' ) ), 1369 array( 'type' => 'attribute', 1370 'subtype' => 'patterntext', 1371 'params' => array( 'classattribute_id', 'value' ) ), 1372 array( 'type' => 'attribute', 1373 'subtype' => 'integer', 1374 'params' => array( 'classattribute_id', 'value' ) ), 1375 array( 'type' => 'attribute', 1376 'subtype' => 'integers', 1377 'params' => array( 'classattribute_id', 'values' ) ), 1378 array( 'type' => 'attribute', 1379 'subtype' => 'byrange', 1380 'params' => array( 'classattribute_id', 'from' , 'to' ) ), 1381 array( 'type' => 'attribute', 1382 'subtype' => 'byidentifier', 1383 'params' => array( 'classattribute_id', 'identifier', 'value' ) ), 1384 array( 'type' => 'attribute', 1385 'subtype' => 'byidentifierrange', 1386 'params' => array( 'classattribute_id', 'identifier', 'from', 'to' ) ), 1387 array( 'type' => 'attribute', 1388 'subtype' => 'integersbyidentifier', 1389 'params' => array( 'classattribute_id', 'identifier', 'values' ) ), 1390 array( 'type' => 'fulltext', 1391 'subtype' => 'text', 1392 'params' => array( 'value' ) ) ); 1393 $generalSearchFilter = array( array( 'type' => 'general', 1394 'subtype' => 'class', 1395 'params' => array( array( 'type' => 'array', 1396 'value' => 'value'), 1397 'operator' ) ), 1398 array( 'type' => 'general', 1399 'subtype' => 'publishdate', 1400 'params' => array( 'value', 'operator' ) ), 1401 array( 'type' => 'general', 1402 'subtype' => 'subtree', 1403 'params' => array( array( 'type' => 'array', 1404 'value' => 'value'), 1405 'operator' ) ) ); 1406 return array( 'types' => $searchTypes, 1407 'general_filter' => $generalSearchFilter ); 1408 } 1409 1410 function searchAttributeInteger( $searchParams ) 1411 { 1412 $classAttributeID = $searchParams['classattribute_id']; 1413 $value = $searchParams['value']; 1414 1415 $classAttributeQuery = ""; 1416 if ( is_numeric( $classAttributeID ) and $classAttributeID > 0 ) 1417 { 1418 $classAttributeQuery = "ezsearch_object_word_link.contentclass_attribute_id = '$classAttributeID' AND "; 1419 } 1420 1421 $searchPartSql = " ezsearch_object_word_link.integer_value = $value AND"; 1422 1423 $searchPartText = $classAttributeQuery . $searchPartSql; 1424 $tableResult = $this->createTemporaryTable( $searchPartText ); 1425 1426 if ( $tableResult === false ) 1427 { 1428 return false; 1429 } 1430 else 1431 { 1432 return true; 1433 } 1434 } 1435 1436 function searchAttributeIntegers( $searchParams ) 1437 { 1438 $classAttributeID = $searchParams['classattribute_id']; 1439 $values = $searchParams['values']; 1440 1441 $classAttributeQuery = ""; 1442 if ( is_numeric( $classAttributeID ) and $classAttributeID > 0 ) 1443 { 1444 $classAttributeQuery = "ezsearch_object_word_link.contentclass_attribute_id = '$classAttributeID' AND "; 1445 } 1446 1447 $integerValuesSql = implode( ', ', $values ); 1448 $searchPartSql = " ezsearch_object_word_link.integer_value IN ( $integerValuesSql ) AND"; 1449 1450 $searchPartText = $classAttributeQuery . $searchPartSql; 1451 $tableResult = $this->createTemporaryTable( $searchPartText ); 1452 1453 if ( $tableResult === false ) 1454 { 1455 return false; 1456 } 1457 else 1458 { 1459 return true; 1460 } 1461 } 1462 1463 function searchAttributeByRange( $searchParams ) 1464 { 1465 $classAttributeID = $searchParams['classattribute_id']; 1466 $fromValue = $searchParams['from']; 1467 $toValue = $searchParams['to']; 1468 1469 $classAttributeQuery = ""; 1470 if ( is_numeric( $classAttributeID ) and $classAttributeID > 0 ) 1471 { 1472 $classAttributeQuery = "ezsearch_object_word_link.contentclass_attribute_id = '$classAttributeID' AND "; 1473 } 1474 1475 $searchPartSql = " ezsearch_object_word_link.integer_value BETWEEN $fromValue AND $toValue AND"; 1476 $searchPartText = $classAttributeQuery . $searchPartSql; 1477 $tableResult = $this->createTemporaryTable( $searchPartText ); 1478 1479 if ( $tableResult === false ) 1480 { 1481 return false; 1482 } 1483 else 1484 { 1485 return true; 1486 } 1487 1488 } 1489 1490 function searchAttributeByIdentifier( $searchParams ) 1491 { 1492 $identifier = $searchParams['identifier']; 1493 $textValue = $searchParams['value']; 1494 1495 $searchText = $this->normalizeText( $textValue, false ); 1496 1497 $phrasesResult = $this->getPhrases( $searchText ); 1498 $phraseTextArray = $phrasesResult['phrases']; 1499 $nonPhraseText = $phrasesResult['nonPhraseText']; 1500 $fullText = $phrasesResult['fullText']; 1501 1502 $totalObjectCount = $this->fetchTotalObjectCount(); 1503 1504 $wordIDArrays = $this->prepareWordIDArrays( $searchText ); 1505 $wordIDArray = $wordIDArrays['wordIDArray']; 1506 $wordIDHash = $wordIDArrays['wordIDHash']; 1507 $wildIDArray = $wordIDArrays['wildIDArray']; 1508 1509 $searchWordArray = $this->splitString( $searchText ); 1510 1511 $nonExistingWordCount = count( $searchWordArray ) - count( $wordIDHash ); 1512 if ( $nonExistingWordCount > 0 ) 1513 return false; 1514 1515 $searchPartsArray = $this->buildSearchPartArray( $phraseTextArray, $nonPhraseText, $wordIDHash, 1516 $wildIDArray, $identifier ); 1517 $this->buildTempTablesForFullTextSearch( $searchPartsArray, array() ); 1518 $this->GeneralFilter['classAttributeQuery'] = ''; 1519 return true; 1520 } 1521 1522 function searchAttributeByIdentifierRange( $searchParams ) 1523 { 1524 $identifier = $searchParams['identifier']; 1525 $fromValue = $searchParams['from']; 1526 $toValue = $searchParams['to']; 1527 1528 $searchPartSql = " ezsearch_object_word_link.integer_value BETWEEN $fromValue AND $toValue AND ezsearch_object_word_link.identifier = '$identifier' AND"; 1529 $tableResult = $this->createTemporaryTable( $searchPartSql ); 1530 1531 if ( $tableResult === false ) 1532 { 1533 return false; 1534 } 1535 else 1536 { 1537 return true; 1538 } 1539 } 1540 1541 function searchAttributeIntegersByIdentifier( $searchParams ) 1542 { 1543 $identifier = $searchParams['identifier']; 1544 $values = $searchParams['values']; 1545 1546 $integerValuesSql = implode( ', ', $values ); 1547 $searchPartSql = " ezsearch_object_word_link.integer_value IN ( $integerValuesSql ) AND ezsearch_object_word_link.identifier = '$identifier' AND"; 1548 $tableResult = $this->createTemporaryTable( $searchPartSql ); 1549 1550 if ( $tableResult === false ) 1551 { 1552 return false; 1553 } 1554 else 1555 { 1556 return true; 1557 } 1558 } 1559 1560 function searchAttributePatternText( $searchParams ) 1561 { 1562 $classAttributeID = $searchParams['classattribute_id']; 1563 $textValue = $searchParams['value']; 1564 1565 // $searchText = $this->normalizeText( $textValue ); 1566 $searchText = $textValue; 1567 1568 $classAttributeQuery = ""; 1569 if ( is_numeric( $classAttributeID ) and $classAttributeID > 0 ) 1570 { 1571 $classAttributeQuery = "ezsearch_object_word_link.contentclass_attribute_id = '$classAttributeID' AND "; 1572 $this->GeneralFilter['classAttributeQuery'] = $classAttributeQuery; 1573 } 1574 1575 $wordIDArrays = $this->prepareWordIDArraysForPattern( $searchText ); 1576 $wordIDArray = $wordIDArrays['wordIDArray']; 1577 $wordIDHash = $wordIDArrays['wordIDHash']; 1578 $wildIDArray = $wordIDArrays['wildIDArray']; 1579 $patternWordIDHash = $wordIDArrays['patternWordIDHash']; 1580 1581 $searchWordArray = $this->splitString( $searchText ); 1582 1583 $nonExistingWordCount = count( $searchWordArray ) - count( $wordIDHash ) - count( $patternWordIDHash ); 1584 if ( $nonExistingWordCount > 0 ) 1585 return false; 1586 1587 preg_replace( "/(\w+\*\s)/", " ", $searchText ); 1588 $nonPhraseText = $this->normalizeText( $searchText, false ); 1589 1590 $searchPartsArray = $this->buildSearchPartArrayForWords( $nonPhraseText, $wordIDHash, $wildIDArray ); 1591 1592 foreach ( $patternWordIDHash as $patternWord ) 1593 { 1594 $searchPart = '( '; 1595 $i = 0; 1596 foreach ( $patternWord as $word ) 1597 { 1598 if ( $i > 0 ) 1599 $searchPart .= ' or '; 1600 $wordID = $word['id']; 1601 $searchPart .= "ezsearch_object_word_link.word_id='$wordID' "; 1602 1603 $i++; 1604 } 1605 $searchPart .= ' ) AND '; 1606 $this->createTemporaryTable( $searchPart ); 1607 } 1608 1609 $this->buildTempTablesForFullTextSearch( $searchPartsArray, array() ); 1610 $this->GeneralFilter['classAttributeQuery'] = ''; 1611 return true; 1612 } 1613 1614 function searchAttributeFulltext( $searchParams ) 1615 { 1616 $classAttributeID = $searchParams['classattribute_id']; 1617 $textValue = $searchParams['value']; 1618 1619 $searchText = $this->normalizeText( $textValue, false ); 1620 1621 $phrasesResult = $this->getPhrases( $searchText ); 1622 $phraseTextArray = $phrasesResult['phrases']; 1623 $nonPhraseText = $phrasesResult['nonPhraseText']; 1624 $fullText = $phrasesResult['fullText']; 1625 1626 1627 $classAttributeQuery = ""; 1628 if ( is_numeric( $classAttributeID ) and $classAttributeID > 0 ) 1629 { 1630 $classAttributeQuery = "ezsearch_object_word_link.contentclass_attribute_id = '$classAttributeID' AND "; 1631 $this->GeneralFilter['classAttributeQuery'] = $classAttributeQuery; 1632 } 1633 1634 $totalObjectCount = $this->fetchTotalObjectCount(); 1635 1636 $wordIDArrays = $this->prepareWordIDArrays( $searchText ); 1637 $wordIDArray = $wordIDArrays['wordIDArray']; 1638 $wordIDHash = $wordIDArrays['wordIDHash']; 1639 $wildIDArray = $wordIDArrays['wildIDArray']; 1640 1641 $searchWordArray = $this->splitString( $searchText ); 1642 1643 $nonExistingWordCount = count( $searchWordArray ) - count( $wordIDHash ); 1644 if ( $nonExistingWordCount > 0 ) 1645 return false; 1646 $searchPartsArray = $this->buildSearchPartArray( $phraseTextArray, $nonPhraseText, 1647 $wordIDHash, $wildIDArray ); 1648 1649 $this->buildTempTablesForFullTextSearch( $searchPartsArray, array() ); 1650 $this->GeneralFilter['classAttributeQuery'] = ''; 1651 return true; 1652 } 1653 1654 function createTemporaryTable( $searchPartText ) 1655 { 1656 1657 $subTreeTable = $this->GeneralFilter['subTreeTable']; 1658 $searchDateQuery = $this->GeneralFilter['searchDateQuery']; 1659 $sectionQuery = $this->GeneralFilter['sectionQuery']; 1660 $classQuery = $this->GeneralFilter['classQuery']; 1661 $classAttributeQuery = $this->GeneralFilter['classAttributeQuery']; 1662 // $searchPartText = $this->GeneralFilter['searchPartText']; 1663 $subTreeSQL = $this->GeneralFilter['subTreeSQL']; 1664 $sqlPermissionChecking = $this->GeneralFilter['sqlPermissionChecking']; 1665 $db =& eZDB::instance(); 1666 $i = $this->TempTablesCount; 1667 if ( $i == 0 ) 1668 { 1669 $table = $db->generateUniqueTempTableName( 'ezsearch_tmp_%_0' ); 1670 $this->saveCreatedTempTableName( 0, $table ); 1671 $db->createTempTable( "CREATE TEMPORARY TABLE $table ( contentobject_id int primary key not null, published int )" ); 1672 $db->query( "INSERT INTO $table SELECT DISTINCT ezsearch_object_word_link.contentobject_id, ezsearch_object_word_link.published 1673 FROM 1674 ezcontentobject, 1675 ezsearch_object_word_link 1676 $subTreeTable, 1677 ezcontentclass, 1678 ezcontentobject_tree 1679 $sqlPermissionChecking[from] 1680 WHERE 1681 $searchDateQuery 1682 $sectionQuery 1683 $classQuery 1684 $classAttributeQuery 1685 $searchPartText 1686 $subTreeSQL 1687 ezcontentobject.id=ezsearch_object_word_link.contentobject_id and 1688 ezcontentobject.contentclass_id = ezcontentclass.id and 1689 ezcontentclass.version = '0' and 1690 ezcontentobject.id = ezcontentobject_tree.contentobject_id and 1691 ezcontentobject_tree.node_id = ezcontentobject_tree.main_node_id 1692 $sqlPermissionChecking[where]" ); 1693 } 1694 else 1695 { 1696 $table = $db->generateUniqueTempTableName( "ezsearch_tmp_%_$i" ); 1697 $this->saveCreatedTempTableName( $i, $table ); 1698 $tmpTable0 = $this->getSavedTempTableName( 0 ); 1699 $db->createTempTable( "CREATE TEMPORARY TABLE $table ( contentobject_id int primary key not null, published int )" ); 1700 $db->query( "INSERT INTO $table SELECT DISTINCT ezsearch_object_word_link.contentobject_id, ezsearch_object_word_link.published 1701 FROM 1702 ezcontentobject, 1703 ezsearch_object_word_link 1704 $subTreeTable, 1705 ezcontentclass, 1706 ezcontentobject_tree, 1707 $tmpTable0 1708 $sqlPermissionChecking[from] 1709 WHERE 1710 $tmpTable0.contentobject_id=ezsearch_object_word_link.contentobject_id AND 1711 $searchDateQuery 1712 $sectionQuery 1713 $classQuery 1714 $classAttributeQuery 1715 $searchPartText 1716 $subTreeSQL 1717 ezcontentobject.id=ezsearch_object_word_link.contentobject_id and 1718 ezcontentobject.contentclass_id = ezcontentclass.id and 1719 ezcontentclass.version = '0' and 1720 ezcontentobject.id = ezcontentobject_tree.contentobject_id and 1721 ezcontentobject_tree.node_id = ezcontentobject_tree.main_node_id 1722 $sqlPermissionChecking[where]" ); 1723 } 1724 1725 $tmpTableI = $this->getSavedTempTableName( $i ); 1726 $insertedCountArray = $db->arrayQuery( "SELECT count(*) as count from $tmpTableI " ); 1727 $i++; 1728 $this->TempTablesCount++; 1729 if ( $insertedCountArray[0]['count'] == 0 ) 1730 { 1731 return false; 1732 } 1733 else 1734 { 1735 return $insertedCountArray[0]['count']; 1736 } 1737 } 1738 1739 function buildTempTablesForFullTextSearch( $searchPartsArray, $generalFilterList = array() ) 1740 { 1741 $ini = eZINI::instance(); 1742 $db =& eZDB::instance(); 1743 1744 $i = $this->TempTablesCount; 1745 $generalFilterList = $this->GeneralFilter; 1746 1747 if ( isset( $generalFilterList['searchDateQuery'] ) and 1748 isset( $generalFilterList['publish_date'] ) ) 1749 $searchDateQuery = $generalFilterList['publish_date']; 1750 else 1751 $searchDateQuery = ''; 1752 1753 if ( isset( $generalFilterList['sectionQuery'] ) ) 1754 $sectionQuery = $generalFilterList['sectionQuery']; 1755 else 1756 $sectionQuery = ''; 1757 1758 if ( isset( $generalFilterList['classQuery'] ) ) 1759 $classQuery = $generalFilterList['classQuery']; 1760 else 1761 $classQuery = ''; 1762 1763 if ( isset( $generalFilterList['classAttributeQuery'] ) ) 1764 $classAttributeQuery = $generalFilterList[ 'classAttributeQuery']; 1765 else 1766 $classAttributeQuery = ''; 1767 1768 if ( isset( $generalFilterList['sqlPermissionChecking'] ) ) 1769 $sqlPermissionChecking = $generalFilterList['sqlPermissionChecking']; 1770 else 1771 $sqlPermissionChecking = array( 'from' => '', 1772 'where' => '' ); 1773 1774 if ( isset( $generalFilterList['subTreeSQL'] ) ) 1775 { 1776 $subTreeTable = $generalFilterList['subTreeTable']; 1777 $subTreeSQL = $generalFilterList['subTreeSQL']; 1778 } 1779 else 1780 { 1781 $subTreeTable = ''; 1782 $subTreeSQL = ''; 1783 } 1784 1785 $totalObjectCount = $this->fetchTotalObjectCount(); 1786 1787 $stopWordThresholdValue = 100; 1788 if ( $ini->hasVariable( 'SearchSettings', 'StopWordThresholdValue' ) ) 1789 $stopWordThresholdValue = $ini->variable( 'SearchSettings', 'StopWordThresholdValue' ); 1790 1791 $stopWordThresholdPercent = 60; 1792 if ( $ini->hasVariable( 'SearchSettings', 'StopWordThresholdPercent' ) ) 1793 $stopWordThresholdPercent = $ini->variable( 'SearchSettings', 'StopWordThresholdPercent' ); 1794 1795 $searchThresholdValue = $totalObjectCount; 1796 if ( $totalObjectCount > $stopWordThresholdValue ) 1797 { 1798 $searchThresholdValue = (int)( $totalObjectCount * ( $stopWordThresholdPercent / 100 ) ); 1799 } 1800 1801 foreach ( $searchPartsArray as $searchPart ) 1802 { 1803 // $wordID = $searchWord['id']; 1804 1805 1806 // do not search words that are too frequent 1807 if ( $searchPart['object_count'] < $searchThresholdValue ) 1808 { 1809 // $tmpTableCount++; 1810 $searchPartText = $searchPart['sql_part']; 1811 if ( $i == 0 ) 1812 { 1813 $table = $db->generateUniqueTempTableName( 'ezsearch_tmp_%_0' ); 1814 $this->saveCreatedTempTableName( 0, $table ); 1815 $db->createTempTable( "CREATE TEMPORARY TABLE $table ( contentobject_id int primary key not null, published int )" ); 1816 $db->query( "INSERT INTO $table SELECT DISTINCT ezsearch_object_word_link.contentobject_id, ezsearch_object_word_link.published 1817 FROM 1818 ezcontentobject, 1819 ezsearch_object_word_link 1820 $subTreeTable, 1821 ezcontentclass, 1822 ezcontentobject_tree 1823 $sqlPermissionChecking[from] 1824 WHERE 1825 $searchDateQuery 1826 $sectionQuery 1827 $classQuery 1828 $classAttributeQuery 1829 $searchPartText 1830 $subTreeSQL 1831 ezcontentobject.id=ezsearch_object_word_link.contentobject_id and 1832 ezcontentobject.contentclass_id = ezcontentclass.id and 1833 ezcontentclass.version = '0' and 1834 ezcontentobject.id = ezcontentobject_tree.contentobject_id and 1835 ezcontentobject_tree.node_id = ezcontentobject_tree.main_node_id 1836 $sqlPermissionChecking[where]" ); 1837 } 1838 else 1839 { 1840 $table = $db->generateUniqueTempTableName( "ezsearch_tmp_%_$i" ); 1841 $this->saveCreatedTempTableName( $i, $table ); 1842 $tmpTable0 = $this->getSavedTempTableName( 0 ); 1843 $db->createTempTable( "CREATE TEMPORARY TABLE $table ( contentobject_id int primary key not null, published int )" ); 1844 $db->query( "INSERT INTO $table SELECT DISTINCT ezsearch_object_word_link.contentobject_id, ezsearch_object_word_link.published 1845 FROM 1846 ezcontentobject, 1847 ezsearch_object_word_link 1848 $subTreeTable, 1849 ezcontentclass, 1850 ezcontentobject_tree, 1851 $tmpTable0 1852 $sqlPermissionChecking[from] 1853 WHERE 1854 $tmpTable0.contentobject_id=ezsearch_object_word_link.contentobject_id AND 1855 $searchDateQuery 1856 $sectionQuery 1857 $classQuery 1858 $classAttributeQuery 1859 $searchPartText 1860 $subTreeSQL 1861 ezcontentobject.id=ezsearch_object_word_link.contentobject_id and 1862 ezcontentobject.contentclass_id = ezcontentclass.id and 1863 ezcontentclass.version = '0' and 1864 ezcontentobject.id = ezcontentobject_tree.contentobject_id and 1865 ezcontentobject_tree.node_id = ezcontentobject_tree.main_node_id 1866 $sqlPermissionChecking[where]" ); 1867 } 1868 $i++; 1869 } 1870 else 1871 { 1872 $stopWordArray[] = array( 'word' => $searchPart['word'] ); 1873 } 1874 } 1875 $this->TempTablesCount = $i; 1876 } 1877 1878 1879 function getPhrases( $searchText ) 1880 { 1881 $numQuotes = substr_count( $searchText, "\"" ); 1882 1883 $phraseTextArray = array(); 1884 $fullText = $searchText; 1885 $nonPhraseText =''; 1886 1887 $postPhraseText = $fullText; 1888 $pos = 0; 1889 if ( ( $numQuotes > 0 ) and ( ( $numQuotes % 2 ) == 0 ) ) 1890 { 1891 for ( $i = 0; $i < ( $numQuotes / 2 ); $i ++ ) 1892 { 1893 $quotePosStart = strpos( $searchText, '"', $pos ); 1894 $quotePosEnd = strpos( $searchText, '"', $quotePosStart + 1 ); 1895 1896 $prePhraseText = substr( $searchText, $pos, $quotePosStart - $pos ); 1897 $postPhraseText = substr( $searchText, $quotePosEnd +1 ); 1898 $phraseText = substr( $searchText, $quotePosStart + 1, $quotePosEnd - $quotePosStart - 1 ); 1899 1900 $phraseTextArray[] = $phraseText; 1901 // $fullText .= $prePhraseText; 1902 $nonPhraseText .= $prePhraseText; 1903 $pos = $quotePosEnd + 1; 1904 } 1905 } 1906 $nonPhraseText .= $postPhraseText; 1907 return array( 'phrases' => &$phraseTextArray, 1908 'nonPhraseText' => $nonPhraseText, 1909 'fullText' => $fullText ); 1910 } 1911 1912 function buildSearchPartArray( $phraseTextArray, $nonPhraseText, &$wordIDHash, &$wildIDArray, 1913 $identifier = false ) 1914 { 1915 $searchPartsArrayForPhrases = $this->buildSearchPartArrayForPhrases( $phraseTextArray, $wordIDHash, 1916 $identifier ); 1917 $searchPartsArrayForWords = $this->buildSearchPartArrayForWords( $nonPhraseText, $wordIDHash, 1918 $wildIDArray, $identifier ); 1919 $searchPartsArray = array_merge( $searchPartsArrayForPhrases, $searchPartsArrayForWords ); 1920 return $searchPartsArray; 1921 } 1922 1923 function buildSearchPartArrayForWords( $nonPhraseText, &$wordIDHash, &$wildIDArray, $identifier = false ) 1924 { 1925 $searchPartsArray = array(); 1926 $nonPhraseWordArray = $this->splitString( $nonPhraseText ); 1927 $uniqueWordArray = array(); 1928 1929 $searchPart = array(); 1930 if ( isset( $wildIDArray ) && count( $wildIDArray ) > 0 ) 1931 { 1932 $searchPart['sql_part'] = '( '; 1933 $i = 0; 1934 $objectCount = -1; 1935 1936 foreach( $wildIDArray as $wordInfo ) 1937 { 1938 1939 if ( $i > 0 ) 1940 $searchPart['sql_part'] .= ' or '; 1941 $searchPart['sql_part'] .= "ezsearch_object_word_link.word_id='". $wordInfo['id'] ."'"; 1942 if ( $objectCount < intval($wordInfo['object_count']) ) 1943 $objectCount = intval($wordInfo['object_count']); 1944 $i++; 1945 } 1946 $searchPart['sql_part'] .= ' ) AND '; 1947 $searchPart['object_count'] = $objectCount; 1948 $searchPart['is_phrase'] = 0; 1949 $searchPartsArray[] = $searchPart; 1950 unset ( $searchPart ); 1951 } 1952 1953 foreach( array_keys( $wordIDHash ) as $word ) 1954 { 1955 $searchPart = array(); 1956 $searchPart['text'] = $word; 1957 $wordID = $wordIDHash[$word]['id']; 1958 $searchPart['sql_part'] = $this->buildSqlPartForWord( $wordID, $identifier ); 1959 $searchPart['is_phrase'] = 0; 1960 $searchPart['object_count'] = $wordIDHash[$word]['object_count']; 1961 $searchPartsArray[] = $searchPart; 1962 unset ( $searchPart ); 1963 } 1964 return $searchPartsArray; 1965 } 1966 1967 function buildSearchPartArrayForPhrases( $phraseTextArray, &$wordIDHash, $identifier = false ) 1968 { 1969 // build an array of the word id's for each phrase 1970 $phraseIDArrayArray = array(); 1971 foreach ( $phraseTextArray as $phraseText ) 1972 { 1973 $wordArray = $this->splitString( $phraseText ); 1974 $wordIDArray = array(); 1975 foreach ( $wordArray as $word ) 1976 { 1977 if ( !isset( $wordIDHash[$word] ) ) return array(); 1978 $wordIDArray[] = $wordIDHash[$word]['id']; 1979 } 1980 $phraseIDArrayArray[] = $wordIDArray; 1981 } 1982 1983 // build phrase SQL query part(s) 1984 $phraseSearchSQLArray = array(); 1985 foreach ( $phraseIDArrayArray as $phraseIDArray ) 1986 { 1987 $phraseSearchSQL = $this->buildPhraseSqlQueryPart( $phraseIDArray, $identifier ); 1988 $phraseSearchSQLArray[] = $phraseSearchSQL; 1989 } 1990 1991 ///Build search parts array for phrases and normal words 1992 $searchPartsArray = array(); 1993 $i = 0; 1994 foreach ( $phraseTextArray as $phraseText ) 1995 { 1996 foreach ( $phraseSearchSQLArray[$i] as $phraseSearchSubSQL ) 1997 { 1998 $searchPart = array(); 1999 $searchPart['text'] = $phraseText; 2000 $searchPart['sql_part'] = ' ( ' . $phraseSearchSubSQL . ' ) AND'; 2001 $searchPart['is_phrase'] = 1; 2002 $searchPart['object_count'] = 0; 2003 $searchPartsArray[] = $searchPart; 2004 } 2005 unset( $searchPart ); 2006 $i++; 2007 } 2008 2009 return $searchPartsArray; 2010 } 2011 2012 function prepareWordIDArraysForPattern( $searchText ) 2013 { 2014 $db =& eZDB::instance(); 2015 $searchWordArray = $this->splitString( $searchText ); 2016 2017 2018 // fetch the word id 2019 $wordQueryString = ''; 2020 $patternWordsCount = 0; 2021 $patternWordArray = array(); 2022 $wordsCount = 0; 2023 $patternWordQueryString = ''; 2024 foreach ( $searchWordArray as $searchWord ) 2025 { 2026 if ( preg_match ( "/(\w+)(\*)/", $searchWord, $matches ) ) 2027 { 2028 if ( $patternWordsCount > 0 ) 2029 $patternWordQueryString .= " or "; 2030 $patternWordArray[] = $matches[1]; 2031 $patternWordsCount++; 2032 } 2033 else 2034 { 2035 if ( $wordsCount > 0 ) 2036 $wordQueryString .= " or "; 2037 $wordQueryString .= " word='$searchWord' "; 2038 $wordsCount++; 2039 } 2040 } 2041 2042 // create the word hash 2043 $wordIDArray = array(); 2044 $wordIDHash = array(); 2045 if ( $wordsCount > 0 ) 2046 { 2047 $wordIDArrayRes = $db->arrayQuery( "SELECT id, word, object_count FROM ezsearch_word where $wordQueryString ORDER BY object_count" ); 2048 2049 foreach ( $wordIDArrayRes as $wordRes ) 2050 { 2051 $wordIDArray[] = $wordRes['id']; 2052 $wordIDHash[$wordRes['word']] = array( 'id' => $wordRes['id'], 'word' => $wordRes['word'], 'object_count' => $wordRes['object_count'] ); 2053 } 2054 } 2055 2056 $patternWordIDHash = array(); 2057 foreach ( $patternWordArray as $word ) 2058 { 2059 $patternWordIDRes = $db->arrayQuery( "SELECT id, word, object_count FROM ezsearch_word where word like '" . $word . "%' order by object_count" ); 2060 $matchedWords = array(); 2061 foreach ( $patternWordIDRes as $wordRes ) 2062 { 2063 $matchedWords[] = array( 'id' => $wordRes['id'], 'word' => $wordRes['word'], 'object_count' => $wordRes['object_count'] ); 2064 } 2065 $patternWordIDHash[$word] = $matchedWords; 2066 } 2067 2068 2069 return array( 'wordIDArray' => &$wordIDArray, 2070 'wordIDHash' => &$wordIDHash, 2071 'patternWordIDHash' => &$patternWordIDHash ); 2072 } 2073 2074 function prepareWordIDArrays( $searchText ) 2075 { 2076 if ( trim( $searchText ) == "" ) 2077 { 2078 $ini =& eZINI::instance(); 2079 if ( $ini->hasVariable( 'SearchSettings', 'AllowEmptySearch' ) and 2080 $ini->variable( 'SearchSettings', 'AllowEmptySearch' ) != 'enabled' ) 2081 return array(); 2082 } 2083 2084 $db =& eZDB::instance(); 2085 2086 //extend search words for urls, by extracting parts without www. and add to the end of search text 2087 $matches = array(); 2088 if ( preg_match_all("/www\.([^\.]+\.[^\s]+)\s?/i", $searchText, $matches ) ); 2089 { 2090 if (isset($matches[1]) ) 2091 { 2092 $searchText.= ' '.join(' ', $matches[1] ); 2093 } 2094 } 2095 2096 $searchWordArray = $this->splitString( $searchText ); 2097 2098 $wildCardWordArray = array(); 2099 $i = 0; 2100 $wildCardQueryString = array(); 2101 $wordQueryString = ''; 2102 2103 $ini =& eZINI::instance(); 2104 $wildSearchEnabled = ( $ini->variable( 'SearchSettings', 'EnableWildcard' ) == 'true' ); 2105 if ( $wildSearchEnabled ) 2106 { 2107 $minCharacters = $ini->variable( 'SearchSettings', 'MinCharacterWildcard' ); 2108 } 2109 2110 foreach ( $searchWordArray as $searchWord ) 2111 { 2112 $wordLength = strlen( $searchWord ) - 1; 2113 2114 if ( $wildSearchEnabled && ( $wordLength >= $minCharacters ) ) 2115 { 2116 if ( $searchWord[$wordLength] == '*' ) 2117 { 2118 $baseWord = substr( $searchWord, 0, $wordLength ); 2119 $wildCardQueryString[] = " word LIKE '". $baseWord ."%' "; 2120 continue; 2121 } 2122 else if ( $searchWord[0] == '*' ) /* Change this to allow searching for shorter/longer words using wildcard */ 2123 { 2124 $baseWord = substr( $searchWord, 1, $wordLength ); 2125 $wildCardQueryString[] = " word LIKE '%". $baseWord ."' "; 2126 continue; 2127 } 2128 } 2129 if ( $i > 0 ) 2130 $wordQueryString .= " or "; 2131 2132 $wordQueryString .= " word='$searchWord' "; 2133 $i++; 2134 } 2135 2136 if ( strlen( $wordQueryString ) > 0 ) 2137 $wordIDArrayRes = $db->arrayQuery( "SELECT id, word, object_count FROM ezsearch_word where $wordQueryString ORDER BY object_count" ); 2138 foreach ( $wildCardQueryString as $wildCardQuery ) 2139 { 2140 $wildCardWordArray[] = $db->arrayQuery( "SELECT id, word, object_count FROM ezsearch_word where $wildCardQuery order by object_count" ); 2141 } 2142 2143 // create the word hash 2144 $wordIDArray = array(); 2145 $wordIDHash = array(); 2146 if ( isset( $wordIDArrayRes ) && is_array( $wordIDArrayRes ) ) 2147 { 2148 foreach ( $wordIDArrayRes as $wordRes ) 2149 { 2150 $wordIDArray[] = $wordRes['id']; 2151 $wordIDHash[$wordRes['word']] = array( 'id' => $wordRes['id'], 'word' => $wordRes['word'], 'object_count' => $wordRes['object_count'] ); 2152 } 2153 } 2154 2155 $wildIDArray = array(); 2156 $wildCardCount = 0; 2157 foreach ( array_keys( $wildCardWordArray ) as $key ) 2158 { 2159 if ( is_array( $wildCardWordArray[$key] ) && count( $wildCardWordArray[$key] ) > 0 ) 2160 { 2161 $wildCardCount++; 2162 foreach ( $wildCardWordArray[$key] as $wordRes ) 2163 { 2164 $wildIDArray[] = array( 'id' => $wordRes['id'], 'object_count' => $wordRes['object_count'] ); 2165 } 2166 } 2167 } 2168 return array( 'wordIDArray' => &$wordIDArray, 2169 'wordIDHash' => &$wordIDHash, 2170 'wildIDArray' => &$wildIDArray, 2171 'wildCardCount' => $wildCardCount ); 2172 } 2173 2174 function fetchTotalObjectCount() 2175 { 2176 // Get the total number of objects 2177 $db =& eZDB::instance(); 2178 $objectCount = array(); 2179 $objectCount = $db->arrayQuery( "SELECT COUNT(*) AS count FROM ezcontentobject" ); 2180 $totalObjectCount = $objectCount[0]["count"]; 2181 return $totalObjectCount; 2182 } 2183 2184 function constructMethodName( $searchTypeData ) 2185 { 2186 $type = $searchTypeData['type']; 2187 $subtype = $searchTypeData['subtype']; 2188 $methodName = 'search' . $type . $subtype; 2189 return $methodName; 2190 2191 } 2192 2193 function callMethod( $methodName, $parameterArray ) 2194 { 2195 if ( !method_exists( $this, $methodName ) ) 2196 { 2197 eZDebug::writeError( $methodName, "Method does not exist in ez search engine" ); 2198 return false; 2199 } 2200 2201 if ( $this->UseOldCall ) 2202 return call_user_method_array( $methodName, $this, $parameterArray ); 2203 else 2204 return $this->$methodName($parameterArray[0]); 2205 //call_user_func_array( array( $this, $methodName ), $parameterArray ); 2206 // 2207 // 2208 } 2209 2210 /*! 2211 Will remove all search words and object/word relations. 2212 */ 2213 function cleanup() 2214 { 2215 $db =& eZDB::instance(); 2216 $db->begin(); 2217 $db->query( "DELETE FROM ezsearch_word" ); 2218 $db->query( "DELETE FROM ezsearch_object_word_link" ); 2219 $db->commit(); 2220 } 2221 2222 /*! 2223 \return true if the search part is incomplete. 2224 */ 2225 function isSearchPartIncomplete( $part ) 2226 { 2227 switch ( $part['subtype'] ) 2228 { 2229 case 'fulltext': 2230 { 2231 if ( !isset( $part['value'] ) || $part['value'] == '' ) 2232 return true; 2233 } 2234 break; 2235 2236 case 'patterntext': 2237 { 2238 if ( !isset( $part['value'] ) || $part['value'] == '' ) 2239 return true; 2240 } 2241 break; 2242 2243 case 'integer': 2244 { 2245 if ( !isset( $part['value'] ) || $part['value'] == '' ) 2246 return true; 2247 } 2248 break; 2249 2250 case 'integers': 2251 { 2252 if ( !isset( $part['values'] ) || count( $part['values'] ) == 0 ) 2253 return true; 2254 } 2255 break; 2256 2257 case 'byrange': 2258 { 2259 if ( !isset( $part['from'] ) || $part['from'] == '' || 2260 !isset( $part['to'] ) || $part['to'] == '' ) 2261 return true; 2262 } 2263 break; 2264 2265 case 'byidentifier': 2266 { 2267 if ( !isset( $part['value'] ) || $part['value'] == '' ) 2268 return true; 2269 } 2270 break; 2271 2272 case 'byidentifierrange': 2273 { 2274 if ( !isset( $part['from'] ) || $part['from'] == '' || 2275 !isset( $part['to'] ) || $part['to'] == '' ) 2276 return true; 2277 } 2278 break; 2279 2280 case 'integersbyidentifier': 2281 { 2282 if ( !isset( $part['values'] ) || count( $part['values'] ) == 0 ) 2283 return true; 2284 } 2285 break; 2286 2287 case 'byarea': 2288 { 2289 if ( !isset( $part['from'] ) || $part['from'] == '' || 2290 !isset( $part['to'] ) || $part['to'] == '' || 2291 !isset( $part['minvalue'] ) || $part['minvalue'] == '' || 2292 !isset( $part['maxvalue'] ) || $part['maxvalue'] == '' ) 2293 { 2294 return true; 2295 } 2296 } 2297 } 2298 return false; 2299 } 2300 2301 2302 var $UseOldCall = false; 2303 var $TempTablesCount = 0; 2304 var $CreatedTempTablesNames = array(); 2305 } 2306 2307 ?>
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
| Généré le : Sat Feb 24 10:30:04 2007 | par Balluche grâce à PHPXref 0.7 |