Refactor access to storage backend to avoid memory limit errors (#2828):
1. query backend and read contact names for sorting 2. sort index according to UI settings and fetched names 3. select the subset for the current page 4. fetch contacts for current page
This commit is contained in:
parent
69b7803f25
commit
ea131a84e6
1 changed files with 47 additions and 69 deletions
|
@ -89,6 +89,8 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
|
||||
private $gid;
|
||||
private $storagefolder;
|
||||
private $dataset;
|
||||
private $sortindex;
|
||||
private $contacts;
|
||||
private $distlists;
|
||||
private $groupmembers;
|
||||
|
@ -266,10 +268,11 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
*
|
||||
* @param array List of cols to show
|
||||
* @param int Only return this number of records, use negative values for tail
|
||||
* @param boolean True to skip the count query (select only)
|
||||
*
|
||||
* @return array Indexed list of contact records, each a hash array
|
||||
*/
|
||||
public function list_records($cols = null, $subset = 0)
|
||||
public function list_records($cols = null, $subset = 0, $nocount = false)
|
||||
{
|
||||
$this->result = new rcube_result_set(0, ($this->list_page-1) * $this->page_size);
|
||||
|
||||
|
@ -277,8 +280,10 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
if ($this->gid) {
|
||||
$this->_fetch_groups();
|
||||
|
||||
$this->contacts = array();
|
||||
$uids = array();
|
||||
$this->sortindex = array();
|
||||
$this->contacts = array();
|
||||
$local_sortindex = array();
|
||||
$uids = array();
|
||||
|
||||
// get members with email specified
|
||||
foreach ((array)$this->distlists[$this->gid]['member'] as $member) {
|
||||
|
@ -287,23 +292,20 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!empty($member['email'])) {
|
||||
$this->contacts[$member['ID']] = $member;
|
||||
}
|
||||
if (!empty($member['uid'])) {
|
||||
$uids[] = $member['uid'];
|
||||
}
|
||||
else if (!empty($member['email'])) {
|
||||
$this->contacts[$member['ID']] = $member;
|
||||
$local_sortindex[$member['ID']] = $this->_sort_string($member);
|
||||
}
|
||||
}
|
||||
|
||||
// get members by UID
|
||||
if (!empty($uids)) {
|
||||
foreach ((array)$this->storagefolder->select(array(array('uid', '=', $uids))) as $record) {
|
||||
$member = $this->_to_rcube_contact($record);
|
||||
$this->contacts[$member['ID']] = $member;
|
||||
}
|
||||
$this->_fetch_contacts(array(array('uid', '=', $uids)));
|
||||
$this->sortindex = array_merge($this->sortindex, $local_sortindex);
|
||||
}
|
||||
|
||||
$ids = array_keys($this->contacts);
|
||||
}
|
||||
else if (is_array($this->filter['ids'])) {
|
||||
$ids = $this->filter['ids'];
|
||||
|
@ -311,34 +313,25 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
$uids = array_map(array($this, 'id2uid'), $this->filter['ids']);
|
||||
$this->_fetch_contacts(array(array('uid', '=', $uids)));
|
||||
}
|
||||
else {
|
||||
$this->contacts = array();
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->_fetch_contacts();
|
||||
$ids = array_keys($this->contacts);
|
||||
}
|
||||
|
||||
// sort data arrays according to desired list sorting
|
||||
if ($count = count($ids)) {
|
||||
uasort($this->contacts, array($this, '_sort_contacts_comp'));
|
||||
// get sorted IDs
|
||||
if ($count != count($this->contacts))
|
||||
$ids = array_values(array_intersect(array_keys($this->contacts), $ids));
|
||||
else
|
||||
$ids = array_keys($this->contacts);
|
||||
|
||||
$this->result->count = count($ids);
|
||||
}
|
||||
// sort results (index only)
|
||||
asort($this->sortindex, SORT_LOCALE_STRING);
|
||||
$ids = array_keys($this->sortindex);
|
||||
|
||||
// fill contact data into the current result set
|
||||
$this->result->count = count($ids);
|
||||
$start_row = $subset < 0 ? $this->result->first + $this->page_size + $subset : $this->result->first;
|
||||
$last_row = min($subset != 0 ? $start_row + abs($subset) : $this->result->first + $this->page_size, $count);
|
||||
$last_row = min($subset != 0 ? $start_row + abs($subset) : $this->result->first + $this->page_size, $this->result->count);
|
||||
|
||||
for ($i = $start_row; $i < $last_row; $i++) {
|
||||
if ($id = $ids[$i])
|
||||
$this->result->add($this->contacts[$id]);
|
||||
if (array_key_exists($i, $ids)) {
|
||||
$idx = $ids[$i];
|
||||
$this->result->add($this->contacts[$idx] ?: $this->_to_rcube_contact($this->dataset[$idx]));
|
||||
}
|
||||
}
|
||||
|
||||
return $this->result;
|
||||
|
@ -411,8 +404,11 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
// save searching conditions
|
||||
$this->filter = array('fields' => $fields, 'value' => $value, 'mode' => $mode, 'ids' => array());
|
||||
|
||||
// search be iterating over all records in memory
|
||||
foreach ($this->contacts as $id => $contact) {
|
||||
// search by iterating over all records in dataset
|
||||
foreach ($this->dataset as $i => $record) {
|
||||
$contact = $this->_to_rcube_contact($record);
|
||||
$id = $contact['ID'];
|
||||
|
||||
// check if current contact has required values, otherwise skip it
|
||||
if ($required) {
|
||||
foreach ($required as $f) {
|
||||
|
@ -593,10 +589,7 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
true, false);
|
||||
}
|
||||
else {
|
||||
$contact = $this->_to_rcube_contact($object);
|
||||
$id = $contact['ID'];
|
||||
$this->contacts[$id] = $contact;
|
||||
$insert_id = $id;
|
||||
$insert_id = $this->uid2id($object['uid']);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -627,7 +620,6 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
true, false);
|
||||
}
|
||||
else {
|
||||
$this->contacts[$id] = $this->_to_rcube_contact($object);
|
||||
$updated = true;
|
||||
|
||||
// TODO: update data in groups this contact is member of
|
||||
|
@ -674,7 +666,7 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
}
|
||||
|
||||
// clear internal cache
|
||||
unset($this->contacts[$id], $this->groupmembers[$id]);
|
||||
unset($this->groupmembers[$id]);
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
@ -723,6 +715,8 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
{
|
||||
if ($this->storagefolder->delete_all()) {
|
||||
$this->contacts = array();
|
||||
$this->sortindex = array();
|
||||
$this->dataset = null;
|
||||
$this->result = null;
|
||||
}
|
||||
}
|
||||
|
@ -979,57 +973,41 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
*/
|
||||
private function _fetch_contacts($query = array())
|
||||
{
|
||||
if (!isset($this->contacts)) {
|
||||
$this->contacts = array();
|
||||
foreach ((array)$this->storagefolder->select($query) as $record) {
|
||||
if (!isset($this->dataset) || !empty($query)) {
|
||||
$this->sortindex = array();
|
||||
$this->dataset = $this->storagefolder->select($query);
|
||||
foreach ($this->dataset as $idx => $record) {
|
||||
$contact = $this->_to_rcube_contact($record);
|
||||
$id = $contact['ID'];
|
||||
$this->contacts[$id] = $contact;
|
||||
$this->sortindex[$idx] = $this->_sort_string($contact);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function for sorting contacts
|
||||
* Extract a string for sorting from the given contact record
|
||||
*/
|
||||
private function _sort_contacts_comp($a, $b)
|
||||
private function _sort_string($rec)
|
||||
{
|
||||
$a_value = $b_value = '';
|
||||
$str = '';
|
||||
|
||||
switch ($this->sort_col) {
|
||||
case 'name':
|
||||
$a_value = $a['name'] . $a['prefix'];
|
||||
$b_value = $b['name'] . $b['prefix'];
|
||||
$str = $rec['name'] . $rec['prefix'];
|
||||
case 'firstname':
|
||||
$a_value .= $a['firstname'] . $a['middlename'] . $a['surname'];
|
||||
$b_value .= $b['firstname'] . $b['middlename'] . $b['surname'];
|
||||
$str .= $rec['firstname'] . $rec['middlename'] . $rec['surname'];
|
||||
break;
|
||||
|
||||
case 'surname':
|
||||
$a_value = $a['surname'] . $a['firstname'] . $a['middlename'];
|
||||
$b_value = $b['surname'] . $b['firstname'] . $b['middlename'];
|
||||
$str = $rec['surname'] . $rec['firstname'] . $rec['middlename'];
|
||||
break;
|
||||
|
||||
default:
|
||||
$a_value = $a[$this->sort_col];
|
||||
$b_value = $b[$this->sort_col];
|
||||
$str = $rec[$this->sort_col];
|
||||
break;
|
||||
}
|
||||
|
||||
$a_value .= is_array($a['email']) ? $a['email'][0] : $a['email'];
|
||||
$b_value .= is_array($b['email']) ? $b['email'][0] : $b['email'];
|
||||
|
||||
$a_value = mb_strtolower($a_value);
|
||||
$b_value = mb_strtolower($b_value);
|
||||
|
||||
// return strcasecmp($a_value, $b_value);
|
||||
// make sorting unicode-safe and locale-dependent
|
||||
if ($a_value == $b_value)
|
||||
return 0;
|
||||
|
||||
$arr = array($a_value, $b_value);
|
||||
sort($arr, SORT_LOCALE_STRING);
|
||||
return $a_value == $arr[0] ? -1 : 1;
|
||||
$str .= is_array($rec['email']) ? $rec['email'][0] : $rec['email'];
|
||||
return mb_strtolower($str);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1039,7 +1017,7 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
{
|
||||
if (!isset($this->distlists)) {
|
||||
$this->distlists = $this->groupmembers = array();
|
||||
foreach ((array)$this->storagefolder->get_objects('distribution-list') as $record) {
|
||||
foreach ($this->storagefolder->get_objects('distribution-list') as $record) {
|
||||
$record['ID'] = $this->uid2id($record['uid']);
|
||||
foreach ((array)$record['member'] as $i => $member) {
|
||||
$mid = $this->uid2id($member['uid'] ? $member['uid'] : 'mailto:' . $member['email']);
|
||||
|
|
Loading…
Add table
Reference in a new issue