Проблемы с фильтрацией ресурсов (больше 2000шт.)

Стоит задача отфильтровать ресурсы: вывести те ресурсы, у которых больше 20 просмотров (HitsPage), комментов больше 0 (Tickets), и которые опубликованы больше месяца назад.

Написал сниппет, но при обработке большого количества ресурсов страница с вызовом сниппета не загружается. Как оптимизировать код?

$mounth = 2629743;
$date = strtotime('now')-$mounth;

$where = array(
	'parent' => 19,
	'publishedon:<' => $date
);

$resources = $modx->getCollection('modResource', $where);
foreach ($resources as $k => $res) {
    //получаем id
    $id = $res->get('id');

    //получаем количество комментов
    $q = $modx->newQuery('modResource', $id);
    $q->leftJoin('TicketThread','TicketThread', "`TicketThread`.`name` = 'resource-{$id}'");
    $q->leftJoin('TicketComment','TicketComment', "`TicketThread`.`id` = `TicketComment`.`thread`");
    $q->select('COUNT(`TicketComment`.`id`) as `comments`');
    
    $count = 0;
    if ($q->prepare() && $q->stmt->execute()) {
    	$count = (integer) $q->stmt->fetch(PDO::FETCH_COLUMN);
    }

    //получаем дату, количество просмотров и заголовок
    $date = $res->get('publishedon');
    $hits = $res->getTVValue(3);
    $titles = $res->get('pagetitle');
    
    //если опубликован больше месяца назад, просмотров больше 20 и комментов больше 0, то выводим
    if ($hits >= 20 and $count > 0) {
        $output .= $date. " | П: ". $hits. " | К: ". $count. "</br>". $titles.  "<hr>";
    }
}
return $output;
Василий Stepanov
03 июля 2017, 09:39
modx.pro
851
0
Поблагодарить автора Отправить деньги

Комментарии: 6

Алексей Ерохин
03 июля 2017, 13:56
+2
Убрать getCollection, использовать getIterator
modxclub.ru/topics/xpdogetiterator-vmesto-xpdogetcollection.html
В критерию можно добавить выбор только нужных колонок (publishedon, pagetitle, id)
Можно сразу присоединить и таблицу с TV hits по contentid и tmplvarid = 3
    Олег
    03 июля 2017, 15:13
    0
    Я не уверен, конечно, но лучше использовать newQuery. С getIterator почти нет опыта, но читал, что меньше всего нагрузка именно при newQuery.
      Алексей Ерохин
      03 июля 2017, 15:26
      0
      Вы путаете, newQuery — это создание запроса xPDOQuery, а getCollection и getIterator получение результата на основании параметров (это может быть и обычный массив и xPDOQuery)

      В случае getCollection и getIterator вы получаете объекты xpdo, из которых вы можете получить подготовленные данные через метод get.

      if ($q->prepare() && $q->stmt->execute()) {
          	$count = (integer) $q->stmt->fetch(PDO::FETCH_COLUMN);
          }
      А в данном случае вы получаете результат из базы данных
        Олег
        03 июля 2017, 15:58
        +1
        Вы путаете, newQuery — это создание запроса xPDOQuery, а getCollection и getIterator получение результата на основании параметров (это может быть и обычный массив и xPDOQuery)

        В случае getCollection и getIterator вы получаете объекты xpdo, из которых вы можете получить подготовленные данные через метод get.
        Как я понял — объекты не нужно получать, ведь там просто вывод результатов.

        Я вчитался в код и офигел… Сначала выбираются минимум 2000 страниц, после чего для каждой из них идет запрос к базе.

        Да, тут вообще вопрос в другом — как избавится от такого количества запросов.
    Олег
    03 июля 2017, 16:43
    +2
    Автор, окстись!
    Очень плохой код.

    Как это сейчас работает: сначала через getCollection забираются 2000+ ресурсов, которые опубликованы. Далее мы получаем их id и для каждого делаем запрос к бд!
    Это очень плохая идея.
    Как минимум те же id можно получить через newQuery — так будет быстрее.
    А во вторых желательно сделать 1 запрос к бд, а не 2001.

    Почитай мануалы.

    А вообще я бы примерно так сделал (вообще не гарантирую, что будет работать, пишу на глазок).
    $mounth = 2629743;
    $date = strtotime('now')-$mounth;
    $where = array(
    	'parent' => 19,
    	'publishedon:<' => $date,
    	'comments:>' => 20
    	//тут надо добавить что-то про hits, но для этого сделать join таблицы modTemplateVar
    );
    
    $q = $modx->newQuery('modResource', $id);
    $q->leftJoin('TicketThread','TicketThread', "`TicketThread`.`name` = 'resource-{$id}'");
    $q->leftJoin('TicketComment','TicketComment', "`TicketThread`.`id` = `TicketComment`.`thread`");
    $q->select('COUNT(`TicketComment`.`id`) as `comments`');
    $q->where($where);
    
    if ($q->prepare() && $q->stmt->execute()) {
        	$data = $q->stmt->fetch(PDO::FETCH_COLUMN);
    	foreach ($data as $res) {
    		//тут код вывода
    	}
    }
    Вариант такого типа будет работать в сотни раз быстрее
      Василий Stepanov
      05 июля 2017, 16:27
      0
      Переделал. Не уверен, что все правильно сделал, но работает в разы быстрее.
      Условия выборки немного изменились: вывести статьи, у которых просмотров меньше 20, количество комментов = 0, и, которые опубликованы больше трех недель назад.
      $week = 604800;
      $cond_date = strtotime('now')-$week*3;
      
      $q_test = " SELECT 
                      modx_site_content.id, 
                      modx_site_content.properties, 
                      modx_site_content.publishedon,
                      modx_tickets_threads.comments 
                  FROM 
                      `modx_site_content`, 
                      `modx_tickets_threads` 
                  WHERE 
                      modx_site_content.id = modx_tickets_threads.resource 
                      AND 
                      modx_site_content.parent = '19'";
      
      $result = $modx->query($q_test);
      $rows = $result->fetch();
      
      do {
          $id = $rows['id'];
          $comments = $rows['comments'];
          $date = $rows['publishedon'];
          $visits = preg_replace("/[^0-9]/", '', $rows['properties']);
          
          if ($comments == 0 and $visits <= 20 and $date < $cond_date) {
              echo "<pre>";
              echo "id = ". $id. "<br />";
              echo "comments = ". $comments. "<br />";
              echo "pubdate = ". date("Y-m-d",$date). "<br />";
              echo "visits = ". $visits;
              echo "</pre>";
          }
      }
      while ($rows = $result->fetch());
      Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
      6