2007-07-22 22:23:00 +00:00
< ? php
/**
*
* @ package search
* @ copyright ( c ) 2005 phpBB Group
2012-06-11 15:04:38 +05:30
* @ license http :// opensource . org / licenses / gpl - 2.0 . php GNU General Public License v2
2007-07-22 22:23:00 +00:00
*
*/
/**
2012-05-01 16:09:08 +05:30
* @ ignore
2007-07-22 22:23:00 +00:00
*/
if ( ! defined ( 'IN_PHPBB' ))
{
exit ;
}
/**
* fulltext_postgres
* Fulltext search for PostgreSQL
* @ package search
*/
2012-05-01 16:09:08 +05:30
class phpbb_search_fulltext_postgres extends phpbb_search_base
2007-07-22 22:23:00 +00:00
{
2012-06-20 02:46:17 +05:30
private $stats = array ();
private $split_words = array ();
private $tsearch_usable = false ;
private $version ;
private $tsearch_query ;
2012-07-03 03:54:01 +05:30
private $phrase_search = false ;
2012-07-22 16:50:09 +05:30
private $config ;
private $db ;
private $user ;
2012-06-20 02:46:17 +05:30
public $search_query ;
public $common_words = array ();
public $word_length = array ();
2007-07-22 22:23:00 +00:00
2012-06-23 02:14:46 +05:30
/**
* Constructor
* Creates a new phpbb_search_fulltext_postgres , which is used as a search backend .
*
* @ param string | bool $error Any error that occurs is passed on through this reference variable otherwise false
*/
2012-07-22 16:50:09 +05:30
public function __construct ( & $error , $phpbb_root_path , $phpEx , $config , $db , $user )
2007-07-22 22:23:00 +00:00
{
2012-07-22 16:50:09 +05:30
$this -> config = $config ;
$this -> db = $db ;
$this -> user = $user ;
2007-07-22 22:23:00 +00:00
2012-07-22 16:50:09 +05:30
$this -> word_length = array ( 'min' => $this -> config [ 'fulltext_postgres_min_word_len' ], 'max' => $this -> config [ 'fulltext_postgres_max_word_len' ]);
2007-07-22 22:23:00 +00:00
2012-07-22 16:50:09 +05:30
if ( $this -> db -> sql_layer == 'postgres' )
2007-07-22 22:23:00 +00:00
{
2012-07-22 16:50:09 +05:30
$pgsql_version = explode ( ',' , substr ( $this -> db -> sql_server_info (), 10 ));
2012-06-16 03:05:40 +05:30
$this -> version = trim ( $pgsql_version [ 0 ]);
2012-06-26 01:27:14 +05:30
if ( version_compare ( $this -> version , '8.3' , '>=' ))
2010-02-10 10:31:00 +00:00
{
2012-06-11 16:18:05 +05:30
$this -> tsearch_usable = true ;
2010-02-10 10:31:00 +00:00
}
2007-07-22 22:23:00 +00:00
}
$error = false ;
}
2012-05-01 16:09:08 +05:30
/**
* Returns the name of this search backend to be displayed to administrators
*
* @ return string Name
2012-06-23 02:14:46 +05:30
*
* @ access public
2012-05-01 16:09:08 +05:30
*/
2012-06-10 16:46:07 +05:30
public function get_name ()
2012-05-01 16:09:08 +05:30
{
return 'PostgreSQL Fulltext' ;
}
2012-07-03 03:54:01 +05:30
/**
* Returns if phrase search is supported or not
*
* @ return bool
*
* @ access public
*/
public function supports_phrase_search ()
{
return $this -> phrase_search ;
}
2012-05-01 16:09:08 +05:30
2007-07-22 22:23:00 +00:00
/**
* Checks for correct PostgreSQL version and stores min / max word length in the config
2012-06-23 02:14:46 +05:30
*
* @ return string | bool Language key of the error / incompatiblity occured
*
* @ access public
2007-07-22 22:23:00 +00:00
*/
function init ()
{
2012-07-22 16:50:09 +05:30
if ( $this -> db -> sql_layer != 'postgres' )
2007-07-22 22:23:00 +00:00
{
2012-07-22 16:50:09 +05:30
return $this -> user -> lang [ 'FULLTEXT_POSTGRES_INCOMPATIBLE_DATABASE' ];
2007-07-22 22:23:00 +00:00
}
2012-06-11 16:18:05 +05:30
if ( ! $this -> tsearch_usable )
2010-04-29 15:15:23 -04:00
{
2012-07-22 16:50:09 +05:30
return $this -> user -> lang [ 'FULLTEXT_POSTGRES_TS_NOT_USABLE' ];
2007-07-22 22:23:00 +00:00
}
return false ;
}
/**
* Splits keywords entered by a user into an array of words stored in $this -> split_words
* Stores the tidied search query in $this -> search_query
*
2012-06-23 02:14:46 +05:30
* @ param string & $keywords Contains the keyword as entered by the user
* @ param string $terms is either 'all' or 'any'
* @ return bool false if no valid keywords were found and otherwise true
*
* @ access public
2007-07-22 22:23:00 +00:00
*/
function split_keywords ( & $keywords , $terms )
{
if ( $terms == 'all' )
{
$match = array ( '#\sand\s#iu' , '#\sor\s#iu' , '#\snot\s#iu' , '#\+#' , '#-#' , '#\|#' );
$replace = array ( ' +' , ' |' , ' -' , ' +' , ' -' , ' |' );
$keywords = preg_replace ( $match , $replace , $keywords );
}
// Filter out as above
2010-02-10 10:31:00 +00:00
$split_keywords = preg_replace ( " #[ \" \n \r \t ]+# " , ' ' , trim ( htmlspecialchars_decode ( $keywords )));
2007-07-22 22:23:00 +00:00
// Split words
2012-06-12 04:18:39 +05:30
$split_keywords = preg_replace ( '#([^\p{L}\p{N}\'*"()])#u' , '$1$1' , str_replace ( '\'\'' , '\' \'' , trim ( $split_keywords )));
$matches = array ();
preg_match_all ( '#(?:[^\p{L}\p{N}*"()]|^)([+\-|]?(?:[\p{L}\p{N}*"()]+\'?)*[\p{L}\p{N}*"()])(?:[^\p{L}\p{N}*"()]|$)#u' , $split_keywords , $matches );
$this -> split_words = $matches [ 1 ];
2007-07-22 22:23:00 +00:00
foreach ( $this -> split_words as $i => $word )
{
$clean_word = preg_replace ( '#^[+\-|"]#' , '' , $word );
// check word length
$clean_len = utf8_strlen ( str_replace ( '*' , '' , $clean_word ));
2012-07-22 16:50:09 +05:30
if (( $clean_len < $this -> config [ 'fulltext_postgres_min_word_len' ]) || ( $clean_len > $this -> config [ 'fulltext_postgres_max_word_len' ]))
2007-07-22 22:23:00 +00:00
{
$this -> common_words [] = $word ;
unset ( $this -> split_words [ $i ]);
}
}
if ( $terms == 'any' )
{
$this -> search_query = '' ;
$this -> tsearch_query = '' ;
foreach ( $this -> split_words as $word )
{
if (( strpos ( $word , '+' ) === 0 ) || ( strpos ( $word , '-' ) === 0 ) || ( strpos ( $word , '|' ) === 0 ))
{
$word = substr ( $word , 1 );
}
$this -> search_query .= $word . ' ' ;
$this -> tsearch_query .= '|' . $word . ' ' ;
}
}
else
{
$this -> search_query = '' ;
$this -> tsearch_query = '' ;
foreach ( $this -> split_words as $word )
{
if ( strpos ( $word , '+' ) === 0 )
{
$this -> search_query .= $word . ' ' ;
$this -> tsearch_query .= '&' . substr ( $word , 1 ) . ' ' ;
}
elseif ( strpos ( $word , '-' ) === 0 )
{
$this -> search_query .= $word . ' ' ;
$this -> tsearch_query .= '&!' . substr ( $word , 1 ) . ' ' ;
}
elseif ( strpos ( $word , '|' ) === 0 )
{
$this -> search_query .= $word . ' ' ;
$this -> tsearch_query .= '|' . substr ( $word , 1 ) . ' ' ;
}
else
{
$this -> search_query .= '+' . $word . ' ' ;
$this -> tsearch_query .= '&' . $word . ' ' ;
}
}
}
$this -> tsearch_query = substr ( $this -> tsearch_query , 1 );
$this -> search_query = utf8_htmlspecialchars ( $this -> search_query );
if ( $this -> search_query )
{
$this -> split_words = array_values ( $this -> split_words );
sort ( $this -> split_words );
return true ;
}
return false ;
}
/**
* Turns text into an array of words
2012-06-23 02:14:46 +05:30
* @ param string $text contains post text / subject
*
* @ access public
2007-07-22 22:23:00 +00:00
*/
function split_message ( $text )
{
// Split words
2012-06-12 04:18:39 +05:30
$text = preg_replace ( '#([^\p{L}\p{N}\'*])#u' , '$1$1' , str_replace ( '\'\'' , '\' \'' , trim ( $text )));
$matches = array ();
preg_match_all ( '#(?:[^\p{L}\p{N}*]|^)([+\-|]?(?:[\p{L}\p{N}*]+\'?)*[\p{L}\p{N}*])(?:[^\p{L}\p{N}*]|$)#u' , $text , $matches );
$text = $matches [ 1 ];
2007-07-22 22:23:00 +00:00
// remove too short or too long words
$text = array_values ( $text );
for ( $i = 0 , $n = sizeof ( $text ); $i < $n ; $i ++ )
{
$text [ $i ] = trim ( $text [ $i ]);
2012-07-22 16:50:09 +05:30
if ( utf8_strlen ( $text [ $i ]) < $this -> config [ 'fulltext_postgres_min_word_len' ] || utf8_strlen ( $text [ $i ]) > $this -> config [ 'fulltext_postgres_max_word_len' ])
2007-07-22 22:23:00 +00:00
{
unset ( $text [ $i ]);
}
}
return array_values ( $text );
}
/**
* Performs a search on keywords depending on display specific params . You have to run split_keywords () first .
*
* @ param string $type contains either posts or topics depending on what should be searched for
2010-02-10 10:31:00 +00:00
* @ param string $fields contains either titleonly ( topic titles should be searched ), msgonly ( only message bodies should be searched ), firstpost ( only subject and body of the first post should be searched ) or all ( all post bodies and subjects should be searched )
* @ param string $terms is either 'all' ( use query as entered , words without prefix should default to " have to be in field " ) or 'any' ( ignore search query parts and just return all posts that contain any of the specified words )
* @ param array $sort_by_sql contains SQL code for the ORDER BY part of a query
* @ param string $sort_key is the key of $sort_by_sql for the selected sorting
* @ param string $sort_dir is either a or d representing ASC and DESC
* @ param string $sort_days specifies the maximum amount of days a post may be old
* @ param array $ex_fid_ary specifies an array of forum ids which should not be searched
* @ param array $m_approve_fid_ary specifies an array of forum ids in which the searcher is allowed to view unapproved posts
* @ param int $topic_id is set to 0 or a topic id , if it is not 0 then only posts in this topic should be searched
* @ param array $author_ary an array of author ids if the author should be ignored during the search the array is empty
* @ param string $author_name specifies the author match , when ANONYMOUS is also a search - match
2007-07-22 22:23:00 +00:00
* @ param array & $id_ary passed by reference , to be filled with ids for the page specified by $start and $per_page , should be ordered
* @ param int $start indicates the first index of the page
* @ param int $per_page number of ids each page is supposed to contain
* @ return boolean | int total number of results
*
* @ access public
*/
2010-02-10 10:31:00 +00:00
function keyword_search ( $type , $fields , $terms , $sort_by_sql , $sort_key , $sort_dir , $sort_days , $ex_fid_ary , $m_approve_fid_ary , $topic_id , $author_ary , $author_name , & $id_ary , $start , $per_page )
2007-07-22 22:23:00 +00:00
{
// No keywords? No posts.
if ( ! $this -> search_query )
{
return false ;
}
// generate a search_key from all the options to identify the results
$search_key = md5 ( implode ( '#' , array (
implode ( ', ' , $this -> split_words ),
$type ,
$fields ,
$terms ,
$sort_days ,
$sort_key ,
$topic_id ,
implode ( ',' , $ex_fid_ary ),
implode ( ',' , $m_approve_fid_ary ),
implode ( ',' , $author_ary )
)));
// try reading the results from cache
$result_count = 0 ;
if ( $this -> obtain_ids ( $search_key , $result_count , $id_ary , $start , $per_page , $sort_dir ) == SEARCH_RESULT_IN_CACHE )
{
return $result_count ;
}
$id_ary = array ();
$join_topic = ( $type == 'posts' ) ? false : true ;
// Build sql strings for sorting
$sql_sort = $sort_by_sql [ $sort_key ] . (( $sort_dir == 'a' ) ? ' ASC' : ' DESC' );
$sql_sort_table = $sql_sort_join = '' ;
switch ( $sql_sort [ 0 ])
{
case 'u' :
$sql_sort_table = USERS_TABLE . ' u, ' ;
$sql_sort_join = ( $type == 'posts' ) ? ' AND u.user_id = p.poster_id ' : ' AND u.user_id = t.topic_poster ' ;
break ;
case 't' :
$join_topic = true ;
break ;
case 'f' :
$sql_sort_table = FORUMS_TABLE . ' f, ' ;
$sql_sort_join = ' AND f.forum_id = p.forum_id ' ;
break ;
}
// Build some display specific sql strings
switch ( $fields )
{
case 'titleonly' :
$sql_match = 'p.post_subject' ;
$sql_match_where = ' AND p.post_id = t.topic_first_post_id' ;
$join_topic = true ;
break ;
case 'msgonly' :
$sql_match = 'p.post_text' ;
$sql_match_where = '' ;
break ;
case 'firstpost' :
$sql_match = 'p.post_subject, p.post_text' ;
$sql_match_where = ' AND p.post_id = t.topic_first_post_id' ;
$join_topic = true ;
break ;
default :
$sql_match = 'p.post_subject, p.post_text' ;
$sql_match_where = '' ;
break ;
}
if ( ! sizeof ( $m_approve_fid_ary ))
{
$m_approve_fid_sql = ' AND p.post_approved = 1' ;
}
else if ( $m_approve_fid_ary === array ( - 1 ))
{
$m_approve_fid_sql = '' ;
}
else
{
2012-07-22 16:50:09 +05:30
$m_approve_fid_sql = ' AND (p.post_approved = 1 OR ' . $this -> db -> sql_in_set ( 'p.forum_id' , $m_approve_fid_ary , true ) . ')' ;
2007-07-22 22:23:00 +00:00
}
$sql_select = ( $type == 'posts' ) ? 'p.post_id' : 'DISTINCT t.topic_id' ;
$sql_from = ( $join_topic ) ? TOPICS_TABLE . ' t, ' : '' ;
$field = ( $type == 'posts' ) ? 'post_id' : 'topic_id' ;
$sql_author = ( sizeof ( $author_ary ) == 1 ) ? ' = ' . $author_ary [ 0 ] : 'IN (' . implode ( ', ' , $author_ary ) . ')' ;
2010-04-29 15:15:23 -04:00
2010-02-10 10:31:00 +00:00
if ( sizeof ( $author_ary ) && $author_name )
{
// first one matches post of registered users, second one guests and deleted users
2012-07-22 16:50:09 +05:30
$sql_author = '(' . $this -> db -> sql_in_set ( 'p.poster_id' , array_diff ( $author_ary , array ( ANONYMOUS )), false , true ) . ' OR p.post_username ' . $author_name . ')' ;
2010-02-10 10:31:00 +00:00
}
else if ( sizeof ( $author_ary ))
{
2012-07-22 16:50:09 +05:30
$sql_author = ' AND ' . $this -> db -> sql_in_set ( 'p.poster_id' , $author_ary );
2010-02-10 10:31:00 +00:00
}
else
{
$sql_author = '' ;
}
2010-04-29 15:15:23 -04:00
2007-07-22 22:23:00 +00:00
$sql_where_options = $sql_sort_join ;
$sql_where_options .= ( $topic_id ) ? ' AND p.topic_id = ' . $topic_id : '' ;
$sql_where_options .= ( $join_topic ) ? ' AND t.topic_id = p.topic_id' : '' ;
2012-07-22 16:50:09 +05:30
$sql_where_options .= ( sizeof ( $ex_fid_ary )) ? ' AND ' . $this -> db -> sql_in_set ( 'p.forum_id' , $ex_fid_ary , true ) : '' ;
2007-07-22 22:23:00 +00:00
$sql_where_options .= $m_approve_fid_sql ;
2010-02-10 10:31:00 +00:00
$sql_where_options .= $sql_author ;
2007-07-22 22:23:00 +00:00
$sql_where_options .= ( $sort_days ) ? ' AND p.post_time >= ' . ( time () - ( $sort_days * 86400 )) : '' ;
$sql_where_options .= $sql_match_where ;
$tmp_sql_match = array ();
foreach ( explode ( ',' , $sql_match ) as $sql_match_column )
{
2012-07-22 16:50:09 +05:30
$tmp_sql_match [] = " to_tsvector (' " . $this -> db -> sql_escape ( $this -> config [ 'fulltext_postgres_ts_name' ]) . " ', " . $sql_match_column . " ) @@ to_tsquery (' " . $this -> db -> sql_escape ( $this -> config [ 'fulltext_postgres_ts_name' ]) . " ', ' " . $this -> db -> sql_escape ( $this -> tsearch_query ) . " ') " ;
2007-07-22 22:23:00 +00:00
}
$sql = " SELECT $sql_select
FROM $sql_from $sql_sort_table " . POSTS_TABLE . " p
WHERE ( " . implode(' OR ', $tmp_sql_match ) . " )
$sql_where_options
ORDER BY $sql_sort " ;
2012-07-22 16:50:09 +05:30
$result = $this -> db -> sql_query_limit ( $sql , $this -> config [ 'search_block_size' ], $start );
2007-07-22 22:23:00 +00:00
2012-07-22 16:50:09 +05:30
while ( $row = $this -> db -> sql_fetchrow ( $result ))
2007-07-22 22:23:00 +00:00
{
$id_ary [] = $row [ $field ];
}
2012-07-22 16:50:09 +05:30
$this -> db -> sql_freeresult ( $result );
2007-07-22 22:23:00 +00:00
$id_ary = array_unique ( $id_ary );
if ( ! sizeof ( $id_ary ))
{
return false ;
}
// if the total result count is not cached yet, retrieve it from the db
if ( ! $result_count )
{
$result_count = sizeof ( $id_ary );
if ( ! $result_count )
{
return false ;
}
}
// store the ids, from start on then delete anything that isn't on the current page because we only need ids for one page
$this -> save_ids ( $search_key , implode ( ' ' , $this -> split_words ), $author_ary , $result_count , $id_ary , $start , $sort_dir );
$id_ary = array_slice ( $id_ary , 0 , ( int ) $per_page );
return $result_count ;
}
/**
* Performs a search on an author ' s posts without caring about message contents . Depends on display specific params
*
2010-02-10 10:31:00 +00:00
* @ param string $type contains either posts or topics depending on what should be searched for
* @ param boolean $firstpost_only if true , only topic starting posts will be considered
* @ param array $sort_by_sql contains SQL code for the ORDER BY part of a query
* @ param string $sort_key is the key of $sort_by_sql for the selected sorting
* @ param string $sort_dir is either a or d representing ASC and DESC
* @ param string $sort_days specifies the maximum amount of days a post may be old
* @ param array $ex_fid_ary specifies an array of forum ids which should not be searched
* @ param array $m_approve_fid_ary specifies an array of forum ids in which the searcher is allowed to view unapproved posts
* @ param int $topic_id is set to 0 or a topic id , if it is not 0 then only posts in this topic should be searched
* @ param array $author_ary an array of author ids
* @ param string $author_name specifies the author match , when ANONYMOUS is also a search - match
* @ param array & $id_ary passed by reference , to be filled with ids for the page specified by $start and $per_page , should be ordered
* @ param int $start indicates the first index of the page
* @ param int $per_page number of ids each page is supposed to contain
* @ return boolean | int total number of results
*
* @ access public
2007-07-22 22:23:00 +00:00
*/
2010-02-10 10:31:00 +00:00
function author_search ( $type , $firstpost_only , $sort_by_sql , $sort_key , $sort_dir , $sort_days , $ex_fid_ary , $m_approve_fid_ary , $topic_id , $author_ary , $author_name , & $id_ary , $start , $per_page )
2007-07-22 22:23:00 +00:00
{
// No author? No posts.
if ( ! sizeof ( $author_ary ))
{
return 0 ;
}
// generate a search_key from all the options to identify the results
$search_key = md5 ( implode ( '#' , array (
'' ,
$type ,
( $firstpost_only ) ? 'firstpost' : '' ,
'' ,
'' ,
$sort_days ,
$sort_key ,
$topic_id ,
implode ( ',' , $ex_fid_ary ),
implode ( ',' , $m_approve_fid_ary ),
2010-02-10 10:31:00 +00:00
implode ( ',' , $author_ary ),
$author_name ,
2007-07-22 22:23:00 +00:00
)));
// try reading the results from cache
$result_count = 0 ;
if ( $this -> obtain_ids ( $search_key , $result_count , $id_ary , $start , $per_page , $sort_dir ) == SEARCH_RESULT_IN_CACHE )
{
return $result_count ;
}
$id_ary = array ();
2010-04-29 15:15:23 -04:00
2007-07-22 22:23:00 +00:00
// Create some display specific sql strings
2010-02-10 10:31:00 +00:00
if ( $author_name )
{
// first one matches post of registered users, second one guests and deleted users
2012-07-22 16:50:09 +05:30
$sql_author = '(' . $this -> db -> sql_in_set ( 'p.poster_id' , array_diff ( $author_ary , array ( ANONYMOUS )), false , true ) . ' OR p.post_username ' . $author_name . ')' ;
2010-02-10 10:31:00 +00:00
}
else
{
2012-07-22 16:50:09 +05:30
$sql_author = $this -> db -> sql_in_set ( 'p.poster_id' , $author_ary );
2010-02-10 10:31:00 +00:00
}
2012-07-22 16:50:09 +05:30
$sql_fora = ( sizeof ( $ex_fid_ary )) ? ' AND ' . $this -> db -> sql_in_set ( 'p.forum_id' , $ex_fid_ary , true ) : '' ;
2007-07-22 22:23:00 +00:00
$sql_topic_id = ( $topic_id ) ? ' AND p.topic_id = ' . ( int ) $topic_id : '' ;
$sql_time = ( $sort_days ) ? ' AND p.post_time >= ' . ( time () - ( $sort_days * 86400 )) : '' ;
$sql_firstpost = ( $firstpost_only ) ? ' AND p.post_id = t.topic_first_post_id' : '' ;
// Build sql strings for sorting
$sql_sort = $sort_by_sql [ $sort_key ] . (( $sort_dir == 'a' ) ? ' ASC' : ' DESC' );
$sql_sort_table = $sql_sort_join = '' ;
switch ( $sql_sort [ 0 ])
{
case 'u' :
$sql_sort_table = USERS_TABLE . ' u, ' ;
$sql_sort_join = ( $type == 'posts' ) ? ' AND u.user_id = p.poster_id ' : ' AND u.user_id = t.topic_poster ' ;
break ;
case 't' :
2010-02-10 10:31:00 +00:00
$sql_sort_table = ( $type == 'posts' && ! $firstpost_only ) ? TOPICS_TABLE . ' t, ' : '' ;
$sql_sort_join = ( $type == 'posts' && ! $firstpost_only ) ? ' AND t.topic_id = p.topic_id ' : '' ;
2007-07-22 22:23:00 +00:00
break ;
case 'f' :
$sql_sort_table = FORUMS_TABLE . ' f, ' ;
$sql_sort_join = ' AND f.forum_id = p.forum_id ' ;
break ;
}
if ( ! sizeof ( $m_approve_fid_ary ))
{
$m_approve_fid_sql = ' AND p.post_approved = 1' ;
}
else if ( $m_approve_fid_ary == array ( - 1 ))
{
$m_approve_fid_sql = '' ;
}
else
{
2012-07-22 16:50:09 +05:30
$m_approve_fid_sql = ' AND (p.post_approved = 1 OR ' . $this -> db -> sql_in_set ( 'p.forum_id' , $m_approve_fid_ary , true ) . ')' ;
2007-07-22 22:23:00 +00:00
}
2010-04-29 15:15:23 -04:00
2007-07-22 22:23:00 +00:00
// Build the query for really selecting the post_ids
if ( $type == 'posts' )
{
$sql = " SELECT p.post_id
FROM " . $sql_sort_table . POSTS_TABLE . ' p' . (( $firstpost_only ) ? ', ' . TOPICS_TABLE . ' t ' : ' ') . "
WHERE $sql_author
$sql_topic_id
$sql_firstpost
$m_approve_fid_sql
$sql_fora
$sql_sort_join
$sql_time
ORDER BY $sql_sort " ;
$field = 'post_id' ;
}
else
{
$sql = " SELECT t.topic_id
FROM " . $sql_sort_table . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p
WHERE $sql_author
$sql_topic_id
$sql_firstpost
$m_approve_fid_sql
$sql_fora
AND t . topic_id = p . topic_id
$sql_sort_join
$sql_time
2010-02-10 10:31:00 +00:00
GROUP BY t . topic_id , $sort_by_sql [ $sort_key ]
2007-07-22 22:23:00 +00:00
ORDER BY $sql_sort " ;
$field = 'topic_id' ;
}
// Only read one block of posts from the db and then cache it
2012-07-22 16:50:09 +05:30
$result = $this -> db -> sql_query_limit ( $sql , $this -> config [ 'search_block_size' ], $start );
2007-07-22 22:23:00 +00:00
2012-07-22 16:50:09 +05:30
while ( $row = $this -> db -> sql_fetchrow ( $result ))
2007-07-22 22:23:00 +00:00
{
$id_ary [] = $row [ $field ];
}
2012-07-22 16:50:09 +05:30
$this -> db -> sql_freeresult ( $result );
2007-07-22 22:23:00 +00:00
// retrieve the total result count if needed
if ( ! $result_count )
{
$result_count = sizeof ( $id_ary );
if ( ! $result_count )
{
return false ;
}
}
if ( sizeof ( $id_ary ))
{
$this -> save_ids ( $search_key , '' , $author_ary , $result_count , $id_ary , $start , $sort_dir );
$id_ary = array_slice ( $id_ary , 0 , $per_page );
return $result_count ;
}
return false ;
}
/**
* Destroys cached search results , that contained one of the new words in a post so the results won ' t be outdated .
*
2012-06-23 02:14:46 +05:30
* @ param string $mode contains the post mode : edit , post , reply , quote ...
* @ param int $post_id contains the post id of the post to index
* @ param string $message contains the post text of the post
* @ param string $subject contains the subject of the post to index
* @ param int $poster_id contains the user id of the poster
* @ param int $forum_id contains the forum id of parent forum of the post
*
* @ access public
2007-07-22 22:23:00 +00:00
*/
function index ( $mode , $post_id , & $message , & $subject , $poster_id , $forum_id )
{
// Split old and new post/subject to obtain array of words
$split_text = $this -> split_message ( $message );
$split_title = ( $subject ) ? $this -> split_message ( $subject ) : array ();
$words = array_unique ( array_merge ( $split_text , $split_title ));
unset ( $split_text );
unset ( $split_title );
// destroy cached search results containing any of the words removed or added
$this -> destroy_cache ( $words , array ( $poster_id ));
unset ( $words );
}
/**
* Destroy cached results , that might be outdated after deleting a post
2012-06-23 02:14:46 +05:30
*
* @ access public
2007-07-22 22:23:00 +00:00
*/
function index_remove ( $post_ids , $author_ids , $forum_ids )
{
$this -> destroy_cache ( array (), $author_ids );
}
/**
* Destroy old cache entries
2012-06-23 02:14:46 +05:30
*
* @ access public
2007-07-22 22:23:00 +00:00
*/
function tidy ()
{
// destroy too old cached search results
$this -> destroy_cache ( array ());
set_config ( 'search_last_gc' , time (), true );
}
/**
* Create fulltext index
2012-06-23 02:14:46 +05:30
*
* @ return string | bool error string is returned incase of errors otherwise false
*
* @ access public
2007-07-22 22:23:00 +00:00
*/
function create_index ( $acp_module , $u_action )
{
// Make sure we can actually use PostgreSQL with fulltext indexes
if ( $error = $this -> init ())
{
return $error ;
}
if ( empty ( $this -> stats ))
{
$this -> get_stats ();
}
if ( ! isset ( $this -> stats [ 'post_subject' ]))
{
2012-07-22 16:50:09 +05:30
$this -> db -> sql_query ( " CREATE INDEX " . POSTS_TABLE . " _ " . $this -> config [ 'fulltext_postgres_ts_name' ] . " _post_subject ON " . POSTS_TABLE . " USING gin (to_tsvector (' " . $this -> db -> sql_escape ( $this -> config [ 'fulltext_postgres_ts_name' ]) . " ', post_subject)) " );
2007-07-22 22:23:00 +00:00
}
if ( ! isset ( $this -> stats [ 'post_text' ]))
{
2012-07-22 16:50:09 +05:30
$this -> db -> sql_query ( " CREATE INDEX " . POSTS_TABLE . " _ " . $this -> config [ 'fulltext_postgres_ts_name' ] . " _post_text ON " . POSTS_TABLE . " USING gin (to_tsvector (' " . $this -> db -> sql_escape ( $this -> config [ 'fulltext_postgres_ts_name' ]) . " ', post_text)) " );
2007-07-22 22:23:00 +00:00
}
2012-07-22 16:50:09 +05:30
$this -> db -> sql_query ( 'TRUNCATE TABLE ' . SEARCH_RESULTS_TABLE );
2007-07-22 22:23:00 +00:00
return false ;
}
/**
* Drop fulltext index
2012-06-23 02:14:46 +05:30
*
* @ return string | bool error string is returned incase of errors otherwise false
*
* @ access public
2007-07-22 22:23:00 +00:00
*/
function delete_index ( $acp_module , $u_action )
{
// Make sure we can actually use PostgreSQL with fulltext indexes
if ( $error = $this -> init ())
{
return $error ;
}
if ( empty ( $this -> stats ))
{
$this -> get_stats ();
}
if ( isset ( $this -> stats [ 'post_subject' ]))
{
2012-07-22 16:50:09 +05:30
$this -> db -> sql_query ( 'DROP INDEX ' . $this -> stats [ 'post_subject' ][ 'relname' ]);
2007-07-22 22:23:00 +00:00
}
if ( isset ( $this -> stats [ 'post_text' ]))
{
2012-07-22 16:50:09 +05:30
$this -> db -> sql_query ( 'DROP INDEX ' . $this -> stats [ 'post_text' ][ 'relname' ]);
2007-07-22 22:23:00 +00:00
}
2012-07-22 16:50:09 +05:30
$this -> db -> sql_query ( 'TRUNCATE TABLE ' . SEARCH_RESULTS_TABLE );
2007-07-22 22:23:00 +00:00
return false ;
}
/**
* Returns true if both FULLTEXT indexes exist
2012-06-23 02:14:46 +05:30
*
* @ access public
2007-07-22 22:23:00 +00:00
*/
function index_created ()
{
if ( empty ( $this -> stats ))
{
$this -> get_stats ();
}
return ( isset ( $this -> stats [ 'post_text' ]) && isset ( $this -> stats [ 'post_subject' ])) ? true : false ;
}
/**
* Returns an associative array containing information about the indexes
2012-06-23 02:14:46 +05:30
*
* @ access public
2007-07-22 22:23:00 +00:00
*/
function index_stats ()
{
if ( empty ( $this -> stats ))
{
$this -> get_stats ();
}
return array (
2012-07-22 16:50:09 +05:30
$this -> user -> lang [ 'FULLTEXT_POSTGRES_TOTAL_POSTS' ] => ( $this -> index_created ()) ? $this -> stats [ 'total_posts' ] : 0 ,
2007-07-22 22:23:00 +00:00
);
}
2012-06-23 02:14:46 +05:30
/**
* Computes the stats and store them in the $this -> stats associative array
*
* @ access private
*/
2007-07-22 22:23:00 +00:00
function get_stats ()
{
2012-07-22 16:50:09 +05:30
if ( $this -> db -> sql_layer != 'postgres' )
2012-06-10 17:13:03 +05:30
{
$this -> stats = array ();
return ;
}
2007-07-22 22:23:00 +00:00
$sql = " SELECT c2.relname, pg_catalog.pg_get_indexdef(i.indexrelid, 0, true) AS indexdef
FROM pg_catalog . pg_class c1 , pg_catalog . pg_index i , pg_catalog . pg_class c2
WHERE c1 . relname = '" . POSTS_TABLE . "'
AND pg_catalog . pg_table_is_visible ( c1 . oid )
AND c1 . oid = i . indrelid
AND i . indexrelid = c2 . oid " ;
2012-07-22 16:50:09 +05:30
$result = $this -> db -> sql_query ( $sql );
2007-07-22 22:23:00 +00:00
2012-07-22 16:50:09 +05:30
while ( $row = $this -> db -> sql_fetchrow ( $result ))
2007-07-22 22:23:00 +00:00
{
// deal with older PostgreSQL versions which didn't use Index_type
if ( strpos ( $row [ 'indexdef' ], 'to_tsvector' ) !== false )
{
2012-07-22 16:50:09 +05:30
if ( $row [ 'relname' ] == POSTS_TABLE . '_' . $this -> config [ 'fulltext_postgres_ts_name' ] . '_post_text' || $row [ 'relname' ] == POSTS_TABLE . '_post_text' )
2007-07-22 22:23:00 +00:00
{
$this -> stats [ 'post_text' ] = $row ;
}
2012-07-22 16:50:09 +05:30
else if ( $row [ 'relname' ] == POSTS_TABLE . '_' . $this -> config [ 'fulltext_postgres_ts_name' ] . '_post_subject' || $row [ 'relname' ] == POSTS_TABLE . '_post_subject' )
2007-07-22 22:23:00 +00:00
{
$this -> stats [ 'post_subject' ] = $row ;
}
}
}
2012-07-22 16:50:09 +05:30
$this -> db -> sql_freeresult ( $result );
2007-07-22 22:23:00 +00:00
2012-07-22 16:50:09 +05:30
$this -> stats [ 'total_posts' ] = $this -> config [ 'num_posts' ];
2007-07-22 22:23:00 +00:00
}
/**
2012-06-23 02:14:46 +05:30
* Display various options that can be configured for the backend from the acp
*
* @ return associative array containing template and config variables
*
* @ access public
2007-07-22 22:23:00 +00:00
*/
function acp ()
{
$tpl = '
< dl >
2012-07-22 16:50:09 +05:30
< dt >< label > ' . $this->user->lang[' FULLTEXT_POSTGRES_VERSION_CHECK '] . ' </ label >< br />< span > ' . $this->user->lang[' FULLTEXT_POSTGRES_VERSION_CHECK_EXPLAIN '] . ' </ span ></ dt >
< dd > ' . (($this->tsearch_usable) ? $this->user->lang[' YES '] : $this->user->lang[' NO ']) . ' ( PostgreSQL ' . $this->version . ' ) </ dd >
2007-07-22 22:23:00 +00:00
</ dl >
< dl >
2012-07-22 16:50:09 +05:30
< dt >< label > ' . $this->user->lang[' FULLTEXT_POSTGRES_TS_NAME '] . ' </ label >< br />< span > ' . $this->user->lang[' FULLTEXT_POSTGRES_TS_NAME_EXPLAIN '] . ' </ span ></ dt >
2007-07-22 22:23:00 +00:00
< dd >< select name = " config[fulltext_postgres_ts_name] " > ' ;
2012-07-22 16:50:09 +05:30
if ( $this -> db -> sql_layer == 'postgres' && $this -> tsearch_usable )
2007-07-22 22:23:00 +00:00
{
2012-06-10 17:11:30 +05:30
$sql = ' SELECT cfgname AS ts_name
FROM pg_ts_config ' ;
2012-07-22 16:50:09 +05:30
$result = $this -> db -> sql_query ( $sql );
2007-07-22 22:23:00 +00:00
2012-07-22 16:50:09 +05:30
while ( $row = $this -> db -> sql_fetchrow ( $result ))
2007-07-22 22:23:00 +00:00
{
2012-07-22 16:50:09 +05:30
$tpl .= '<option value="' . $row [ 'ts_name' ] . '"' . ( $row [ 'ts_name' ] === $this -> config [ 'fulltext_postgres_ts_name' ] ? ' selected="selected"' : '' ) . '>' . $row [ 'ts_name' ] . '</option>' ;
2007-07-22 22:23:00 +00:00
}
2012-07-22 16:50:09 +05:30
$this -> db -> sql_freeresult ( $result );
2007-07-22 22:23:00 +00:00
}
else
{
2012-07-22 16:50:09 +05:30
$tpl .= '<option value="' . $this -> config [ 'fulltext_postgres_ts_name' ] . '" selected="selected">' . $this -> config [ 'fulltext_postgres_ts_name' ] . '</option>' ;
2007-07-22 22:23:00 +00:00
}
$tpl .= ' </ select ></ dd >
</ dl >
< dl >
2012-07-22 16:50:09 +05:30
< dt >< label for = " fulltext_postgres_min_word_len " > ' . $this->user->lang[' FULLTEXT_POSTGRES_MIN_WORD_LEN '] . ' :</ label >< br />< span > ' . $this->user->lang[' FULLTEXT_POSTGRES_MIN_WORD_LEN_EXPLAIN '] . ' </ span ></ dt >
< dd >< input id = " fulltext_postgres_min_word_len " type = " text " size = " 3 " maxlength = " 3 " name = " config[fulltext_postgres_min_word_len] " value = " ' . (int) $this->config ['fulltext_postgres_min_word_len'] . ' " /></ dd >
2007-07-22 22:23:00 +00:00
</ dl >
< dl >
2012-07-22 16:50:09 +05:30
< dt >< label for = " fulltext_postgres_max_word_len " > ' . $this->user->lang[' FULLTEXT_POSTGRES_MAX_WORD_LEN '] . ' :</ label >< br />< span > ' . $this->user->lang[' FULLTEXT_POSTGRES_MAX_WORD_LEN_EXPLAIN '] . ' </ span ></ dt >
< dd >< input id = " fulltext_postgres_max_word_len " type = " text " size = " 3 " maxlength = " 3 " name = " config[fulltext_postgres_max_word_len] " value = " ' . (int) $this->config ['fulltext_postgres_max_word_len'] . ' " /></ dd >
2007-07-22 22:23:00 +00:00
</ dl >
' ;
// These are fields required in the config table
return array (
'tpl' => $tpl ,
'config' => array ( 'fulltext_postgres_ts_name' => 'string' , 'fulltext_postgres_min_word_len' => 'integer:0:255' , 'fulltext_postgres_max_word_len' => 'integer:0:255' )
);
}
}