Improve search in calendar: consider recurrence exceptions for indexing and matching (#4279); ignore order of search terms (boolean and matching)
This commit is contained in:
parent
e6c11695f9
commit
754a4d51bf
2 changed files with 65 additions and 20 deletions
|
@ -274,21 +274,13 @@ class kolab_calendar extends kolab_storage_folder_api
|
||||||
|
|
||||||
// filter events by search query
|
// filter events by search query
|
||||||
if (!empty($search)) {
|
if (!empty($search)) {
|
||||||
$hit = false;
|
$hits = 0;
|
||||||
foreach ($this->search_fields as $col) {
|
$words = rcube_utils::tokenize_string($search, 1);
|
||||||
$sval = is_array($event[$col]) ? self::_complex2string($event[$col]) : $event[$col];
|
foreach ($words as $word) {
|
||||||
if (empty($sval))
|
$hits += $this->_fulltext_match($event, $word);
|
||||||
continue;
|
|
||||||
|
|
||||||
// do a simple substring matching (to be improved)
|
|
||||||
$val = mb_strtolower($sval);
|
|
||||||
if (strpos($val, $search) !== false) {
|
|
||||||
$hit = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$hit) // skip this event if not match with search term
|
if ($hits < count($words)) // skip this event if not match with search term
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,6 +316,18 @@ class kolab_calendar extends kolab_storage_folder_api
|
||||||
// resolve recurring events
|
// resolve recurring events
|
||||||
if ($record['recurrence'] && $virtual == 1) {
|
if ($record['recurrence'] && $virtual == 1) {
|
||||||
$events = array_merge($events, $this->get_recurring_events($record, $start, $end));
|
$events = array_merge($events, $this->get_recurring_events($record, $start, $end));
|
||||||
|
|
||||||
|
// when searching, only recurrence exceptions may match the query so post-filter the results again
|
||||||
|
if (!empty($search) && $record['recurrence']['EXCEPTIONS']) {
|
||||||
|
$me = $this;
|
||||||
|
$events = array_filter($events, function($event) use ($words, $me) {
|
||||||
|
$hits = 0;
|
||||||
|
foreach ($words as $word) {
|
||||||
|
$hits += $me->_fulltext_match($event, $word, false);
|
||||||
|
}
|
||||||
|
return $hits >= count($words);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -755,6 +759,36 @@ class kolab_calendar extends kolab_storage_folder_api
|
||||||
return $event;
|
return $event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Match the given word in the event contents
|
||||||
|
*/
|
||||||
|
private function _fulltext_match($event, $word, $recursive = true)
|
||||||
|
{
|
||||||
|
$hits = 0;
|
||||||
|
foreach ($this->search_fields as $col) {
|
||||||
|
$sval = is_array($event[$col]) ? self::_complex2string($event[$col]) : $event[$col];
|
||||||
|
if (empty($sval))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// do a simple substring matching (to be improved)
|
||||||
|
$val = mb_strtolower($sval);
|
||||||
|
if (strpos($val, $word) !== false) {
|
||||||
|
$hits++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// search in recurrence exceptions
|
||||||
|
if (!$hits && $recursive && !empty($event['recurrence']['EXCEPTIONS'])) {
|
||||||
|
foreach ($event['recurrence']['EXCEPTIONS'] as $exception) {
|
||||||
|
$hits = $this->_fulltext_match($exception, $word, false);
|
||||||
|
if ($hits) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $hits;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a complex event attribute to a string value
|
* Convert a complex event attribute to a string value
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -581,27 +581,38 @@ abstract class kolab_format_xcal extends kolab_format
|
||||||
*
|
*
|
||||||
* @return array List of words to save in cache
|
* @return array List of words to save in cache
|
||||||
*/
|
*/
|
||||||
public function get_words()
|
public function get_words($obj = null)
|
||||||
{
|
{
|
||||||
$data = '';
|
$data = '';
|
||||||
|
$object = $obj ?: $this->data;
|
||||||
|
|
||||||
foreach (self::$fulltext_cols as $colname) {
|
foreach (self::$fulltext_cols as $colname) {
|
||||||
list($col, $field) = explode(':', $colname);
|
list($col, $field) = explode(':', $colname);
|
||||||
|
|
||||||
if ($field) {
|
if ($field) {
|
||||||
$a = array();
|
$a = array();
|
||||||
foreach ((array)$this->data[$col] as $attr)
|
foreach ((array)$object[$col] as $attr)
|
||||||
$a[] = $attr[$field];
|
$a[] = $attr[$field];
|
||||||
$val = join(' ', $a);
|
$val = join(' ', $a);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$val = is_array($this->data[$col]) ? join(' ', $this->data[$col]) : $this->data[$col];
|
$val = is_array($object[$col]) ? join(' ', $object[$col]) : $object[$col];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strlen($val))
|
if (strlen($val))
|
||||||
$data .= $val . ' ';
|
$data .= $val . ' ';
|
||||||
}
|
}
|
||||||
|
|
||||||
return array_unique(rcube_utils::normalize_string($data, true));
|
$words = rcube_utils::normalize_string($data, true);
|
||||||
|
|
||||||
|
// collect words from recurrence exceptions
|
||||||
|
if (is_array($object['recurrence']) && $object['recurrence']['EXCEPTIONS']) {
|
||||||
|
foreach((array)$object['recurrence']['EXCEPTIONS'] as $exception) {
|
||||||
|
$words = array_merge($words, $this->get_words($exception));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_unique($words);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Add table
Reference in a new issue