Source for file search_lib.php
Documentation is available at search_lib.php
* @copyright (c) 2004 bitweaver.org
* Copyright (c) 2003 tikwiki.org
* Copyright (c) 2002-2003, Luis Argerich, Garland Foster, Eduardo Polidor, et. al.
* All Rights Reserved. See below for details and a complete list of authors.
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See http://www.gnu.org/copyleft/lesser.html for details
* @author Luis Argerich (lrargerich@yahoo.com)
$this->wordlist_cache = array(); // for caching queries to the LRU-cache-list.
foreach ($words as $word) {
"search_stats` WHERE `term`=?", array($word));
$query = "UPDATE `" . BIT_DB_PREFIX . "search_stats` SET `hits`= `hits` + 1 WHERE `term`=?";
$query = "INSERT INTO `" . BIT_DB_PREFIX . "search_stats` (`term`,`hits`) VALUES (?,1)";
$result = $this->mDb->query($query,array($word));
function find( &$pParamHash ) { // $where, $words, $offset, $max_records, $plUsePart = false) {
$pParamHash['words'] = preg_split("/[\W]+/", strtolower($pParamHash['words']), - 1, PREG_SPLIT_NO_EMPTY);
if ( isset ($pParamHash['$plUsePart']) && $pParamHash['$plUsePart'] ) {
if ( array( $wordList ) ) {
$pParamHash['words'] = array_merge( $pParamHash['words'], $wordList );
* This function checks the search_syllable table to see how old the "syllable" is
* If the syllable is to old or doesn't exist, it refreshes the syllable/word list stored in search_words
* Then, it get a list of words from the search_words table and returns an array of them
$search_syll_age = $gBitSystem->getConfig( 'search_syll_age', SEARCH_PKG_NAME );
foreach($syllables as $syllable) {
$bindvars = array($syllable);
$age = time() - $this->mDb->getOne(
"select `last_updated` from `" . BIT_DB_PREFIX . "search_syllable` where `syllable`=?",
if(!$age || $age > ($search_syll_age * 3600)) {// older than search_syll_age hours
// update lru last used value (Used to purge oldest last used records)
$this->mDb->query("update `" . BIT_DB_PREFIX . "search_syllable` set `last_used`=? where `syllable`=?",
array((int) $now, $syllable));
if(!isset ($this->wordlist_cache[$syllable])) {
$query = "select `searchword` from `" . BIT_DB_PREFIX . "search_words` where `syllable`=?";
$result = $this->mDb->query($query, array($syllable));
if ($result->RecordCount() > 0) {
while ($res = $result->fetchRow()) {
$this->wordlist_cache[$syllable][]= $res["searchword"];
$ret = $this->wordlist_cache[$syllable];
$search_max_syllwords = $gBitSystem->getConfig( 'search_max_syllwords', SEARCH_PKG_NAME );;
$search_lru_length = $gBitSystem->getConfig( 'search_lru_length', SEARCH_PKG_NAME );;
$search_lru_purge_rate = $gBitSystem->getConfig( 'search_lru_purge_rate', SEARCH_PKG_NAME );
// delete from wordlist and lru list
$this->mDb->query("delete from `". BIT_DB_PREFIX. "search_words` where `syllable`=?",array($syllable),- 1,- 1);
$this->mDb->query("delete from `". BIT_DB_PREFIX. "search_syllable` where `syllable`=?",array($syllable),- 1,- 1);
if (!isset ($search_max_syllwords)) {
$search_max_syllwords = 100;
$query = "SELECT `searchword`, SUM(`i_count`) AS `cnt` FROM `" . BIT_DB_PREFIX .
"search_index` WHERE `searchword` LIKE ? GROUP BY `searchword` ORDER BY 2 desc";
$result = $this->mDb->query($query, array('%' . $syllable . '%'), $search_max_syllwords); // search_max_syllwords: how many different search_words that contain the syllable are taken into account?. Sortet by number of occurences.
while ($res = $result->fetchRow()) {
$ret[] = $res["searchword"];
// cache this long running query
foreach($ret as $searchword) {
"search_words` (`syllable`,`searchword`) VALUES (?,?)",
array($syllable, $searchword), - 1, - 1);
// set lru list parameters
"search_syllable`(`syllable`,`last_used`,`last_updated`) values (?,?,?)",
array($syllable,(int) $now,(int) $now));
// at random rate: check length of lru list and purge these that
// have not been used for long time. This is what a lru list
if(rand(1, $search_lru_purge_rate) == 1) {
"search_syllable`", array());
if ($lrulength > $search_lru_length) { // only purge if lru list is too long.
$diff = $lrulength - $search_lru_length;
$query = "select `syllable` from `". BIT_DB_PREFIX. "search_syllable` ORDER BY `last_used` asc";
$result = $this->mDb->query($query, array(), $diff);
while ($res = $result->fetchRow()) {
$oldwords[]= $res["syllable"];
foreach($oldwords as $oldword) {
"search_words` where `syllable`=?", array($oldword), - 1, - 1);
"search_syllable` where `syllable`=?", array($oldword), - 1, - 1);
function find_with_or($allowed, $selectSql, $joinSql, $whereSql, $bindVars,&$pParamHash) {
// Putting in the below hack because mssql cannot select distinct on a text blob column.
$bindVars = array_merge( $pParamHash['words'], $allowed );
COALESCE(lch.`hits`,0) AS hits,
WHERE si.`content_id`=lc.`content_id` AND si.`searchword` IN (" . $qPlaceHolders1 . ")
LEFT OUTER JOIN `". BIT_DB_PREFIX. "liberty_content_hits` lch ON (lc.`content_id` = lch.`content_id`)
WHERE si.`content_id`=lc.`content_id`
AND si.`searchword` IN (" . $qPlaceHolders1 . ")
LEFT OUTER JOIN `". BIT_DB_PREFIX. "liberty_content_hits` lch ON (lc.`content_id` = lch.`content_id`)
WHERE si.`content_id`=lc.`content_id`
AND si.`searchword` IN (" . $qPlaceHolders1 . ")
$result = $this->mDb->query( $query, array_merge( $pParamHash['words'] ,$bindVars), $pParamHash['max_records'], $pParamHash['offset'] );
$pParamHash['cant'] = $this->mDb->getOne( $querycant, $bindVars );
while ($res = $result->fetchRow()) {
$res['href'] = BIT_ROOT_URL . "index.php?content_id=" . $res['content_id'];
function find_with_and($allowed, $selectSql, $joinSql, $whereSql, $bindVars, &$pParamHash) {
// Make a slot for the search word.
foreach($pParamHash['words'] as $word) {
$query = "SELECT lc.`content_id` AS hash_key,
COALESCE(lch.`hits`,0) AS hits,
si.`i_count` AS relevancy
LEFT OUTER JOIN `". BIT_DB_PREFIX. "liberty_content_hits` lch ON (lc.`content_id` = lch.`content_id`)
INNER JOIN `". BIT_DB_PREFIX. "search_index` si ON (si.`content_id`=lc.`content_id` AND si.`searchword` = ? )
WHERE `i_count` > 0 $whereSql
$result = $this->mDb->getAssoc( $query, $bindVars );
$pParamHash['cant'] = count($ret);
uasort($ret, 'search_relevance_sort');
$ret = array_slice($ret, $pParamHash['offset'], $pParamHash['offset'] + $pParamHash['max_records']);
foreach ($ret as $content_id => $data) {
$ret[$content_id]['href'] = BIT_ROOT_URL . "index.php?content_id=" . $data['content_id'];
global $gPage, $gBitSystem, $gLibertySystem, $gBitDbType;
foreach( $gLibertySystem->mContentTypes as $contentType ) {
if (( $pParamHash['content_type_guid'] == $contentType["content_type_guid"] or $pParamHash['content_type_guid'] == "" ) // pages ?
and ( ! $gBitSystem->getConfig('search_restrict_types') ||
$gBitSystem->getConfig('search_pkg_'. $contentType["content_type_guid"]) ) ) {
$allowed[] = $contentType["content_type_guid"];
if (count($allowed) > 0 && count($pParamHash['words']) > 0) {
if (isset ($pParamHash['useAnd']) && $pParamHash['useAnd']) {
return $this->find_with_and($allowed, $selectSql, $joinSql, $whereSql, $bindVars, $pParamHash);
return $this->find_with_or($allowed, $selectSql, $joinSql, $whereSql, $bindVars, $pParamHash);
// Remove those that don't overlap or update relevance
foreach ($ret as $content_id => $data) {
if (!isset ($result[$content_id])) {
unset ($ret[$content_id]);
$ret[$content_id]['relevancy'] += $result[$content_id]['relevancy'];
global $gBitUser, $gLibertySystem;
if ( ! empty( $pContentType ) ) {
if ( ! empty( $object ) ) {
// Note that we can't do verify access here because
// we are using a generic object but we can at least get a
// basic permission check here.
return $object->hasViewPermission(FALSE);
if (!defined('search_relevance_sort')) {
$rel = $b['relevancy'] - $a['relevancy'];
$rel = $b['hits'] - $a['hits'];
|