Nested meta_query with multiple relation keys

I am curious whether WordPress is able to run nested meta_query, with each having different relation keys? As of WordPress 3.0, tax_query is able to perform this function; I’m wondering whether this has an equivalent with meta_query.

$results = query_posts( array(
    'post_type' => 'event_id',
    'meta_query' => array(
        'relation' => 'AND',
        array(
            'relation' => 'OR',
            array(
                'key' => 'primary_user_id',
                'value' => $user_id
            ),
            array(
                'key' => 'secondary_user_id',
                'value' => $user_id
            )
        ),
        array(
            'key' => 'date',
            'value' => array( $start_date, $end_date ),
            'type' => 'DATETIME',
            'compare' => 'BETWEEN'
        )
    )
) );

References:

Related posts

Leave a Reply

3 comments

  1. Meanwhile this is possible, see documentation with example and explanation:

    Old link: https://codex.wordpress.org/Class_Reference/WP_Query#Custom_Field_Parameters
    Update: 2021, new link: https://developer.wordpress.org/reference/classes/wp_query/#custom-field-post-meta-parameters

    and another example https://wordpress.org/support/topic/wp_query-with-multiple-meta_query/#post-9410992

    'meta_query'  => array(
        'relation' => 'OR',
        array(
            'relation' => 'AND',
            array(
                'key'     => '_price',
                'value'   => 1,
                'compare' => '>=',
                'type' => 'DECIMAL',
            ),
            array(
                'key'     => '_price',
                'value' => 3000,
                'compare' => '<=',
                'type' => 'DECIMAL',
            ),
        ),
        array(
            'relation' => 'AND',
            array(
                'key'     => '_price',
                'value'   => 3001,
                'compare' => '>=',
                'type' => 'DECIMAL',
            ),
            array(
                'key'     => '_price',
                'value' => 6000, //fixed <= to =>
                'compare' => '<=',
                'type' => 'DECIMAL',
            ),
        )
    ),
    
  2. That seems to be impossible. Please someone correct me if I’m wrong.

    The meta_query parameter will actually be transformed into a WP_Meta_Query object, and the relation verification won’t go deeper in wp-includes/meta.php, and occurs just once in the top level:

    if ( isset( $meta_query['relation'] ) && strtoupper( $meta_query['relation'] ) == 'OR' ) {
        $this->relation = 'OR';
    } else {
        $this->relation = 'AND';
    }
    

    A possible solution for this is to build your own JOIN for this query.

    $query = new WP_Query( array(
        ...
        'my_meta_query' => true,
        'suppress_filters' => false
    ) );
    
    add_filter( 'posts_join', 'my_meta_query_posts_join', 10, 2 );
    function my_meta_query_posts_join( $join, $query ) {
    
        if ( empty( $query->query_vars['my_meta_query'] ) )
            return $join;
    
        global $wpdb;
    
        $new_join = "
            INNER JOIN {$wpdb->postmeta} pm1 ON 1=1
                AND pm1.post_id = {$wpdb->posts}.ID
                AND pm1.meta_key = '_some_meta_key'
                AND pm1.meta_value = 'some_value'
        ";
    
        return $join . ' ' . $new_join;
    }
    

    And if you need further verifications and rules, you can also use the posts_where filter.