Использование условий OR в meta_query для аргумента query_posts

24 июн. 2013 г., 18:36:37
Просмотры: 39.7K
Голосов: 9

Я искал везде, но не смог найти ничего подходящего.

Я пытаюсь отфильтровать некоторые результаты, и когда я создаю массив args и передаю его в query_posts, я заметил, что сгенерированный SQL использует AND вместо OR для значения 'class_1_days'. Я хотел бы изменить это на OR. Как это сделать? (Я использую LIKE, так как данные сериализованы, иначе я бы попробовал "IN" и передал массив. Какой правильный способ это сделать?)

$args = array(
            'taxonomy' => 'courses', 
            'term' => 'drawing', 
            'post_type' => 'school', 
            'paged' => $paged,
            'meta_query' =>
                #$checkbox_array,
                array(
                    array(
                        'key' => 'instructor',
                        'value' => '1128',
                        'compare' => 'LIKE',
                    ),
                    array(
                        'key' => 'class_1_days',
                        'value' => 'Friday',
                        'compare' => 'LIKE',
                    ),
                    array(
                        'key' => 'class_1_days',
                        'value' => 'Saturday',
                        'compare' => 'LIKE',
                    ),
                )

        );

Это соответствующий SQL-запрос, который генерируется, где я заметил AND

[138] => Array
    (
        [0] => SELECT SQL_CALC_FOUND_ROWS  wp_posts.ID FROM wp_posts  INNER JOIN     wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id) INNER JOIN     wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id)
INNER JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id)
INNER JOIN wp_postmeta AS mt2 ON (wp_posts.ID = mt2.post_id) WHERE 1=1  AND (     wp_term_relationships.term_taxonomy_id IN (18) ) AND wp_posts.post_type = 'school' AND     (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private') AND (     (wp_postmeta.meta_key = 'instructor' AND CAST(wp_postmeta.meta_value AS CHAR) LIKE '%1128%')
AND  (mt1.meta_key = 'class_1_days' AND CAST(mt1.meta_value AS CHAR) LIKE '%Friday%')
AND  (mt2.meta_key = 'class_1_days' AND CAST(mt2.meta_value AS CHAR) LIKE '%Saturday%') ) GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC LIMIT 0, 5
0
Все ответы на вопрос 2
9
14

Используйте 'relation' => 'OR' как в примере из Codex ниже:

$args = array(
    'post_type' => 'product',
    'meta_query' => array(
        'relation' => 'OR', /* <-- здесь */
        array(
            'key' => 'color',
            'value' => 'blue',
            'compare' => 'NOT LIKE'
        ),
        array(
            'key' => 'price',
            'value' => array( 20, 100 ),
            'type' => 'numeric',
            'compare' => 'BETWEEN'
        )
    )
);
$query = new WP_Query( $args );

Это обеспечит применение оператора OR ко всем блокам meta_query. Если вам нужно что-то вроде WHERE "instructor" = 1128 AND (class_1_days = "value1" OR class_1_days = "value2"), то подойдет что-то подобное, при условии что ваши данные не сериализованы.

$args = array(
        'taxonomy' => 'courses', 
        'term' => 'drawing', 
        'post_type' => 'school', 
        'paged' => $paged,
        'meta_query' =>
            array(
                array(
                    'key' => 'instructor',
                    'value' => '1128',
                    'compare' => 'LIKE',
                ),
                array(
                    'key' => 'class_1_days',
                    'value' => array('Friday','Saturday')
                    'compare' => 'IN',
                )
            )

    );

Учитывая, что у вас сериализованные данные, это будет сложно реализовать. Вы могли бы написать фильтр для posts_where, но я ожидаю, что результат будет неэффективным и подверженным ошибкам.

Правильный подход для подобных случаев, когда вам нужно искать/фильтровать/сортировать по определенным данным, на мой взгляд, заключается в том, чтобы изначально не хранить данные в виде сериализованного массива. Это просто неподходящий подход, если вам нужны подобные запросы.

Если бы я взялся за этот проект, я бы перебрал данные, которые нужно запрашивать таким образом, и пересохранил каждую часть как отдельную пару ключ/значение. Части, которые не требуют запросов, могут оставаться сериализованными. Нет (хорошего) способа сделать это в MySQL с помощью функций MySQL, ни как часть запроса, который вы пытаетесь выполнить, ни как отдельный запрос. Я видел попытки создания функции MySQL для "десериализации", но не вижу в этом смысла. Пересохранение данных — правильный путь.

Должен быть довольно простой запрос для получения всех данных class_1_days. Затем переберите их и сохраните части, по которым нужно выполнять запросы, как независимые ключи, а остальное верните обратно как сериализованные данные.

24 июн. 2013 г. 18:50:37
Комментарии

Спасибо большое за ваш ответ. Можно ли сделать условие AND или OR? Я пытаюсь сохранить условие по преподавателю. Ваш пример делает все условия через OR. Мне нужно, чтобы только условие class_1_days было через OR. По аналогии с тем, как если бы я использовал 'value' => array(bla,bla,bla), 'compare' => IN

По сути, чтобы было: Instructor = 'bla' AND value IN (bla,bla,bla)

Devario Johnson Devario Johnson
24 июн. 2013 г. 19:06:52

Спасибо @S_ha_dam. Есть ли способ десериализовать базу данных? Я унаследовал этот проект, поэтому не знаю, какие последствия это может иметь. Видел ссылку на приложение, которое делает именно это, но ссылка оказалась битой.

Devario Johnson Devario Johnson
24 июн. 2013 г. 19:09:55

Спасибо, но просто пересохранение не поможет — это рабочая среда, где данные постоянно обновляются, и class_1_data — это лишь часть уравнения. Есть множество других — class_0_data, class_2_data и так далее, которые добавляются пользователями. Думаю, лучший подход — откатиться и найти способ переустановить модуль, чтобы данные не сохранялись в сериализованном виде (если я разберусь как) и двигаться дальше X-( ... Я не упоминал другие классы, потому что думал, что смогу разобраться с этим самостоятельно

Devario Johnson Devario Johnson
24 июн. 2013 г. 19:23:08

Вам определенно придется рефакторить функцию "save", но также нужно будет пересохранить существующие данные, иначе получится странная смесь сериализованных и несериализованных данных. Сначала добейтесь работоспособности кода на тестовом сервере, а затем переведите рабочий сервер в режим обслуживания на двадцать минут в период низкой нагрузки.

s_ha_dum s_ha_dum
24 июн. 2013 г. 19:27:37

О да, конечно, мне придется изменить текущие данные!

Это отстой. Я думал, что это будет простое исправление, блин!

Devario Johnson Devario Johnson
24 июн. 2013 г. 19:30:01

Погодите... а что если так... есть ли способ добавить ПОСЛЕ этого фильтра еще один фильтр по преподавателю?

Или даже получить обратно прямой php-массив, который использует WordPress, и отредактировать его?

Devario Johnson Devario Johnson
24 июн. 2013 г. 19:30:59

Я не уверен, что полностью понял последний вопрос, но с помощью фильтров вы можете изменить запрос практически как угодно. Однако, просто "прицепив" что-то, вы вряд ли получите эффективный запрос.

s_ha_dum s_ha_dum
24 июн. 2013 г. 19:35:20

Ну, например, в PHP у вас может быть массив вида classes[1][bla], classes[2][bla] и так далее. Если бы я знал, как называется этот массив, я мог бы просто выполнить поиск по массиву и регулярные выражения на PHP, чтобы отфильтровать результаты самостоятельно после получения нужных данных из OR-оператора, который вы предложили ранее.

Devario Johnson Devario Johnson
24 июн. 2013 г. 19:39:24

Да, это возможно, но вам придется сначала получить все данные, а потом их сортировать.

s_ha_dum s_ha_dum
24 июн. 2013 г. 19:40:22
Показать остальные 4 комментариев
1

Согласно WordPress Codex, вложенные массивы могут использоваться для построения сложных запросов. В вашем примере вам понадобится что-то вроде этого:

$args = array(
    'taxonomy' => 'courses', 
    'term' => 'drawing', 
    'post_type' => 'school', 
    'paged' => $paged,
    'meta_query' =>
        array(
           'relation' => 'AND', // отношение по умолчанию
            array(
                'key' => 'instructor',
                'value' => '1128',
                'compare' => 'LIKE',
            ),
            array(
                'relation' => 'OR',
                array(
                    'key' => 'class_1_days',
                    'value' => 'Friday',
                    'compare' => 'LIKE',
                ),
                array(
                    'key' => 'class_1_days',
                    'value' => 'Saturday',
                    'compare' => 'LIKE',
                ),
            )
        )
); 
27 июл. 2020 г. 00:26:55
Комментарии

Пришлось покопаться, чтобы найти это. У меня сложный запрос, связанный с этим. Твой ответ получил столько голосов, что я думаю задать новый вопрос на основе этого. Честно говоря, мне очень интересно посмотреть, как сообщество сейчас решает подобные проблемы.

Nathan Powell Nathan Powell
31 янв. 2025 г. 03:11:52