meta_query с мета-значениями в виде сериализованных массивов

9 мая 2011 г., 05:06:35
Просмотры: 81.7K
Голосов: 51

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

<p> 
    <label for="latitude">Широта:</label><br /> 
    <input type="text" id="latitude" name="coordinates[latitude]" class="full-width" value="" /> 
</p> 
<p>     
    <label for="longitude">Долгота:</label><br /> 
    <input type="text" id="longitude" name="coordinates[longitude]" class="full-width" value="" /> 
</p>

По какой-то причине мне понравилась идея иметь одну запись postmeta для каждого мета-бокса. В хуке save_post я сохраняю данные следующим образом:

update_post_meta($post_id, '_coordinates', $_POST['coordinates']);

Я сделал это потому, что у меня есть три мета-бокса, и мне нравится иметь всего 3 значения postmeta для каждой записи; однако теперь я осознал потенциальную проблему. Мне может понадобиться использовать WP_Query для извлечения только определенных записей на основе этих мета-значений. Например, я могу захотеть получить все записи, у которых значения широты выше 50. Если бы эти данные хранились в базе данных по отдельности, возможно, используя ключ latitude, я бы сделал что-то вроде:

$args = array(
    'post_type' => 'my-post-type',
    'meta_query' => array(
        array(
            'key' => 'latitude',
            'value' => '50',
            'compare' => '>'
        )
    )
 );
$query = new WP_Query( $args );

Поскольку широта является частью postmeta _coordinates, этот способ не сработает.

Итак, мой вопрос: существует ли способ использовать meta_query для запроса к сериализованному массиву, как в моем сценарии?

0
Все ответы на вопрос 14
6
52

Нет, это невозможно и даже может быть опасно.

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

Сериализованные данные — это вектор атаки и серьезная проблема для производительности.

Я рассмотрю:

  • Исправление и возможность запросов
  • Почему нельзя запрашивать внутри сериализованных данных?
    • Почему LIKE не является решением
  • Примечание о хранении записей/сущностей/объектов как сериализованных объектов в метаданных
  • Безопасность и сериализованные объекты
  • Что делать, если у меня есть список ID?
  • Что делать, если у меня есть массив именованных элементов?
  • Полный обход проблемы
  • Заключение

Но что насчет LIKE?

Вы могли видеть благонамеренные вопросы, предлагающие использовать LIKE для решения этой проблемы. Разве это не решает проблему? Это не решение, а обманчивый путь.

Есть несколько серьезных проблем:

  • ложные совпадения, поиск test с LIKE также найдет test, testing, untested и другие значения
  • нет способа ограничить это подключами для массивов с ключами или объектов
  • невозможно выполнить сортировку
  • очень медленно и ресурсоемко

LIKE будет работать только в ограниченных и нереалистичных ситуациях, а также несет серьезные потери производительности.

Исправление и возможность запросов

Я настоятельно рекомендую десериализовать ваши данные и изменить процедуру сохранения. Примерно такой код должен преобразовать ваши данные в новый формат:

$args = [
    'post_type' => 'my-post-type',
    'meta_key' => '_coordinates',
    'posts_per_page' => -1,
];
$query = new WP_Query( $args );
if ( $query->have_posts() ) {
    while ( $query->have_posts() ) {
        $query->the_post();
        // получаем данные
        $c = get_post_meta( $post->ID, '_coordinates', true );

        // сохраняем в новом формате: отдельные метаданные, таксономия и т.д.
        add_post_meta( $post->ID, '_longitude', $c['longitude'] );
        add_post_meta( $post->ID, '_latitude', $c['latitude'] );

        // Опционально: очищаем старые метаданные
        delete_post_meta( $post->ID, '_coordinates', $c );
    }
    wp_reset_postdata();
}

Теперь вы сможете выполнять запросы с отдельными ключами.

Если вам нужно хранить несколько долгот и широт, вы можете сохранить несколько метаданных с одинаковым именем. Просто используйте третий параметр get_post_meta, и он вернет их все как массив.

Почему нельзя запрашивать внутри сериализованных данных?

MySQL видит это просто как строку и не может разбить ее на структурированные данные. Разделение на структурированные данные — это именно то, что делает код выше.

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

Помимо затрат на частичный поиск строк, запросы к метаданным медленны, а сериализованные данные могут меняться в зависимости от длины содержимого, что делает поиск невероятно дорогим, если не невозможным, в зависимости от искомого значения.

Примечание о хранении записей/сущностей/объектов как сериализованных объектов в метаданных

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

Решение здесь не в том, чтобы разбить их на отдельные метаданные, а в осознании, что они вообще не должны быть метаданными, а должны быть пользовательским типом записи. Например, журнал или запись могут быть пользовательским типом записи с исходным постом в качестве родителя или связанным через таксономию.

Безопасность и сериализованные объекты

Хранение сериализованных PHP-объектов через функцию serialize может быть опасно, что печально, так как передача объекта в WordPress приведет к его сериализации. Это связано с тем, что при десериализации объекта создается объект, и все его методы пробуждения и конструкторы выполняются. Это может не казаться большой проблемой, пока пользователь не сможет проскользнуть тщательно сформированный ввод, приводящий к удаленному выполнению кода, когда данные читаются из базы данных и десериализуются WordPress.

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

Что делать, если у меня есть список ID?

Вы можете попытаться передать WordPress массив или превратить его в список, разделенный запятыми, но в этом нет необходимости!

Ключи метаданных поста не уникальны, вы можете сохранять один и тот же ключ несколько раз, например:


$id = ...;
add_post_meta( $id, 'mylist', 1 );
add_post_meta( $id, 'mylist', 2 );
add_post_meta( $id, 'mylist', 3 );
add_post_meta( $id, 'mylist', 4 );
add_post_meta( $id, 'mylist', 5 );
add_post_meta( $id, 'mylist', 6 );

Но как получить данные обратно? Вы замечали, что вызовы get_post_meta имеют третий параметр, который всегда установлен в true? Установите его в false:

$mylist = get_post_meta( $id, 'mylist', false );
foreach ( $mylist as $number ) {
    echo '<p>' . esc_html( $number ) . '</p>;
}

Что делать, если у меня есть массив именованных элементов?

Что если я хочу сохранить эту структуру данных так, чтобы можно было запрашивать поля?

{
  "foo": "bar",
  "fizz": "buzz"
  "parent": {
    "child": "value"
  }
}

Это просто: разбейте их с префиксами:

add_post_meta( $id, "tomsdata_foo", "bar" );
add_post_meta( $id, "tomsdata_fizz", "buzz" );
add_post_meta( $id, "tomsdata_parent_child", "value" );

И если вам нужно перебрать некоторые из этих значений, используйте get_post_meta( $id );, чтобы получить все метаданные поста и перебрать ключи, например:

$all_meta = get_post_meta( $id );
$look_for = 'tomsdata_parent';
foreach ( $all_meta as $key => $value ) {
    if ( substr($string, 0, strlen($look_for)) !== $look_for ) {
        continue; // не совпадает, пропускаем!
    }
    echo '<p>' . $key . ' = ' . $value . '</p>';
}

Что выведет:

<p>tomsdata_parent_child = value</p>

Помните, когда WordPress загружает пост, он одновременно загружает все его метаданные, поэтому вызовы get_post_meta очень дешевы и не вызывают дополнительных запросов к базе данных.

Полный обход проблемы

Если вы знаете, что вам нужно будет искать/запрашивать/фильтровать по подзначению, почему бы просто не сохранить дополнительное метаданное с этим значением, чтобы можно было его искать?

Заключение

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

Возможно, можно использовать регулярное выражение и LIKE, но это крайне ненадежно, не работает для большинства типов данных и очень, очень медленно и ресурсоемко для базы данных. Вы также не можете выполнять математические операции с результатами, как это можно было бы сделать, если бы они были отдельными значениями.

20 авг. 2012 г. 18:19:49
Комментарии

Для тех, кто проходит мимо, не прекращайте чтение: более полезные (и свежие) ответы находятся ниже

Erenor Paz Erenor Paz
13 апр. 2017 г. 13:07:01

Что если у меня есть массив ID для сохранения - и они не представляют разные ключи, под которыми я мог бы их сохранить, такие как 'latitude' и т.д., а только один ключ для всех (например, при сохранении связей и т.п.). Что делать в таком случае? Решение от @rabni?

trainoasis trainoasis
16 июн. 2017 г. 10:52:59

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

Tom J Nowell Tom J Nowell
19 июн. 2017 г. 20:31:41

Конечно, это возможно. Да, вам нужно убедиться, что значения уникальны, что является основной сутью таких postmeta. Обычно такие сериализованные значения происходят из чекбоксов, которые являются массивами. Каждый чекбокс в наборе должен иметь уникальное значение. А если нет — они должны убедиться, что его опция (в php: ключ массива) уникальна, чтобы её можно было найти.

Позже, запрос — это простая задача по сравнению с вашим решением: 'meta_query' => array( array( 'key' => 'thekey', 'value' => 'thevalue', 'compare' => 'LIKE', 'type' => 'CHAR', ), ),

User User
17 февр. 2023 г. 09:42:18

@BedaSchmid это не делает то, что вы думаете. Если бы вы прочитали мой ответ, то увидели бы, что я затронул пункт про LIKE и указал на серьёзные недостатки, которые делают его ненадёжным и вызывают ошибки. Смотрите часть под названием "А что насчёт LIKE?". Я отредактировал свой ответ, переместив её выше для тех, кто не читает ответ целиком — за годы таких накопилось несколько.

Tom J Nowell Tom J Nowell
17 февр. 2023 г. 11:42:52

@TomJNowell — из всех 4 перечисленных причин только одна является серьёзной проблемой, и, как я пояснил в комментарии, её можно избежать. Медленно? Да. Любой meta query работает медленно. Сортировка? Ладно. Допустим, если вам действительно нужно делать запросы по этому полю, можно закрыть глаза на сортировку. В любом случае сортировка по массиву, где может быть выбрано несколько значений, выглядит странно. Возможно делать запросы по таким значениям, и это не возвращает неточных результатов, при условии, что сериализованный массив содержит уникальные значения, как я упомянул в своём комментарии.

User User
18 февр. 2023 г. 12:50:12
Показать остальные 1 комментариев
6
41

Я также столкнулся с такой ситуацией. Вот что я сделал:

$args = array(
    'post_type' => 'my-post-type',  // Тип записи
    'meta_query' => array(         // Мета-запрос
        array(
            'key' => 'latitude',  // Ключ мета-поля
            'value' => sprintf(':"%s";', $value),  // Форматируем значение
            'compare' => 'LIKE'    // Оператор сравнения
        )
    )
);

Надеюсь, это поможет

16 апр. 2015 г. 15:38:57
Комментарии

Мне очень понравилось это решение. К сожалению, оно не применимо, когда $value также является ID. В этом случае я предлагаю создать две функции: одну для добавления символа к каждому элементу массива перед сохранением данных, и другую для удаления этого символа перед использованием данных. Таким образом, сериализованный индекс i:2 не будет путаться с i:D2 "реальных" данных. Параметр meta query тогда должен принять вид 'value' => sprintf(':"D%s";', $value),, и вы сохраните корректную функциональность этого замечательного ответа!

Erenor Paz Erenor Paz
13 апр. 2017 г. 12:30:09

Это решение работает у меня

Rakhi Rakhi
12 янв. 2018 г. 11:32:02

Это тоже идеально сработало у меня. Хотя сначала испытал мини-панику, когда увидел принятое решение

Shane Jones Shane Jones
26 февр. 2018 г. 12:48:20

@Erenor Paz, я только что опубликовал решение, которое хорошо работает как с ID, так и со строками: https://wordpress.stackexchange.com/a/299325/25264

Pablo S G Pacheco Pablo S G Pacheco
29 мар. 2018 г. 22:09:08

использование LIKE — отличный и быстрый способ положить ваш сервер (не говоря уже о ложных срабатываниях), вам лучше иметь очень хорошее кеширование.

Mark Kaplun Mark Kaplun
2 апр. 2018 г. 08:23:41

Обратите внимание, что с этим решением будет совпадать любое подзначение с этим значением. Так что если вы ищете широту 5, это также совпадёт с долготой 5. Это менее критично для данного конкретного случая, но за этим нужно следить. Также невозможно сортировать результаты, и нельзя выполнять какую-либо логику/математику, например, получить все значения, которые больше или меньше числа

Tom J Nowell Tom J Nowell
29 мая 2020 г. 12:29:37
Показать остальные 1 комментариев
0
11

Вы действительно потеряете возможность эффективно запрашивать свои данные при сериализации записей в базу данных WordPress.

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

Вместо этого используйте сериализацию для данных, которые вы не планируете запрашивать таким образом, а будете получать только пассивно через прямой вызов WordPress API get_post_meta() — с помощью этой функции вы также можете распаковать сериализованную запись для доступа к её свойствам массива.

Фактически, если присвоить значение true, как в примере:

$meta = get_post_meta( $post->ID, 'key', true );

Данные будут возвращены в виде массива, с которым вы сможете работать в обычном режиме.

Вы можете сосредоточиться на других оптимизациях базы данных/сайта, таких как кэширование, минификация CSS и JS, а также использование таких сервисов, как CDN, если это необходимо. Это лишь несколько примеров... WordPress Codex — хорошая отправная точка для изучения этой темы: ЗДЕСЬ

20 авг. 2012 г. 17:36:42
5

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

array(37,87);

сохраняется как сериализованный массив, вот так:

a:2:{i:0;i:37;i:1;i:87;}

Обратите внимание на i:0 как первую позицию массива и i:37 как первое значение. Шаблон одинаковый. Но давайте перейдем к решениям.


1) Решение с REGEXP

Это решение работает для меня независимо от того, сохранено ли мета-значение как строка или число / ID. Однако оно использует REGEXP, что не так быстро, как использование LIKE.

$args = array(
    'post_type' => 'my-post-type',
    'meta_query' => array(
        array(
            'key' => 'latitude',
            'value' => '\;i\:' . $value . '\;|\"' . $value . '\";',
            'compare' => 'REGEXP'
        )
    )
);

2) Решение с LIKE

Я не уверен насчет разницы в производительности, но это решение использует LIKE и также работает как для чисел, так и для строк.

 $args = array(
        'post_type' => 'my-post-type',
        'meta_query' => array(
            'relation' => 'OR',
            array(
                'key' => 'latitude',
                'value' => sprintf(':"%s";', $value),
                'compare' => 'LIKE'
            ),
            array(
                'key' => 'latitude',
                'value' => sprintf(';i:%d;', $value),
                'compare' => 'LIKE'
            )
        )
    );
29 мар. 2018 г. 22:07:17
Комментарии

REGEXP хорош в определенных ситуациях, но если можно использовать LIKE, я считаю, что это предпочтительный метод. Старая ссылка, но все еще довольно полезная, на мой взгляд: https://thingsilearn.wordpress.com/2008/02/28/mysql-query-speed-regexp-vs-like/ :-)

Erenor Paz Erenor Paz
30 мар. 2018 г. 11:44:57

@ErenorPaz Вы правы. LIKE работает быстрее. Но это решение, которое работает как со строками, так и с числами

Pablo S G Pacheco Pablo S G Pacheco
30 мар. 2018 г. 23:37:19

Да..так что ответ (как всегда): зависит от ситуации, если можно использовать "LIKE" — это предпочтительно, в противном случае REGEXP тоже подойдет :-)

Erenor Paz Erenor Paz
31 мар. 2018 г. 16:10:05

@ErenorPaz, я отредактировал свой ответ, добавив новое решение, которое использует LIKE, но работает как с числами, так и со строками. Я не уверен насчёт производительности, так как приходится сравнивать результаты с помощью OR

Pablo S G Pacheco Pablo S G Pacheco
1 апр. 2018 г. 22:41:09

Точно!!! Именно такой результат мне и нужен был...

Спасибо, чувак!!!

kuldip Makadiya kuldip Makadiya
27 сент. 2018 г. 14:23:28
0

Я только что разобрался с сериализованными полями и смог выполнить по ним запрос. Не используя meta_query, а с помощью SQL-запроса.

global $wpdb; 

$search = serialize('latitude').serialize(50);

$query = $wpdb->prepare("SELECT `post_id`
FROM `wp_postmeta`
WHERE `post_id` IN (SELECT `ID` FROM `wp_posts` WHERE `post_type` = 'my-post-type')
AND `meta_key` = '_coordinates'
AND `meta_value` LIKE '%s'",'%'.$search.'%');

$ids = $wpdb->get_col($query);

$args = array(
    'post__in' => $ids
    'post_type' => 'team' // добавляем тип, потому что по умолчанию будет 'post'
);

$posts = get_posts($args);

Запрос сначала ищет записи с соответствующим post_type, чтобы уменьшить количество записей в wp_postmeta для фильтрации. Затем я добавил условие WHERE, чтобы дополнительно сократить количество строк, фильтруя по meta_key.

ID в итоге попадают в массив, как и требуется для get_posts.

P.S. Для хорошей производительности подзапросов требуется MySQL версии 5.6 или выше.

22 авг. 2013 г. 12:41:49
2

Простой способ обойти эту ситуацию — использовать сравнение LIKE с сериализованным форматом (длина с типом и самим значением), как показано ниже:

$id = 123; // ID для проверки в сериализованных данных.
//(предполагается, что числа хранятся как строка. В моём случае так и было.)

$args = array(
    'post_type' => 'custom_post_type',
    'meta_query' => array(
        array(
            'key' => 'meta_key_to_check_from',
            'value' => 's:' . strlen($id) . ':"' . $id . '";',
            'compare' => 'LIKE'
        )
    )
);

$query = new WP_Query( $args );

Если нужно искать по пользовательской таксономии:

$id = 123; // ID для проверки в сериализованных данных.
//(предполагается, что числа хранятся как строка. В моём случае так и было.)

$args = array(
    'hide_empty' => false, // также получать термины, которые ещё не используются
    'meta_query' => array(
        array(
            'key' => 'meta_key_to_check_from',
            'value' => 's:' . strlen($id) . ':"' . $id . '";',
            'compare' => 'LIKE'
        )
    ),
    'taxonomy' => 'custom_taxonomy'
);

$terms = get_terms( $args );
15 мая 2022 г. 16:22:19
Комментарии

Можно сказать, что этот пример прямо отвечает на вопрос и именно то, что я искал.

Sharpey Sharpey
18 мая 2023 г. 01:25:34

Это именно то решение проблемы, лучший ответ здесь!

rank rank
20 февр. 2024 г. 19:55:03
1

Этот пример мне очень помог. Он предназначен специально для плагина S2Members (который сериализует метаданные пользователей). Но он позволяет выполнять запрос к части сериализованного массива внутри meta_key.

Это работает с использованием функции MySQL REGEXP.

Вот источник

Вот код, который запрашивает всех пользователей, проживающих в США. Я легко модифицировал его для запроса одного из моих пользовательских полей регистрации, и он заработал мгновенно.

<?php
global $wpdb;
$users = $wpdb->get_results ("SELECT `user_id` as `ID` FROM `" . $wpdb->usermeta . 
          "` WHERE `meta_key` = '" . $wpdb->prefix . "s2member_custom_fields' AND 
           `meta_value` REGEXP '.*\"country_code\";s:[0-9]+:\"US\".*'");
if (is_array ($users) && count ($users) > 0)
    {
        foreach ($users as $user)
            {
                $user = /* Получаем полный объект пользователя. */ new WP_User ($user->ID);
                print_r($user); /* Получаем полный список свойств при отладке. */
            }
    }
?>
12 дек. 2012 г. 20:43:48
Комментарии

Спасибо. Это единственное, что сработало, когда я пытался получить сериализованные данные из стороннего плагина.

adamalexanderw adamalexanderw
25 апр. 2023 г. 12:40:45
0

После прочтения множества советов по выполнению запроса WP_Query с фильтрацией по сериализованным массивам, вот как я в итоге решил эту задачу: создав массив значений, разделенных запятыми, с помощью функции implode в сочетании с пользовательским SQL-запросом $wpdb, использующим FIND_IN_SET для поиска в списке значений, разделенных запятыми, нужного значения.

(это похоже на ответ Томаса, но немного менее ресурсоемко для SQL-запроса)

1. В functions.php:

В файле functions.php (или там, где вы настраиваете метабокс) в функции yourname_save_post() используйте

update_post_meta($post->ID, 'checkboxArray', implode(",", $checkboxArray)); //добавляем implode

для создания массива со значениями, разделенными запятыми.

Также вам нужно изменить переменную вывода в функции построения метабокса администратора yourname_post_meta() на

$checkboxArray = explode(",", get_post_custom($post->ID)["checkboxArray"][0]); //добавляем explode

2. В файле шаблона PHP:

Проверка: если вы запустите get_post_meta( $id );, вы должны увидеть checkboxArray как массив, содержащий ваши значения, разделенные запятыми, вместо сериализованного массива.

Теперь мы строим наш пользовательский SQL-запрос с помощью $wpdb.

global $wpdb;

$search = $post->ID;

$query = "SELECT * FROM wp_posts
          WHERE FIND_IN_SET( $search, (
              SELECT wp_postmeta.meta_value FROM wp_postmeta
              WHERE wp_postmeta.meta_key = 'blogLocations'
              AND wp_postmeta.post_id = wp_posts.ID )
          )
          AND ( wp_posts.post_type = 'post' )
          AND ( wp_posts.post_status = 'publish' );";

$posts = $wpdb->get_results($query);

foreach ($posts as $post) {
    //ваш контент поста здесь
}

Обратите внимание на FIND_IN_SET — именно здесь происходит вся магия.

Теперь... Поскольку я использую SELECT *, это возвращает все данные поста, и внутри цикла foreach вы можете вывести то, что вам нужно (выполните print_r($posts);, если не знаете, что включено). Это не настраивает "петлю" за вас (мне так больше нравится), но это можно легко изменить для настройки петли, если вы предпочитаете (посмотрите setup_postdata($post); в кодексе, вам, вероятно, понадобится изменить SELECT * на выбор только ID постов и $wpdb->get_results на правильный тип $wpdb — см. кодекс для $wpdb также для информации по этой теме).

Что ж, потребовались некоторые усилия, но поскольку wp_query не поддерживает выполнение 'compare' => 'IN' для сериализованных или разделенных запятыми значений, этот костыль — ваш лучший вариант!

Надеюсь, это кому-то поможет.

23 янв. 2015 г. 00:10:32
0

Если вы используете оператор сравнения like в мета-запросе, он должен корректно работать для поиска внутри сериализованного массива.

$wp_user_search = new WP_User_Query(array(
    'meta_query' => array(
        array(
            'key'     => 'wp_capabilities',
            'value'   => 'subscriber',
            'compare' => 'not like'
            )
        )
    )
);

результат:

[query_where] => WHERE 1=1 AND (
  ( wp_usermeta.meta_key = 'wp_capabilities' 
  AND CAST(wp_usermeta.meta_value AS CHAR) NOT LIKE '%subscriber%' )
4 июл. 2015 г. 00:14:38
1

Если мои метаданные имеют тип массива, я использую этот метод для запроса по мета:

$args = array(
    'post_type' => 'fotobank', // Тип записи
    'posts_per_page' => -1,     // Получить все записи
    'meta_query' => array(
            array(
                   'key' => 'collections', // Ключ метаполя
                   'value' => ':"'.$post->ID.'";', // Ищем ID поста в сериализованном массиве
                   'compare' => 'LIKE'     // Используем LIKE для поиска в строке
            )
     )
);
$fotos = new WP_Query($args); // Создаем новый запрос
7 июл. 2016 г. 09:50:49
Комментарии

Это может привести к нежелательным результатам, если ID записи имеет такое же значение, как и ID сериализованной строки

Erenor Paz Erenor Paz
13 апр. 2017 г. 13:05:22
0

Мне стало любопытно насчет ответов выше, где meta_query указывал на ключ latitude вместо _coordinates. Пришлось проверить, действительно ли в meta-запросах можно адресовать конкретный ключ внутри сериализованного массива. :)

Очевидно, это не так.

Итак, обратите внимание, что правильный ключ для поиска — это _coordinates, а не latitude.

$args = array(
     'post_type' => 'my-post-type',
     'meta_query' => array(
         array(
             'key' => '_coordinates',
             'value' => sprintf(':"%s";', $value),
             'compare' => 'LIKE'
         )
     )
 );

ПРИМЕЧАНИЯ:

  1. Данный подход позволяет искать только точные совпадения. Поэтому поиск по условиям типа все широты больше 50 невозможен.

  2. Для включения поиска по подстроке можно использовать 'value' => sprintf(':"%%%s%%";', $value),. (не тестировалось)

6 июл. 2017 г. 14:57:01
0

Вы можете заключить значение в %, чтобы выполнить запрос сериализованного значения:

$args = array(
    'post_type' => 'my-post-type',
    'meta_query' => array(
        array(
            'key' => 'latitude',
            'value' => '%' . 50 . '%',
            'compare' => '>'
        )
    )
 );
$query = new WP_Query( $args );
30 сент. 2023 г. 20:09:06
3
-1

У меня такой же вопрос. Возможно, вам нужен параметр 'type'? Посмотрите этот связанный вопрос: Запрос по произвольным полям - Мета-значение является массивом

Попробуйте, возможно, так:

    $args = array(
    'post_type' => 'my-post-type',
    'meta_query' => array(
        array(
            'key' => 'latitude',
            'value' => '50',
            'compare' => '>',
            'type' => 'numeric'
        )
    )
    );
12 мая 2011 г. 20:44:40
Комментарии

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

tollmanz tollmanz
13 мая 2011 г. 06:10:57

Да, ты прав. Я попробовал это сегодня утром, и у меня тоже не сработало.

У меня та же проблема. Хранение значения мета-ключа в виде массива. Я начинаю думать, что это невозможно, и вместо этого мне придется хранить их как отдельные мета-поля с одинаковыми именами... и просто правильно управлять их удалением/обновлением.

user4356 user4356
13 мая 2011 г. 18:05:58

@user4356... это именно то, что я собираюсь сделать. Я надеялся сократить количество строк, которые вставляю для каждого поста, но, видимо, это невозможно.

tollmanz tollmanz
14 мая 2011 г. 22:15:09
5
-1

Я столкнулся с похожей ситуацией при использовании плагина Magic Fields. Возможно, это решение вам подойдет:

$values_serialized = serialize(array('50'));
$args = array(
    'post_type' => 'my-post-type',
    'meta_query' => array(
        array(
            'key' => 'latitude',
            'value' => $values_serialized,
            'compare' => '>'
        )
    )
);
13 мая 2011 г. 00:10:32
Комментарии

Спасибо за предложение! Думаю, это максимально близкое решение, но оно не будет работать, так как сравнение сериализованного массива с другим сериализованным массивом не имеет смысла, если я не ищу точное совпадение.

tollmanz tollmanz
13 мая 2011 г. 06:08:22

Тогда этот ответ не должен быть помечен как правильный, и безответственно с вашей стороны так делать. Правильным ответом в таком случае будет: "Нет, это невозможно".

Tom J Nowell Tom J Nowell
20 авг. 2012 г. 18:14:28

Согласен, к тому же WordPress сам обрабатывает сериализацию, serialize() не требуется в данном случае...

Adam Adam
20 авг. 2012 г. 19:11:31

На самом деле ответ @seth-stevenson отличный, когда делаешь именно то, что он сказал, используя плагин "Magic Fields". Поскольку этот плагин по умолчанию сериализует данные определенного типа, это лучший способ сделать ТОЧНОЕ совпадение.

zmonteca zmonteca
9 янв. 2013 г. 07:02:53

@TomJNowell Готово! Мне понадобилось всего 5 месяцев ;)

tollmanz tollmanz
18 янв. 2013 г. 00:15:31