Projekt

Obecné

Profil

« Předchozí | Další » 

Revize 2236

Přidáno uživatelem Ondřej Fibich před asi 11 roky(ů)

Novinky:
- closes #811: Prepracovani fulltextu

Zobrazit rozdíly:

freenetis/branches/1.1/application/controllers/search.php
* Base method, makes search.
* Search result is placed to property $results
*
* @author Michal Kliment
* @author Ondrej Fibich, Michal Kliment
* @param string $keyword
* @param integer $limit Limit of results
*/
private function search($keyword = NULL, $limit = null)
private function search($keyword = NULL, $limit = NULL)
{
$keyword = preg_replace("[\]\[!\"#$%&'()*+,/:;<=>?@\^`{|}~-]", '', $keyword);
// trim and remove unrequired cars from keyword
$keyword = trim($keyword);
$mkeyword = preg_replace("/[\[\]!\"#$%&'()*+,\/:;<=>?@\^`{|}~]/", ' ', $keyword);
$keywords = explode(" ", trim($keyword));
// separate keywords
$keywords = explode(' ', preg_replace("/\s+/", ' ', $mkeyword));
$keys = array();
// variations - if keyword contains many words (>= 3) then only create
// variations that contains all words of keyword but only sorted in many
// ways. If keyword do not contain many words then make all variations.
// If keyword contains more then 5 words then disable variations at all.
$variation_keys = array();
// finds all possible variations
for ($i = count($keywords); $i > 0; $i--)
$keys = arr::merge($keys, arr::variation($keywords, $i));
if (count($keywords) < 3) // all
{
for ($i = count($keywords); $i > 0; $i--)
{
$variation_keys = arr::merge($variation_keys, arr::variation($keywords, $i));
}
}
else if (count($keywords) > 5) // only keyword
{
$variation_keys = array($mkeyword);
}
else // only mixed keyword
{
$variation_keys = arr::variation($keywords, count($keywords));
}
// search model
$search_model = new Search_Model();
// search variables
$search_rules = Search_Model::get_rules_sorted_by_weight();
$sums = array();
$counts = array();
$total_counts = array();
$values = array();
// each rule should get oportunity to be searched (reserved limits)
$result_limit = !empty($limit) ? ceil($limit / count($search_rules)) : NULL;
$total_count = 0;
// foreach all search rules
foreach (Search_Model::$rules as $rule)
foreach ($search_rules as $rule)
{
// networks is disabled
if ($rule['model'] == 'device' && !Settings::get('networks_enabled'))
if (!Settings::get('networks_enabled') && (
$rule['model'] == 'device' ||
$rule['model'] == 'subnet' ||
$rule['model'] == 'link'
))
{
continue;
}
// search only in keyword by default
$searched_keys = array($keyword);
// variation enabled?
if (isset($rule['variation_enabled']) && $rule['variation_enabled'])
{
$searched_keys = $variation_keys;
}
// foreach variations
foreach ($keys as $key)
foreach ($searched_keys as $key)
{
if (isset($total_counts[$rule['model']]))
{
$total_counts[$rule['model']]++;
}
else
{
$total_counts[$rule['model']] = 1;
}
$result = $search_model->{$rule['method']}($key, $limit);
$result = $search_model->{$rule['method']}($key, $result_limit);
foreach ($result as $row)
{
$titled_value = url::title($row->value, ' ');
$percent = 0;
// test how much are texts similar
similar_text(url::title($row->value, " "), url::title($key, " "), $percent);
similar_text($titled_value, url::title($key, ' '), $percent);
// rating and informations about this result was not registered yet
if (!isset($sums[$rule['model']][$row->id]))
{
$sums[$rule['model']][$row->id] = 0;
$counts[$rule['model']][$row->id] = 0;
$values[$rule['model']][$row->id] = $row;
}
$sums[$rule['model']][$row->id] += $percent;
// weight to percentage
$weight = $rule['weight'];
// special treatment if value is same as keyword
if (!isset($rule['ignore_special_threatment']) ||
!$rule['ignore_special_threatment'])
{
// keyword match with special threatment for
// login (we do want to mess tha members)
if (strtolower(trim($keyword)) == strtolower(trim($row->value)) &&
$rule['method'] != 'user_login')
{
$weight = 3.5;
}
// modified keyword match
if (strtolower(trim($mkeyword)) == strtolower(trim($row->value)))
{
$weight = 2.5;
}
// special treatment if titled value is same as titled modified keyword
else if (url::title($mkeyword) == $titled_value)
{
$weight = 2;
}
}
// special threatment for number results weight if they are equal
if ($rule['method'] == 'member_id' &&
intval($keyword) == intval($row->value))
{
// member ID is equal => increase weight of this result
$weight = 6;
}
else if ($rule['method'] == 'member_variable_symbol' &&
intval($keyword) == intval($row->value))
{
// variable key equals to key?
$weight = 6;
}
// add rating about the current result
$sums[$rule['model']][$row->id] += $percent * $weight;
$counts[$rule['model']][$row->id]++;
$total_count++;
if ($total_count >= $limit)
// end if we have already enought results
if (!empty($limit) && $total_count >= $limit)
{
break 3;
}
......
}
$result_sums = array();
$result_priorities = array();
$result_counts = array();
// transforms to 1-dimensional array
foreach ($sums as $model => $model_sums)
......
foreach ($model_sums as $id => $sum)
{
$result_sums[] = $sum;
if ($model == 'member')
$result_priorities[] = 1;
else
$result_priorities[] = 0;
$result_counts[] = $counts[$model][$id] / $total_counts[$model];
$this->results[] = $values[$model][$id];
}
}
// sorts results
array_multisort($result_priorities, SORT_DESC, $result_sums, SORT_DESC, $this->results, SORT_DESC);
array_multisort($result_sums, SORT_DESC, $this->results, SORT_DESC);
}
/**
......
* @param integer $page_word
* @param integer $page
*/
public function simple(
$keyword = NULL, $limit_results = 20, $page_word = 'page', $page = 1)
public function simple($keyword = NULL, $limit_results = 20,
$page_word = 'page', $page = 1)
{
//$profiler = new Profiler();
// bad parameter
if (!$keyword)
{
Controller::warning(PARAMETER);
}
// searching
$this->search($keyword);
......
$to = $page * $limit_results;
if ($to >= count($this->results))
{
$to = count($this->results) - 1;
}
$view = new View('main');
$view->keyword = $keyword;
......
$view->content->from = $from;
$view->content->to = $to;
$view->render(TRUE);
//echo $profiler->render(TRUE);
}
/**
* Ajax searching (for whisper)
*
* @author Michal Kliment
* @param integer $count
* @param integer $count do not change contans unless you know what it will cause!!
*/
public function ajax($count = 100)
public function ajax($count = 150)
{
$this->search($this->input->get('q'), $count);
$keyword = $this->input->get('q');
$this->search($keyword, $count);
$counter = 0;
// prints all results
foreach ($this->results as $result)
{
$counter++;
?>
<a href="<?php echo url_lang::base() . $result->link . $result->id ?>" class="whisper_search_result">
<b><?php echo $result->return_value ?></b><br />
<i><?php echo $result->desc ?></i>
</a>
<?php
if ($counter == $count)
break;
}
// no results
if (!$counter)
{
?>
<div class="whisper_search_result"><?php echo __('No items found.') ?></div>
<?php
}
$view = new View('search/ajax');
$view->total_items = count($this->results);
$view->results = $this->results;
$view->keyword = $keyword;
$view->render(TRUE);
}
}
freenetis/branches/1.1/application/helpers/text.php
return $replace.' '.$extra_text;
}
}
/**
* Highlight occurences of what in where by span with class highlighted
*
* @param string $what What should be highlighted
* @param string $where Where it should be highlighted
* @return string String with hightligthed occurences of what
*/
public static function highligth($what, $where)
{
// make every letter valid in regex
$what_letters = str_split($what);
foreach ($what_letters as $i => $valid_what_letter)
{
$what_letters[$i] = '[' . $valid_what_letter . ']';
}
// highlingth occurrences with ignore case
$r = preg_replace(
'/(' . implode('', $what_letters) . ')/i',
'<span class="highlighted">$1</span>',
$where
);
// an error occured, we will rather return old string
if (!$r)
{
return $where;
}
return $r;
}
} // End text
freenetis/branches/1.1/application/models/search.php
class Search_Model extends Model
{
/**
* Definitions of search rules
* Definitions of search rules, each item contains:
*
* - method: that defines which method of search model should be use for searching
* - model: that defines where searched property belongs
* - weight: that defines importance of the property in system, these field
* is used for searching of results. Weight should be a non-negative
* number from interval (0, 10>
* - ignore_special_threatment: that defines whether weight can be changed
* if property equals to searched keyword [optional, default FALSE]
* - variation_enabled: enable variations of keyword for searching this
* property, used because performance reasons [optional, default FALSE]
*
* @var array
*/
......
array
(
'method' => 'member_name',
'model' => 'member'
'model' => 'member',
'weight' => 5,
'variation_enabled' => TRUE
),
array
(
'method' => 'member_id',
'model' => 'member'
'model' => 'member',
'weight' => 0.1
),
array
(
'method' => 'member_variable_symbol',
'model' => 'member'
'model' => 'member',
'weight' => 0.1
),
array
(
'method' => 'member_comment',
'model' => 'member'
'model' => 'member',
'weight' => 0.5,
'variation_enabled' => TRUE
),
array
(
'method' => 'member_town',
'model' => 'member'
'model' => 'member',
'weight' => 0.4,
'ignore_special_threatment' => TRUE,
'variation_enabled' => TRUE
),
array
(
'method' => 'member_street',
'model' => 'member'
'model' => 'member',
'weight' => 0.6,
'variation_enabled' => TRUE
),
array
(
'method' => 'member_street_number',
'model' => 'member',
'weight' => 0.1
),
array
(
'method' => 'member_organization_identifier',
'model' => 'member'
'model' => 'member',
'weight' => 0.1
),
array
(
'method' => 'member_vat_organization_identifier',
'model' => 'member'
'model' => 'member',
'weight' => 0.1
),
array
(
'method' => 'user_name',
'model' => 'user',
'weight' => 1,
'variation_enabled' => TRUE
),
array
(
'method' => 'user_login',
'model' => 'user',
'weight' => 0.5
),
array
(
'method' => 'user_contact',
'model' => 'user',
'weight' => 0.1
),
array
(
'method' => 'town_name',
'model' => 'town'
'model' => 'town',
'weight' => 1,
'variation_enabled' => TRUE
),
array
(
'method' => 'device_name',
'model' => 'device'
'model' => 'device',
'weight' => 1,
'variation_enabled' => TRUE
),
array
(
'method' => 'device_mac',
'model' => 'device'
'model' => 'device',
'weight' => 0.5
),
array
(
'method' => 'device_ip_address',
'model' => 'device'
'model' => 'device',
'weight' => 0.5
),
array(
'method' => 'device_ssid',
'model' => 'device'
'model' => 'device',
'weight' => 0.5
),
array
(
'method' => 'subnet_name',
'model' => 'subnet'
'model' => 'subnet',
'weight' => 1,
'variation_enabled' => TRUE
),
array
(
'method' => 'subnet_address',
'model' => 'subnet'
'model' => 'subnet',
'weight' => 1
),
array
(
'method' => 'link_name',
'model' => 'link'
'model' => 'link',
'weight' => 1,
'variation_enabled' => TRUE
),
);
/**
* Comparator for rules by weight (highest is first)
*
* @param mixed $a
* @param mixed $b
* @return integer
*/
private static function cmp_rule_weight($a, $b)
{
return ($a['weight'] < $b['weight']) ?
1 : (($a['weight'] == $b['weight']) ? 0 : -1);
}
/**
* Returns sorted rules by their weight (highest is first)
*
* @return array Sorted rules
*/
public static function get_rules_sorted_by_weight()
{
$rules = self::$rules;
usort($rules, 'Search_Model::cmp_rule_weight');
return $rules;
}
/**
* Searchs in members by name
*
* @author Michal Kliment
......
LEFT JOIN membership_interrupts mi ON m.id = mi.member_id
LEFT JOIN members_fees mf ON mi.members_fee_id = mf.id AND (mf.activation_date < CURDATE() AND mf.deactivation_date > CURDATE())
WHERE m.name LIKE ? COLLATE utf8_general_ci
".$sql_limit, array
(
" (" . __('I') . ")",
......
LEFT JOIN membership_interrupts mi ON m.id = mi.member_id
LEFT JOIN members_fees mf ON mi.members_fee_id = mf.id AND (mf.activation_date < CURDATE() AND mf.deactivation_date > CURDATE())
WHERE m.id LIKE ? COLLATE utf8_general_ci
".$sql_limit, array
(
" (" . __('I') . ")",
"%$keyword%"
"$keyword%"
));
}
......
LEFT JOIN membership_interrupts mi ON m.id = mi.member_id
LEFT JOIN members_fees mf ON mi.members_fee_id = mf.id AND (mf.activation_date < CURDATE() AND mf.deactivation_date > CURDATE())
WHERE vs.variable_symbol LIKE ? COLLATE utf8_general_ci
".$sql_limit, array
(
__('Variable symbol') . ": ",
" (" . __('I') . ")",
"%$keyword%"
"$keyword%"
));
}
......
LEFT JOIN membership_interrupts mi ON m.id = mi.member_id
LEFT JOIN members_fees mf ON mi.members_fee_id = mf.id AND (mf.activation_date < CURDATE() AND mf.deactivation_date > CURDATE())
WHERE m.comment LIKE ? COLLATE utf8_general_ci
".$sql_limit, array
(
__('Comment') . ": ",
......
return $this->db->query("
SELECT m.id,
CONCAT(town, ' ',IFNULL(quarter,'')) AS value,
CONCAT(town, IF(quarter IS NOT NULL, CONCAT(' ', quarter), '')) AS value,
CONCAT('ID ',m.id,', ',s.street,' ',ap.street_number,', ',tw.town,IF(tw.quarter IS NOT NULL,CONCAT('-',tw.quarter),'')) AS `desc`,
CONCAT(IFNULL(t.translated_term,e.value),' ',m.name,IF(mf.id IS NOT NULL,?,'')) AS return_value,
'members/show/' AS link FROM members m
......
JOIN towns tw ON ap.town_id = tw.id
LEFT JOIN membership_interrupts mi ON m.id = mi.member_id
LEFT JOIN members_fees mf ON mi.members_fee_id = mf.id AND (mf.activation_date < CURDATE() AND mf.deactivation_date > CURDATE())
WHERE CONCAT(tw.town, ' ',IFNULL(tw.quarter,'')) LIKE ? COLLATE utf8_general_ci
WHERE CONCAT(tw.town, IF(tw.quarter IS NOT NULL, CONCAT(' ', tw.quarter), '')) LIKE ? COLLATE utf8_general_ci
".$sql_limit, " (" . __('I') . ")", "%$keyword%");
}
......
LEFT JOIN membership_interrupts mi ON m.id = mi.member_id
LEFT JOIN members_fees mf ON mi.members_fee_id = mf.id AND (mf.activation_date < CURDATE() AND mf.deactivation_date > CURDATE())
WHERE CONCAT(s.street, ' ',ap.street_number) LIKE ? COLLATE utf8_general_ci
".$sql_limit, " (" . __('I') . ")", "%$keyword%");
}
/**
* Searchs in members by street and street number
*
* @author Michal Kliment
* @param string $keyword
* @return MySQL_Result object
*/
public function member_street_number($keyword, $limit = null)
{
$sql_limit = '';
if ($limit)
{
$sql_limit = ' LIMIT '.$limit;
}
return $this->db->query("
SELECT m.id,
ap.street_number AS value,
CONCAT('ID ',m.id,', ',s.street,' ',ap.street_number,', ',tw.town,IF(tw.quarter IS NOT NULL,CONCAT('-',tw.quarter),'')) AS `desc`,
CONCAT(IFNULL(t.translated_term,e.value),' ',m.name,IF(mf.id IS NOT NULL,?,'')) AS return_value,
'members/show/' AS link FROM members m
JOIN enum_types e ON m.type = e.id
LEFT JOIN translations t ON e.value LIKE t.original_term AND t.lang = 'cs'
JOIN address_points ap ON m.address_point_id = ap.id
JOIN streets s ON ap.street_id = s.id
JOIN towns tw ON ap.town_id = tw.id
LEFT JOIN membership_interrupts mi ON m.id = mi.member_id
LEFT JOIN members_fees mf ON mi.members_fee_id = mf.id AND (mf.activation_date < CURDATE() AND mf.deactivation_date > CURDATE())
WHERE ap.street_number LIKE ? COLLATE utf8_general_ci
".$sql_limit, " (" . __('I') . ")", "$keyword%");
}
/**
* Searchs in members by organization identifier
......
}
return $this->db->query("
SELECT m.id, m.id AS value,
SELECT m.id, m.organization_identifier AS value,
CONCAT('ID ',m.id,', ',s.street,' ',ap.street_number,', ',tw.town,IF(tw.quarter IS NOT NULL,CONCAT('-',tw.quarter),'')) AS `desc`,
CONCAT(IFNULL(t.translated_term,e.value),' ',m.name,IF(mf.id IS NOT NULL,?,'')) AS return_value,
'members/show/' AS link FROM members m
......
LEFT JOIN membership_interrupts mi ON m.id = mi.member_id
LEFT JOIN members_fees mf ON mi.members_fee_id = mf.id AND (mf.activation_date < CURDATE() AND mf.deactivation_date > CURDATE())
WHERE m.organization_identifier LIKE ? COLLATE utf8_general_ci
".$sql_limit, array
(
" (" . __('I') . ")",
"%$keyword%"
"$keyword%"
));
}
......
}
return $this->db->query("
SELECT m.id, m.id AS value,
SELECT m.id, m.vat_organization_identifier AS value,
CONCAT('ID ',m.id,', ',s.street,' ',ap.street_number,', ',tw.town,IF(tw.quarter IS NOT NULL,CONCAT('-',tw.quarter),'')) AS `desc`,
CONCAT(IFNULL(t.translated_term,e.value),' ',m.name,IF(mf.id IS NOT NULL,?,'')) AS return_value,
'members/show/' AS link FROM members m
......
LEFT JOIN membership_interrupts mi ON m.id = mi.member_id
LEFT JOIN members_fees mf ON mi.members_fee_id = mf.id AND (mf.activation_date < CURDATE() AND mf.deactivation_date > CURDATE())
WHERE m.vat_organization_identifier LIKE ? COLLATE utf8_general_ci
".$sql_limit, array
(
" (" . __('I') . ")",
"%$keyword%"
"$keyword%"
));
}
......
CONCAT(?, u.name, ' ',u.surname) AS return_value, 'users/show/' AS link
FROM users u
WHERE CONCAT(u.name,' ',u.surname) LIKE ? COLLATE utf8_general_ci
".$sql_limit, array
(
__('Username') . ": ",
......
CONCAT(?, u.name, ' ',u.surname) AS return_value,
'users/show/' AS link
FROM users u WHERE u.login LIKE ? COLLATE utf8_general_ci
".$sql_limit, array
(
__('Username') . ": ",
......
LEFT JOIN enum_types e ON c.type = e.id
LEFT JOIN translations t ON e.value LIKE t.original_term AND t.lang = 'cs'
WHERE c.value LIKE ? COLLATE utf8_general_ci
ORDER BY ABS(LENGTH(c.value) - " . mb_strlen($keyword) . ")
".$sql_limit, __('User') . ": ", "%$keyword%");
}
......
}
return $this->db->query("
SELECT t.id, CONCAT(t.town, ' ',t.quarter) AS value, t.id AS `desc`,
SELECT t.id,
CONCAT(t.town, IF(t.quarter IS NOT NULL, CONCAT(' ', t.quarter), '')) AS value,
CONCAT(?,t.zip_code) AS `desc`,
CONCAT(?,t.town,IF(t.quarter IS NOT NULL AND t.quarter <> '',CONCAT('-',t.quarter),'')) AS return_value,
'towns/show/' AS link
FROM towns t
WHERE CONCAT(t.town, ' ',t.quarter) LIKE ? COLLATE utf8_general_ci
WHERE CONCAT(t.town, IF(t.quarter IS NOT NULL, CONCAT(' ', t.quarter), '')) LIKE ? COLLATE utf8_general_ci
".$sql_limit, array
(
__('ZIP code') . ": ",
......
FROM devices d
JOIN users u ON d.user_id = u.id
WHERE d.name LIKE ? COLLATE utf8_general_ci
".$sql_limit, array
(
__('User') . ": ",
......
FROM devices d
JOIN ifaces ic ON ic.device_id = d.id
WHERE ic.mac LIKE ? COLLATE utf8_general_ci
".$sql_limit, array
(
__('MAC address') . ": ",
......
JOIN ifaces ic ON ic.device_id = d.id
LEFT JOIN ip_addresses ip ON ip.iface_id = ic.id
WHERE ip.ip_address LIKE ? COLLATE utf8_general_ci
".$sql_limit, array
(
__('IP address') . ": ",
......
JOIN links s ON i.link_id = s.id
WHERE i.wireless_mode = ? AND s.medium = ? AND
s.wireless_ssid LIKE ? COLLATE utf8_general_ci
".$sql_limit, array
(
__('SSID') . ": ",
......
'subnets/show/' AS link
FROM subnets s
WHERE s.name LIKE ? COLLATE utf8_general_ci
".$sql_limit, array
(
__('Address') . ": ",
......
'subnets/show/' AS link
FROM subnets s
WHERE CONCAT(s.network_address,'/',32-log2((~inet_aton(s.netmask) & 0xffffffff) + 1)) LIKE ? COLLATE utf8_general_ci
".$sql_limit, array
(
__('Address') . ": ",
......
'links/show/' AS link
FROM links s
WHERE s.name LIKE ? COLLATE utf8_general_ci
".$sql_limit, array
(
__('Link') . ' ',
freenetis/branches/1.1/application/views/search/ajax.php
<?php if ($total_items > 0): ?>
<?php foreach ($results as $result): ?>
<a href="<?php echo url_lang::base() . $result->link . $result->id ?>" class="whisper_search_result">
<b><?php echo text::highligth($keyword, $result->return_value) ?></b><br />
<i><?php echo text::highligth($keyword, $result->desc) ?></i>
</a>
<?php endforeach ?>
<?php else: ?>
<div class="whisper_search_result"><?php echo __('No items found.') ?></div>
<?php endif;
freenetis/branches/1.1/application/views/search/simple.php
<?php echo __('Total found items') ?>: <?php echo $total_items ?>
<?php for ($i = $from; $i <= $to; $i++): ?>
<div class="search_result">
<b><?php echo html::anchor(url_lang::base() . $results[$i]->link . $results[$i]->id, $results[$i]->return_value, array('class' => 'search_result_title')) ?></b><br />
<i><?php echo $results[$i]->desc ?></i>
<b><?php echo html::anchor(url_lang::base() . $results[$i]->link . $results[$i]->id, text::highligth($keyword, $results[$i]->return_value), array('class' => 'search_result_title')) ?></b><br />
<i><?php echo text::highligth($keyword, $results[$i]->desc) ?></i>
</div>
<?php endfor ?>
freenetis/branches/1.1/media/css/style.css
.search_result_title {
}
/* highlighting of results of searching */
span.highlighted {
background-color: #fed480;
}
#whisper {
display: none;
background-color: white;

Také k dispozici: Unified diff