Пара фокусов с xPDO
В ходе одного исследования выяснил несколько интересных вещей про xPDO, о которых раньше не задумывался или некогда было проверить.
Решил немедленно поделиться.
В чем же разница?
Внимательный зритель уже догадался в чем трюк — в первом примере мы достаем не объекты, а чистые массивы данных, отсюда скорость и экономия памяти.
Если нужно просто вывести содержимое объектов на экран — то это способ гораздо быстрее.
Обратите внимание, что к полям приписывается имя класса — modResource_ в данном случае. Это можно изменить, используя $q->select('pagetitle');
А если нужно использовать $resource->get(), $resource->set(), $resource->getTVValue() и другие методы объектов — тут второй запрос, помедленнее.
Те же 2 выборки, но с $q->limit(100):
Без объектов:
Память: 17.6 Mb
Время: 0.00191211700439
С объектами:
Память: 23.6 Mb
Время: 0.216797113419
Тут отличие уже не так значительно. Особенно учитывая, что в реальных выводах страниц используется пагинация, и разбивка результатов на 10 — 15 ресурсов за раз.
Те же 2 выборки, но с $q->limit(1); показывают закономерную картину:
Без объектов:
Память: 17.6 Mb
Время: 0.00191211700439
С объектами:
Память: 17.8 Mb
Время: 0.00573897361755
На малой выборке разницы особой нет.
Тут все просто.
Когда вам нужна экономия памяти и высокая производительность для выгрузки товаров, например, или генерации карты солидного сайта — используйте первый метод.
Если же нужны удобства, или размер выборки небольшой — можно не заморачиваться и делать как обычно.
А знаете ли вы, что можно составлять запросы вот так:
А при использовании $modx->newQuery() — будет, он позаботся о правильном составлении запроса согласно модели.
Плюс, newQuery еще дополнительно приведет значения к типам, указанным в модели. То есть, если у вас в модели id указан как int(10), а вы пихаете туда строку — newQuery превратит ее в 0. То есть, приведет к типу int.
Как видно, написание запроса вручную нисколько не экономит время. Но позволяет делать сложные выборки.
Память: 77.8 Mb
Время: 2.14725112915
Ну и самый легкий вариант:
Но разница с ног не сшибает:
Память: 32.7 Mb
Время: 0.0292019844055
Хотя в этом случае не создается здоровенный объект xPDOCriteria.
Видимо, кудесники из MODx что-там хорошо наоптимизировали в своем xPDO.
Вот такие приемы можно использовать в MODx Revolution.
Да, кстати, выборка 3000 ресурсов первым методом проходит за 0.0797028541565 и занимает 64.0.
Вторым — за 6.38202881813 и кушает 194.5 Mb.
Для замера времени использовалась функция microtime(true), для памяти — memory_get_usage(true).
Решил немедленно поделиться.
Выборка 1000 ресурсов
$q = $modx->newQuery('modResource', array('id:>' => 0));
$q->limit(1000);
$q->prepare();
$q->stmt->execute();
$res = $q->stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($res as $v) {
//echo $v['modResource_pagetitle'];
}
Этот код работает за 0.042197227478 и занимает 33.3 Mb памяти.$q = $modx->newQuery('modResource', array('id:>' => 0));
$q->limit(1000);
$res = $modx->getCollection('modResource', $q);
foreach ($res as $v) {
//echo $v->get('pagetitle');
}
А этот уже за 2.15289998055 и занимает 78.3 Mb памяти.В чем же разница?
Внимательный зритель уже догадался в чем трюк — в первом примере мы достаем не объекты, а чистые массивы данных, отсюда скорость и экономия памяти.
Если нужно просто вывести содержимое объектов на экран — то это способ гораздо быстрее.
Обратите внимание, что к полям приписывается имя класса — modResource_ в данном случае. Это можно изменить, используя $q->select('pagetitle');
А если нужно использовать $resource->get(), $resource->set(), $resource->getTVValue() и другие методы объектов — тут второй запрос, помедленнее.
Выборка 100 ресурсов
Те же 2 выборки, но с $q->limit(100):
Без объектов:
Память: 17.6 Mb
Время: 0.00191211700439
С объектами:
Память: 23.6 Mb
Время: 0.216797113419
Тут отличие уже не так значительно. Особенно учитывая, что в реальных выводах страниц используется пагинация, и разбивка результатов на 10 — 15 ресурсов за раз.
Выборка 1 ресурса
Те же 2 выборки, но с $q->limit(1); показывают закономерную картину:
Без объектов:
Память: 17.6 Mb
Время: 0.00191211700439
С объектами:
Память: 17.8 Mb
Время: 0.00573897361755
На малой выборке разницы особой нет.
Выводы
Тут все просто.
Когда вам нужна экономия памяти и высокая производительность для выгрузки товаров, например, или генерации карты солидного сайта — используйте первый метод.
Если же нужны удобства, или размер выборки небольшой — можно не заморачиваться и делать как обычно.
На закуску
А знаете ли вы, что можно составлять запросы вот так:
$sql = "SELECT * FROM {$modx->getTableName('modResource')} WHERE `id` > 0 LIMIT 1000";
$q = new xPDOCriteria($modx, $sql);
$res = $modx->getCollection('modResource', $q);
foreach ($res as $v) {
//echo $v->get('pagetitle');
}
Это тот же второй вариант, с объектами, но SQL запрос составлен в ручную. С одной стороны — вы можете делать так любые выборки, с другой — таккой запрос не универсален, и на Microsoft SQL это работать не будет. А при использовании $modx->newQuery() — будет, он позаботся о правильном составлении запроса согласно модели.
Плюс, newQuery еще дополнительно приведет значения к типам, указанным в модели. То есть, если у вас в модели id указан как int(10), а вы пихаете туда строку — newQuery превратит ее в 0. То есть, приведет к типу int.
Как видно, написание запроса вручную нисколько не экономит время. Но позволяет делать сложные выборки.
Память: 77.8 Mb
Время: 2.14725112915
Ну и самый легкий вариант:
$sql = "SELECT * FROM {$modx->getTableName('modResource')} WHERE `id` > 0 LIMIT 1000";
$q = $modx->prepare($sql);
$q->execute();
$res = $q->fetchAll(PDO::FETCH_ASSOC);
foreach ($res as $v) {
//echo $v['pagetitle'];
}
Этот метод отрабатывает уже на чистом PDO, вообще без xPDO объектов.Но разница с ног не сшибает:
Память: 32.7 Mb
Время: 0.0292019844055
Хотя в этом случае не создается здоровенный объект xPDOCriteria.
Видимо, кудесники из MODx что-там хорошо наоптимизировали в своем xPDO.
Вот такие приемы можно использовать в MODx Revolution.
Да, кстати, выборка 3000 ресурсов первым методом проходит за 0.0797028541565 и занимает 64.0.
Вторым — за 6.38202881813 и кушает 194.5 Mb.
Для замера времени использовалась функция microtime(true), для памяти — memory_get_usage(true).
Комментарии: 39
Это отличная статья, я ей постоянно пользуюсь =)
очень полезная инфа, скоро будет протестена, спасибо
Выборка самым последним способом 20к ресурсов
Ресурсов: 20000
Памяти скушано: 94.25Мб
Время работы сниппета 0.58 сек
Ресурсов: 20000
Памяти скушано: 94.25Мб
Время работы сниппета 0.58 сек
Василий, не могли бы Вы пояснить по поводу строки $q = $modx->newQuery('modResource', array('id:>' => 0));
в частности id:>' => 0, тут имеется ввиду значения в столбце айди больше нуля?
И с modResource не понятно — это системная таблица какая-то или что?
Я новичек не могу пока до конца разобраться в xPDO по этому прошу не удивляться сильно моим вопросам))
в частности id:>' => 0, тут имеется ввиду значения в столбце айди больше нуля?
И с modResource не понятно — это системная таблица какая-то или что?
Я новичек не могу пока до конца разобраться в xPDO по этому прошу не удивляться сильно моим вопросам))
в частности id:>' => 0, тут имеется ввиду значения в столбце айди больше нуля?верно
И с modResource не понятно — это системная таблица какая-то или что?modResource это модель описывающая структуру таблицы. таблица *table_prefix*site_content
Иван, а если я уже сгенерировал модель с помощью скрипта для своих таблиц? тогда нужно что-то типа:
<?php
$modx->addPackage('leleko-cdb', $modx->getOption('core_path').'components/leleko-cdb/model/','leleko_cdb_');
и уже тогда вместо modResource мою модель?
<?php
$modx->addPackage('leleko-cdb', $modx->getOption('core_path').'components/leleko-cdb/model/','leleko_cdb_');
и уже тогда вместо modResource мою модель?
все верно
Василий, я только вникаю в суть работы PDO и xPDO. Прошу подсказать как вывести все данные из своей созданной таблички.
Из предыдущей заметки
$sql = "SELECT * FROM {$modx->getTableName('tablename')} LIMIT 1000";
$q = $modx->prepare($sql);
$q->execute();
$res = $q->fetchAll(PDO::FETCH_ASSOC);
foreach ($res as $v) {
echo $v['createdon'];
}
Ничего не выводится… Я пытался и modx_tablename — тоже результата 0. Из предыдущей заметки
$q = $modx->newQuery('modResource');
$q->where(array('id:>' => 0));
$q->limit('100');
if ($q->prepare() && $q->stmt->execute()) {
$arr = $q->stmt->fetchAll(PDO::FETCH_ASSOC);
}
getStatus('Выборка '.count($arr).' ресурсов через xPDO');
Это вообще кладет сайт с 500й ошибкой
{$modx->getTableName('tablename')}
Ага, в этом была проблема, нафиг getTableName только чистое название бд… А на сколько этот метод правильный?
Ага, в этом была проблема, нафиг getTableName только чистое название бд… А на сколько этот метод правильный?
Если делаешь для себя и переносить никуда не планируешь — то вполне правильный метод.
Всё пытаюсь исключить из выдачи ненужные поля, но никак не могу понять как это сделать, к примеру мне нужны «id» и «parent», а мне выплёвываются в массив все поля всех найденных документов, что и увеличивает работу скрипта.
М.б. кто нибудь подскажет ссылочку на статью, для наводки? или пример.
М.б. кто нибудь подскажет ссылочку на статью, для наводки? или пример.
Ты когда вызываешь toArray(), xPDO довыбирает все невыбранные поля.
Просто получай нужное через get() или используй PDO — там всё прозрачнее.
Просто получай нужное через get() или используй PDO — там всё прозрачнее.
Делаю так
$pubTime = strtotime(date("Y-m-d G:i:s"))-(24*60*60);
$pubTimeEnd = $pubTime+((24*60*60)*2);
$q = $modx->newQuery('modResource', array(
'context_key' => 'web'
,'isfolder' => 0
,'deleted' => 0
,'publishedon:>' => $pubTime
,'publishedon:<=' => $pubTimeEnd
));
$q->select($modx->getSelectColumns('modResource','modResource','',array('id','parent')));
$q->prepare();
$q->stmt->execute();
$result = $q->stmt->fetchAll(PDO::FETCH_ASSOC);
return $modx->toJSON($result);
Получаю массив, но не знаю… верно ли я это сделал, не особо понял зачем $modx->getSelectColumns и для чего указывать 'modResource','modResource', всё это меня смущает и возможно оно работает криво??
сделал так, работает:
$q->select('id,parent');
Не могу найти пример использования OR для запроса в БД, для
$q->where()
Этот код:
$q = $modx->newQuery('modResource');
$q->select(array(
'modResource.*',
));
$q->where(array(
'template' => 1,
'OR:pagetitle:LIKE' => '%home%',
));
даст такой запрос:SELECT modResource.* FROM `modx_site_content` AS `modResource` WHERE ( `modResource`.`template` = 1 OR `modResource`.`pagetitle` LIKE '%home%' )
А этот:$q = $modx->newQuery('modResource');
$q->select(array(
'modResource.*',
));
$q->where(array(
array(
'template' => 1,
'pagetitle:LIKE' => '%home%',
),
array(
'OR:id:=' => 1,
'published' => true,
),
));
даст две группы, объединенные оператором OR:SELECT modResource.* FROM `modx_site_content` AS `modResource` WHERE ( ( `modResource`.`template` = 1 AND `modResource`.`pagetitle` LIKE '%home%' ) OR ( `modResource`.`id` = 1 AND `modResource`.`published` = 1 ) )
Дополнительную информацию можно посмотреть тут: rtfm.modx.com/xpdo/2.x/class-reference/xpdoquery/xpdoquery.where
Спасибо. Странно, что мне он выводит пустой массив, хотя если делать 2 разных запроса, то массивы будут не пусты.
Пишу так:
Пишу так:
array(
'context_key' => $context
,'isfolder' => 0
,'deleted' => 0
,'editedon:>' => $pubTime
,'editedon:<=' => $pubTimeEnd
),
array(
'OR:context_key' => $context
,'isfolder' => 0
,'deleted' => 0
,'publishedon:>' => $pubTime
,'publishedon:<=' => $pubTimeEnd
)
Получаю пустоту [].
Если используете OR:, то надо обязательно добавлять оператор сравнения, в вашем случае
'OR:context_key:=' => $context
вместо'OR:context_key' => $context
OR идёт для второго массива или только для значения $context ????
Помогло, что-то вывел), буду дальше пытать.
спасибо.
Помогло, что-то вывел), буду дальше пытать.
спасибо.
В конце статьи приведен «Ну и самый легкий вариант», очень показался подходящим, сделал на подобии запрос, мне нужно было вывести некоторые поля всех ресурсов + два тв параметра, с одним нет проблем, но два не выходит, цепляется только последний.
Array ( [pagetitle] =>… [introtext] =>… [content] =>… [value] =>… ) т.е. почему-то на псевдонимы не заменяются названия таблиц, и из-за одинаковых полей работает только последний джоин. Как можно этого избежать?
$sql = "
SELECT res.pagetitle, res.introtext, res.content, img.value, video.value
FROM {$modx->getTableName('modResource')} res
LEFT JOIN {$modx->getTableName('modTemplateVarResource')} img
ON res.id=img.contentid AND img.tmplvarid=2
LEFT JOIN {$modx->getTableName('modTemplateVarResource')} video
ON res.id=video.contentid AND video.tmplvarid=6
LIMIT 7000";
$q = $modx->prepare($sql);
$q->execute();
$arr = $q->fetchAll(PDO::FETCH_ASSOC);
print_r($arr[0]);
и выдается следующая структура массива:Array ( [pagetitle] =>… [introtext] =>… [content] =>… [value] =>… ) т.е. почему-то на псевдонимы не заменяются названия таблиц, и из-за одинаковых полей работает только последний джоин. Как можно этого избежать?
Нужно указать псевдонимы:
SELECT res.pagetitle, res.introtext, res.content, img.value as img, video.value as video
Спасибо! Если можно, спрошу заодно, насколько неразумно в проекте с большим количеством данных, и, например, в ответе запросов Ajax, такими методами выводить информацию?
Запрос лучше строить всё-таки через xPDO, а выводить через PDO.
Это можно писать вручную, а можно использовать сниппеты pdoTools и методы самой библиотеки.
Это можно писать вручную, а можно использовать сниппеты pdoTools и методы самой библиотеки.
искал на эту полезную информацию на bezumkin.ru минут 30 -)) почему же поиск не выдает этот комментарий по запросу «select as»?
Я те больше скажу, эта информация даже по Ctrl+F находится только в твоем комментарии.
Офигенно релевантный запрос. Зато обрати внимание, что с твоим комментарием эта тема сразу оказалась на третьем месте и здесь и в яндексе.
P.S. На сайте есть еще bezumkin.ru/index для таких интересных статей. В верхнем меню — первая кнопка.
Офигенно релевантный запрос. Зато обрати внимание, что с твоим комментарием эта тема сразу оказалась на третьем месте и здесь и в яндексе.
P.S. На сайте есть еще bezumkin.ru/index для таких интересных статей. В верхнем меню — первая кнопка.
Индекс это хорошо, но было бы удобнее, если была была возможность добавить тему «в избранное» как на modx.im. Чтоб щелкнул на звездочку в теме — и она в профиле в разделе «Избранное».
modx.im — это движок LiveStreet.
Мой сайт построен целиком на MODX и компоненте Tickets. Здесь пока нет избранного, но когда-нибудь, возможно, и появится.
Мне вообще кажется неправильным строить сайты для модыксеров на базе неMODX решений.
Мой сайт построен целиком на MODX и компоненте Tickets. Здесь пока нет избранного, но когда-нибудь, возможно, и появится.
Мне вообще кажется неправильным строить сайты для модыксеров на базе неMODX решений.
Да, Василий, добавь избранное. Иной раз читаешь, понимаешь — пользительно! А потом силишься вспомнить, йод пьешь,-втираешь, а не вспомнить, что за статья была.
И пока не забыл — отчего-то msOrder параметр tplSuccess забирает, только если прописать в дефолтных настройках. В вызове &tplSuccess=`bla-bla` не понимает.
И пока не забыл — отчего-то msOrder параметр tplSuccess забирает, только если прописать в дефолтных настройках. В вызове &tplSuccess=`bla-bla` не понимает.
Ты готов заказать и оплатить этот новый функционал в Tickets?
На всякий случай — в браузерах дааааавно есть такая функция.
На всякий случай — в браузерах дааааавно есть такая функция.
Браузер может слететь, комп может полететь из окна (хотя я уже давно пользуюсь синхронизацией избранного с аккаунтом гугла для таких случаев).
Но все же, как интересно было бы смотреть чужое избранное, к примеру, что Илья Уткин подчерпнул ценное на сайте, и что занес в избранное? Кстати, статистика статей добавленных в избранное покруче будет рейтинга — новый критерий для «веса» в поиске.
PS: цена вопроса?
Но все же, как интересно было бы смотреть чужое избранное, к примеру, что Илья Уткин подчерпнул ценное на сайте, и что занес в избранное? Кстати, статистика статей добавленных в избранное покруче будет рейтинга — новый критерий для «веса» в поиске.
PS: цена вопроса?
Решил на выходных помочь сообществу и Василию, написал систему избранного для Tickets. Если Василий примет pull reqгest, опишу подробнее уже в отдельной статье.
Как это выглядит, можно посмотреть в минидемонстрации. Сейчас там одно избранное на всех, а в реальности у каждого будет свое избранное.
Посмотрел по диагонали — для начала неплохо. Но нужны еще комментарии и вывод этого избранного самому юзеру, или другим.
Постараюсь доработать на досуге и причесать всё, чтобы было в одном стиле с уже написанным.
Спасибо!
Постараюсь доработать на досуге и причесать всё, чтобы было в одном стиле с уже написанным.
Спасибо!
Вывод пользователю:
[[!pdoPage?
&element=`getTickets`
&sortby=`{"star_createdon":"DESC"}`
&groupby=`star_id HAVING star_id IS NOT NULL AND star_user = [[!+modx.user.id]]`
]]
Илья, чудесно реализовано! Спасибо!
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.