Итак, у вас тысяча ресурсов на сайте и вам нужно сгенерировать карту Google для сайта. С несколькими сотнями ресурсов, вы можете использовать некоторые из дополнений из репозитория (getResources, GoogleSiteMap, pdoSitemap) и при этом не будет никаких проблем с производительностью. Но что будет, если у вас 15 тыс. ресурсов?

Ответ простой, необходимое время для генерации ресурсов быстро увеличивается и вы начинаете быстро подходить к границе памяти и времени выполнения скриптов PHP. На последнем сайте, с которым я работал, пришлось решать именно эту проблему. И на ум мне прошло самое простое решение!

После обдумывания различных процессов оптимизации, я быстро понял, что получение количества ресурсов и последующая их итерация для получения нодов была слишком расточительной - две с половиной минуты это слишком. Поэтому я установил способ, при котором нет нужды проходить повторно через ресурсы и это было достигнуто тем, что мой код генерировал вывод XML нода. Законченный сниппет будет иметь следующий вид:

`<?php ini_set(‘max_execution_time’, 0);

$priority = $modx->getOption(‘priorityId’, $scriptProperties, 1); $changeFreq = $modx->getOption(‘changeFreqId’, $scriptProperties, 1);

$options = array( xPDO::OPT_CACHE_KEY => ‘sitemap’, ); $output = $modx->cacheManager->get(‘sitemap’, $options);

if ($output == null) { $output = ‘ ‘;

$stmt = $modx->query("
    SELECT
        GROUP_CONCAT(
            '<url>',        
            CONCAT('<loc>" . MODX_SITE_URL . "',uri,'</loc>'),
            CONCAT('<lastmod>',FROM_UNIXTIME(editedon, '%Y-%m-%d'),'</lastmod>'),
            IFNULL(
                CONCAT('<priority>',(
                    SELECT value
                    FROM modx_site_tmplvar_contentvalues
                    USE INDEX (tv_cnt)
                    WHERE contentid=id AND tmplvarid={$priorityId}
                ),'</priority>'),''),
            IFNULL(
                CONCAT('<changefreq>',(
                    SELECT value
                    FROM modx_site_tmplvar_contentvalues
                    USE INDEX (tv_cnt)
                    WHERE contentid=id AND tmplvarid={$changeFreqId}
                ), '</changefreq>'), ''),
            '</url>'
            SEPARATOR ''
        ) AS node
    FROM modx_site_content AS s
    WHERE s.deleted = 0 AND s.published = 1 AND s.searchable = 1 AND context_key='web'
    GROUP BY s.id
    ORDER BY s.id ASC
");

if ($stmt) {
    $rows = $stmt->fetchAll(PDO::FETCH_COLUMN);
    $output .= implode('', $rows);
}

$output .= '</urlset>';

$modx->cacheManager->set('sitemap', $output, 86400, $options);

}

return $output;`

Как вы видите, запрос установлен для получения приоритета и частоты изменений из двух ТВ - это схоже на то, как дополнения для генерации карты сайта способны устанавливать пользовательские приоритеты и частоту изменений для ресурса. Если значения не установлены специально для ресурса, он не выводит соответствующую часть ноды и будет использовать значения для Гугла по-умолчанию.

Отмечу в частности USE INDEX(tv_cnt) в подзапросах: он сообщает подзапросам использовать специальный индех привязанный к фильтрации ИД ТВ и ИД контента - без этого запрос занимал бы значительное время.

Также ещё пару вещей нужно отметить:

  • Запрос ограничен ресурсами в web контексте. Если вы решите использовать этот сниппет для другого контекса, помните, что нужно обновить ключ контекста
  • Вывод закеширован на 24 часа. Если нужно другое время хранения в кеше - поменяйте значение 86400 на нужное вам в секундах
  • Можете свободно менять скрипт для ваших нужд и давайте ваши отзывы.

Оригинал статьи Generating a Google Sitemap when you have thousands of resources