Генерация Google Sitemap для тысяч ресурсов

Итак, у вас тысяча ресурсов на сайте и вам нужно сгенерировать карту 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 = '<?xml version="1.0" encoding="UTF-8"?>
    <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
    ';

    $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