Wordpress - How do I query for posts by partial meta key?

It is quite difficult to concretely answer your question. The first part is easy though. I recently did something similar on stackoverflow

Meta keys are are compared and match exactly. WP_Query have no means to adjust this behavior with a simple parameter, but we can always introduce one ourselves and then adjust the posts_where clause to do a LIKE comparison on meta keys.

THE FILTER

This is just a basic filter, adjust it as needed.

add_filter( 'posts_where', function ( $where, \WP_Query $q )
{ 
    // Check for our custom query var
    if ( true !== $q->get( 'wildcard_on_key' ) )
        return $where;

    // Lets filter the clause
    $where = str_replace( 'meta_key =', 'meta_key LIKE', $where );

    return $where;
}, 10, 2 );

As you can see, the filter is only fired when we set our new custom parameter, wildcard_on_key to true. When this checks out, we simply change the = comparator to the LIKE comparator

Just a note on this, LIKE comparisons are inherently more expensive to run that other comparisons

THE QUERY

You can simply query your posts as follow to get all posts with meta keys like_status_{user_id}

$args = [
    'wildcard_on_key' => true,
    'meta_query'      => [
        [
            'key'   => 'like_status_',
            'value' => 1,
        ]
    ]
];
$query = new WP_Query( $args );

OTHER QUESTION

Custom fields does not have impact on performance, you can read my post on this subject here. I am however troubled by that you say each post can have hundreds or thousands of likes. This can hit you on performance getting and caching such a large amount of custom field data. It can also clog your db with a huge amount of unnecessary custom field data which makes it quite hard to maintain.

I am not a very big fan of storing serialized data in custom fields as one cannot search or order by serialized data. I would however suggest storing all the user ID's in an array under one custom field. You can simply just update the array with the user ID when a user like a post. Getting the custom field data and looping over the array of ID's and doing something with the ID's are easy. Just have a look at get_post_meta()

Updating a custom field is also easy. For that, you will need to look into update_post_meta(), I do not know how you create your custom fields, but update_post_meta() is definitely something you would want to use.

If you need to send emails or push notifications when a custom field is updated, you have the following hooks available to work with. (See update_metadata() for context)

  • update_postmeta

  • updated_{$meta_type}_meta

  • updated_postmeta

  • update_{$meta_type}_meta

  • update_{$meta_type}_metadata

CONCLUSION

Just before I post this, again, before you go the serialized route, make sure that you would not need to sort by the sorted data or search for particular data inside the serialized data.


Since wordpress 5.1 it is possible now to use meta query like: enter image description here


Unfortunately you cannot perform a meta_query using a LIKE comparison on the meta_key value when using WP_Query. I've been down this road...

Instead you have a couple other options if you want to maintain like status relationships as post meta and not user meta and or meta in a custom table.

Option 1

  • requires no modification of your meta schema
  • uses wpdb class to perform a custom query

Example:

//when a user likes a post...
$current_user_id = get_current_user_id();
add_post_meta($current_user_id, "like_status_{$current_user_id}", 1, false);

//later in the request...
global $wpdb;

$results = $wpdb->get_results(
    "
    SELECT meta_key 
    FROM {$wpdb->prefix}postmeta 
    WHERE meta_key 
    LIKE 'like_status_%'
    ",
    ARRAY_N
);

$results = array_map(function($value){

    return (int) str_replace('like_status_', '', $value[0]);

}, $results);

array_walk($results, function($notify_user_id, $key){

    //apply to all users except the user who just liked the post
    if ( $notify_user_id !== $current_user_id ) {
        //notify logic here...           
    }

});

Note: logic could be simplified further if you wish.

Option 2

  • requires you change your meta schema
  • requires you store the user id as the meta value
  • allows you to use WP_Query along with meta_query

Option 2 requires that you change your meta key from like_status_{user_id} to something universal such as like_status or liked_by_user_id where in turn instead of storing the value of 1 against the key, you instead store the user's id as the value.

//when a user likes a post...
$current_user_id = get_current_user_id();
add_post_meta($current_user_id, "liked_by_user_id", $current_user_id, false);

//later in the request
$args = array(
    'post_type'  => 'post', //or a post type of your choosing
    'posts_per_page' => -1,
    'meta_query' => array(
        array(
            'key' => 'liked_by_user_id',
            'value' => 0,
            'type' => 'numeric'
            'compare' => '>'
        )
    )
);

$query = new WP_Query($args);   

array_walk($query->posts, function($post, $key){

    $user_ids = get_post_meta($post->ID, 'liked_by_user_id');

    array_walk($user_ids, function($notify_user_id, $key){
        
        //notify all users except the user who just like the post
        if ( $notify_user_id !== $current_user_id ) {
                
            //notify logic here...
            //get user e.g. $user = get_user_by('id', $notify_user_id);
            
        }

    });

});