[ Index ]
 

Code source de eZ Publish 3.9.0

Accédez au Source d'autres logiciels libresSoutenez Angelica Josefina !

title

Body

[fermer]

/kernel/search/plugins/ezsearchengine/ -> ezsearchengine.php (source)

   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  ?>


Généré le : Sat Feb 24 10:30:04 2007 par Balluche grâce à PHPXref 0.7