Сложная выборка по TV параметрам в getResources

Доброго времени суток, сообщество!

Хочу поделиться одним небольшим трюком, который позволяет устранить одну, на мой взляд нелогичность в getResources

Как известно, для того чтобы отфильтровать товары (или ещё какие оъекты) по TV параметрам следует использовать tvFilters

[[getResources? &tvFilters=`manuf===Sony||manuf===LG`]]
выбрать товары Sony или LG (можно так: manuf===LG||Sony)

[[getResources? &tvFilters=`diag===65||diag===85`]]
выбрать товары с диагональю 65 или 85 дюймов

[[getResources? &tvFilters=`manuf===Sony,diag===65`]]
выбрать товары Sony c диагональю 65 дюйм

Если нам нужно отобрать товары Sony с диагональю 65 или 85 дюймов придётся использовать такую конструкцию
[[getResources? &tvFilters=`manuf===Sony,diag===65||manuf===Sony,diag===85`]]

А теперь представим, что нам нужно отобрать товары Sony с диагональю 65 или 85 дюймов черного или серого цвета
[[getResources? &tvFilters=`manuf===Sony,diag===65,color===black||manuf===Sony,diag===65,color===gray||manuf===Sony,diag===85,color===black||manuf===Sony,diag===85,color===gray`]]

Если мы добавим в условие ещё один цвет или размер диагонали начнётся жесть

Дело в том, что при разборе параметра &tvFilters, getResources делит запрос на операторы или (||), а потом на операторы и (,)

На практике же, гораздо чаще требуется, чтобы порядок был обратным

[[getResources? &tvFilters=`manuf===Sony,diag===65||85,color===gray||black`]]
согласитесь такая конструкция куда проще, чем та, которая выше и добавление ещё одного условия не составит труда

Чуть-чуть подправим getResources

120 строка
Меняем
$tvFilters = !empty($tvFilters) ? explode($tvFiltersOrDelimiter, $tvFilters) : array();
На
$tvFilters = !empty($tvFilters) ? explode($tvFiltersAndDelimiter, $tvFilters) : array();

256 строка
Меняем
$filters = explode($tvFiltersAndDelimiter, $tvFilter);
На
$filters = explode($tvFiltersOrDelimiter, $tvFilter);

312 Строка
Меняем
if (!empty($conditions)) {
        $firstGroup = true;
        foreach ($conditions as $cGroup => $c) {
            if (is_array($c)) {
                $first = true;
                foreach ($c as $cond) {
                    if ($first && !$firstGroup) {
                        $criteria->condition($criteria->query['where'][0][1], $cond, xPDOQuery::SQL_OR, null, $cGroup);
                    } else {
                        $criteria->condition($criteria->query['where'][0][1], $cond, xPDOQuery::SQL_AND, null, $cGroup);
                    }
                    $first = false;
                }
            } else {
                $criteria->condition($criteria->query['where'][0][1], $c, $firstGroup ? xPDOQuery::SQL_AND : xPDOQuery::SQL_OR, null, $cGroup);
            }
            $firstGroup = false;
        }
    }
На
if (!empty($conditions)) {
        $firstGroup = true;
        foreach ($conditions as $cGroup => $c) {
            if (is_array($c)) {
                $first = true;
                foreach ($c as $cond) {
                    if ($first && !$firstGroup) {
                        $criteria->condition($criteria->query['where'][0][1], $cond, xPDOQuery::SQL_AND, null, $cGroup);
                    } else {
                        $criteria->condition($criteria->query['where'][0][1], $cond, xPDOQuery::SQL_OR, null, $cGroup);
                    }
                    $first = false;
                }
            } else {
                $criteria->condition($criteria->query['where'][0][1], $c, $firstGroup ? xPDOQuery::SQL_OR : xPDOQuery::SQL_AND, null, $cGroup);
            }
            $firstGroup = false;
        }
    }

Таким нехитрым способом гораздо проще реализовывать сложные фильтры, наподобие таких, как реализованы на сайтах Евросети и Эльдорадо
bratskiy
06 августа 2015, 11:33
modx.pro
4
11 376
+1

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

Василий Наумкин
06 августа 2015, 14:50
0
Таким нехитрым способом гораздо проще реализовывать сложные фильтры, наподобие таких, как реализованы на сайтах Евросети и Эльдорадо
Через getResources?

За сколько у тебя грузится страница с результатами такой фильтрации?
    bratskiy
    06 августа 2015, 15:42
    0
    Аjax ответ приходит за 0.3 — 1 сек.

    Много?

    Заметил, что чем мягче условия поиска, тем дольше приходит ответ. Т.е если сбросить все фильтры, то ответ приходит за секунду. Если составить условия таким образом, что в результате на вывод будет всего пара записей, то ответ сервера 0.3 сек

    В базе сейчас примерно 2500 записей
      Alexander V
      06 августа 2015, 15:53
      0
      Василий наверное хотел вам порекомендовать pdoResouces
        Василий Наумкин
        06 августа 2015, 16:26
        0
        Если устраивает — то вопросов больше нет.

        Мне, в своё время, скорость работы getResources показалась неприемлимой.
          bratskiy
          07 августа 2015, 05:11
          0
          Василий, подскажите, есть ли в pdoResources возможность решения подобной задачи без правки кода сниппета?

          Ознакомился с документацией pdoTools, но не нашёл там ответ

          Конкретно меня интересует возможность смены приоритета работы операторов И и ИЛИ в tvFilters

            Василий Наумкин
            07 августа 2015, 05:24
            4
            +3
            В pdoResources задача решается несколько иначе:
            [[!pdoResources?
            	&includeTVs=`manuf,diag,color`
            	&where=`{
            		"manuf":"Sony",
            		"diag:IN":[65,85],
            		"color:IN":["black","gray"]
            	}`
            ]]
            В принципе, там есть и &tvFilters, но только как временная замена при миграции c getResources. Пользоваться этим параметром я никому не советую — &where гораздо гибче, лучше и удобнее.

            Вот здесь кратко написано, а вот здесь значительно подробнее.
              bratskiy
              07 августа 2015, 05:34
              0
              Спасибо!

              Буду пробовать.

              Ещё маленький вопросик можно?

              Если в OR нам нужен LIKE?
              "color:IN":["%black%","%gray%"]
              — мне почему-то кажется, что такая констукция работать не будет

              Такое нужно например когда мы используем multiselect TV

              Как быть тогда?
                Василий Наумкин
                07 августа 2015, 06:19
                1
                0
                &where=`{
                	"color:LIKE":"%black%",
                	"OR:color:LIKE":%gray%"
                }`
                И вот еще один OR:color:LIKE тут добавить не выйдёт — потому что одинаковых ключей в массиве быть не может.

                Но можно указывать строки:
                &where=`["
                	color LIKE '%black%' OR color LIKE '%gray%' OR color LIKE '%white%'
                "]`
                Подробнее про синтаксис where можно прочитать в официальной документации.

                И очень тебя прошу — почини свой enter, ненужные пустые строки в комментарии выглядят неопрятно.
                  bratskiy
                  07 августа 2015, 09:55
                  0
                  pdoResources ни как не хочет подхватывать строку $where. Использую вариант с квадратными скобками
                  $output .= $modx->runSnippet('pdoResources',array(
                     'includeTVs' => $includeTVs,
                     'where' => $where,
                  ));
                  В $where лежит грамотный кусок SQL, сформированный на основе post запроса, обрамлённый квадратными скобками. Например такой
                  "[ TVfo LIKE '%Южный%' AND (TVclient_activity LIKE '%Физ.охрана%' OR TVclient_activity LIKE '%Пультовая охрана%') ]"
                  На выходе же where игнорируется и я получаю все записи базы. Спору нет, работает быстрее, если сравнивать с getResources c пустым фильтром, но не фильтруется… В логе есть sql запрос, в нём также присутствие $where я не обнаружил
                    Василий Наумкин
                    07 августа 2015, 10:05
                    0
                    Может там и лежит грамотный кусок SQL, только написан он неграмотно. Посмотри на скобки и кавычки в моём примере — там одномерный JSON массив со строкой внутри.

                    У тебя — просто строка со скобочками, которая вовсе не раскодируется как JSON. Верный код:
                    $where = "TVfo LIKE '%Южный%' AND (TVclient_activity LIKE '%Физ.охрана%' OR TVclient_activity LIKE '%Пультовая охрана%'";
                    
                    $output .= $modx->runSnippet('pdoResources',array(
                       'includeTVs' => $includeTVs,
                       'where' => $modx->toJSON(array($where)),
                    ));

                    И если я ничего не путаю, то при вызове из PHP можно вообще не париться с JSON, а указывать просто массив вот так:
                    $output .= $modx->runSnippet('pdoResources',array(
                       'includeTVs' => $includeTVs,
                       'where' => array($where),
                    ));
                    Но не факт — нужно проверять.
                      bratskiy
                      07 августа 2015, 11:28
                      0
                      'where' => $modx->toJSON(array($where)),
                      Это способ работает. Но в целом не удалось получить то что надо.

                      Ситуация у меня следующая. С помощью getResources, описанным в посте способом, я получаю список объектов. Он выводится у меня постранично. На против каждого объекта есть чекбокс. Ещё есть форма-фильтр. Мне нужно отметить чекбоксы всех записей, которые соответствуют фильтру, а их на данный момент может быть до 2500.

                      Изначально я сделал всё на getResources. Всё работает. Теперь же решил ускорить работу и для выделения чекбоксов решил использовать ajax с pdoResources.

                      Теперь я могу визуально наблюдать разницу между getResources и pdoResources. Есть ряд записей, которые pdoResources мне не возвращает, хотя они соответствуют условию фильтра, и тот же getResources мне их отдаёт

                        Василий Наумкин
                        07 августа 2015, 12:16
                        1
                        0
                        хотя они соответствуют условию фильтра,
                        Если речь о значении ТВ «по умолчанию» — то их нет в БД физически, так что pdoResources их не выберет.

                        А в остальном должно всё работать.
                        bratskiy
                        07 августа 2015, 12:50
                        0
                        Спасибо!
                        Проверил, действительно у неотмеченных записей проставлены значения по умолчанию. Буду искать способ как автоматом проставить все записи по умолчанию
    Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
    13