Bir arama sorgusuyla ('s') meta sorguyu ('meta_query') kullanma


25

Yalnızca varsayılanları (başlığı, içeriği vb.) Değil belirli bir özel alanı da arayacak bir arama oluşturmaya çalışıyorum.

Şu anki sorgum:

$args = array(
  'post_type' => 'post',
  's' => $query,
  'meta_query' => array(
     array(
       'key' => 'speel',
       'value' => $query,
       'compare' => 'LIKE'
     )
   )
);

$search = new WP_Query( $args )
...

Bu, hem arama sorgusu hem de meta sorgusu ile eşleşen gönderileri döndürür, ancak aynı zamanda yalnızca ikisinden biriyle eşleşen gönderileri de döndürmesini istiyorum.

Herhangi bir fikir?


Ne yapmak istediğinizi sadece bir arama sorgusu kullanarak yapmak mümkün değildir. Her iki sorguyu da ayrı ayrı çalıştırmanız ve sonra bunları birleştirmeniz gerekir. Bu, diğer cevabında açıklanmıştır. Nasıl yapılacağı konusunda size yardım etmelidir. wordpress.stackexchange.com/questions/55519/…
Nick Perkins

Yanıtlar:


20

Bu soruna bir çözüm bulmak için saatlerdir arıyordum. Dizi birleştirme, özellikle sorgular karmaşık olduğunda ve gelecekte meta sorgular ekleyebilmeniz gerektiğinde gitmenin yolu değildir. Basitçe güzel olan çözüm, 's' harfini hem başlık hem de meta alanlarını aramaya izin verecek şekilde değiştirmek.

add_action( 'pre_get_posts', function( $q )
{
    if( $title = $q->get( '_meta_or_title' ) )
    {
        add_filter( 'get_meta_sql', function( $sql ) use ( $title )
        {
            global $wpdb;

            // Only run once:
            static $nr = 0; 
            if( 0 != $nr++ ) return $sql;

            // Modified WHERE
            $sql['where'] = sprintf(
                " AND ( %s OR %s ) ",
                $wpdb->prepare( "{$wpdb->posts}.post_title like '%%%s%%'", $title),
                mb_substr( $sql['where'], 5, mb_strlen( $sql['where'] ) )
            );

            return $sql;
        });
    }
});

Kullanımı:

$meta_query = array();
$args = array();
$search_string = "test";

$meta_query[] = array(
    'key' => 'staff_name',
    'value' => $search_string,
    'compare' => 'LIKE'
);
$meta_query[] = array(
    'key' => 'staff_email',
    'value' => $search_string,
    'compare' => 'LIKE'
);

//if there is more than one meta query 'or' them
if(count($meta_query) > 1) {
    $meta_query['relation'] = 'OR';
}

// The Query
$args['post_type'] = "staff";
$args['_meta_or_title'] = $search_string; //not using 's' anymore
$args['meta_query'] = $meta_query;



$the_query = new WP_Query($args)

doğru yol bu,
Zorox

Fantastik, bu benim için çok iyi çalıştı. Harika çözüm! Teşekkürler!
Fabiano

Bu bir sorgu, arama değil. Aramada çalışan eklentileriniz varsa çalışmaz. Yanlış mıyım?
marek.m

5

Bu cevabın değiştirilmiş bir versiyonu kullanılarak çok sayıda kod azaltılabilir .

$q1 = new WP_Query( array(
    'post_type' => 'post',
    'posts_per_page' => -1,
    's' => $query
));

$q2 = new WP_Query( array(
    'post_type' => 'post',
    'posts_per_page' => -1,
    'meta_query' => array(
        array(
           'key' => 'speel',
           'value' => $query,
           'compare' => 'LIKE'
        )
     )
));

$result = new WP_Query();
$result->posts = array_unique( array_merge( $q1->posts, $q2->posts ), SORT_REGULAR );
$result->post_count = count( $result->posts );

Bu benim için çok işe yaradı (özellikle get_posts yerine WP_Query kullanmam gerektiğinden). Post_count satırınızı değiştirmek zorunda kaldım: $result->post_count = count( $result->posts );çünkü aksi halde sadece 1 sonuç alıyordum.
GreatBlakes

4

@Stabir Kira'nın cevabını biraz optimize ettim

function wp78649_extend_search( $query ) {
    $search_term = filter_input( INPUT_GET, 's', FILTER_SANITIZE_NUMBER_INT) ?: 0;
    if (
        $query->is_search
        && !is_admin()
        && $query->is_main_query()
        && //your extra condition
    ) {
        $query->set('meta_query', [
            [
                'key' => 'meta_key',
                'value' => $search_term,
                'compare' => '='
            ]
        ]);

        add_filter( 'get_meta_sql', function( $sql )
        {
            global $wpdb;

            static $nr = 0;
            if( 0 != $nr++ ) return $sql;

            $sql['where'] = mb_eregi_replace( '^ AND', ' OR', $sql['where']);

            return $sql;
        });
    }
    return $query;
}
add_action( 'pre_get_posts', 'wp78649_extend_search');

Şimdi (başlık, içerik, hariç) veya (meta alan) veya (her ikisiyle) arayabilirsiniz.


3

Gereğince Nick Perkins'in önerisi, ben şöyle iki sorgu birleştirmek zorunda:

$q1 = get_posts(array(
        'fields' => 'ids',
        'post_type' => 'post',
        's' => $query
));

$q2 = get_posts(array(
        'fields' => 'ids',
        'post_type' => 'post',
        'meta_query' => array(
            array(
               'key' => 'speel',
               'value' => $query,
               'compare' => 'LIKE'
            )
         )
));

$unique = array_unique( array_merge( $q1, $q2 ) );

$posts = get_posts(array(
    'post_type' => 'posts',
    'post__in' => $unique,
    'post_status' => 'publish',
    'posts_per_page' => -1
));

if( $posts ) : foreach( $posts as $post ) :
     setup_postdata($post);

     // now use standard loop functions like the_title() etc.     

enforeach; endif;

1
2016'da birleşme olmadan bu hala mümkün değil mi? Pre_get_posts ile arama sorgusu düzenliyorum, bu yüzden bu gerçekten bir seçenek değil ...
trainoasis

@ trainoasis Sanmıyorum. Son 2 saatten beri deniyorum ve google araması beni buraya getirdi.
Umair Khan Jadoon

2

Eh, bir tür hack ama işe yarıyor. Posts_clauses filtresi eklemeniz gerekiyor. Bu süzgeç işlevi, "speel" özel alanında herhangi bir sorgu sözcüğünün olup olmadığını denetler ve kalan sorgu bozulmadan kalır.

function custom_search_where($pieces) {

    // filter for your query
    if (is_search() && !is_admin()) {

        global $wpdb;

        $keywords = explode(' ', get_query_var('s'));
        $query = "";
        foreach ($keywords as $word) {

            // skip possible adverbs and numbers
            if (is_numeric($word) || strlen($word) <= 2) 
                continue;

            $query .= "((mypm1.meta_key = 'speel')";
            $query .= " AND (mypm1.meta_value  LIKE '%{$word}%')) OR ";
        }

        if (!empty($query)) {
            // add to where clause
            $pieces['where'] = str_replace("(((wp_posts.post_title LIKE '%", "( {$query} ((wp_posts.post_title LIKE '%", $pieces['where']);

            $pieces['join'] = $pieces['join'] . " INNER JOIN {$wpdb->postmeta} AS mypm1 ON ({$wpdb->posts}.ID = mypm1.post_id)";
        }
    }
    return ($pieces);
}
add_filter('posts_clauses', 'custom_search_where', 20, 1);

2

aynı sorunu yaşadım, yeni sitem için yeni bir meta "başlık" ekledim:

functions.php

add_action('save_post', 'title_to_meta');

function title_to_meta($post_id)
{
    update_post_meta($post_id, 'title', get_the_title($post_id)); 
}

Ve sonra .. sadece böyle bir şey ekleyin:

$sub = array('relation' => 'OR');

$sub[] = array(
    'key'     => 'tags',
    'value'   => $_POST['q'],
    'compare' => 'LIKE',
);

$sub[] = array(
    'key'     => 'description',
    'value'   => $_POST['q'],
    'compare' => 'LIKE',
);

$sub[] = array(
    'key'     => 'title',
    'value'   => $_POST['q'],
    'compare' => 'LIKE',
);

$params['meta_query'] = $sub;

Bu geçici çözüm hakkında ne düşünüyorsunuz?


1
Bu aslında fena değil, ancak içerik eklemeye başlamadan önce mevcut tüm gönderileri yeniden kaydetmenizi veya kullanmaya başlamanızı gerektirir.
Berend

@ Muhtemelen tüm gönderileri alan ve her biri için post_meta'yı güncelleyen bunlardan geçen bir işlev yazabilirsiniz. Özel bir şablona veya işleve ekleyin, bir kez çalıştırın, sonra atın.
Slam

1

Yukarıdaki çözümlerin tümü yalnızca speel meta anahtarında bir eşleşme varsa sonuçları döndürür. Başka bir yerde sonuç alırsanız ancak bu alanda olmazsa hiçbir şey elde edemezsiniz. Kimse bunu istemiyor.

Sol bir birleştirme gerekiyor. Aşağıdaki bir tane yaratacaktır.

           $meta_query_args = array(
              'relation' => 'OR',
              array(
                'key' => 'speel',
                'value' => $search_term,
                'compare' => 'LIKE',
              ),
              array(
                'key' => 'speel',
                'compare' => 'NOT EXISTS',
              ),
            );
            $query->set('meta_query', $meta_query_args);

0

Bu harika bir çözüm ama bir şeyi düzeltmeniz gerekiyor. 'Post__in' işlevini çağırdığınızda, bir ids dizisi ayarlamanız gerekir ve $ unique bir yazı dizisidir.

örnek:

$q1 = get_posts(array(
        'fields' => 'ids',
        'post_type' => 'post',
        's' => $query
));

$q2 = get_posts(array(
        'fields' => 'ids',
        'post_type' => 'post',
        'meta_query' => array(
            array(
               'key' => 'speel',
               'value' => $query,
               'compare' => 'LIKE'
            )
         )
));

$unique = array_unique( array_merge( $q1->posts, $q2->posts ) );

$array = array(); //here you initialize your array

foreach($posts as $post)
{
    $array[] = $post->ID; //fill the array with post ID
}


$posts = get_posts(array(
    'post_type' => 'posts',
    'post__in' => $array,
    'post_status' => 'publish',
    'posts_per_page' => -1
));

0

@ Satbir-kira cevap harika çalışıyor ama sadece meta ve posta başlığı ile arama yapacak. Meta, başlık ve içerik üzerinden arama yapmasını istiyorsanız, işte değiştirilmiş versiyon.

    add_action( 'pre_get_posts', function( $q )
    {
      if( $title = $q->get( '_meta_or_title' ) )
      {
        add_filter( 'get_meta_sql', function( $sql ) use ( $title )
        {
          global $wpdb;

          // Only run once:
          static $nr = 0;
          if( 0 != $nr++ ) return $sql;

          // Modified WHERE
          $sql['where'] = sprintf(
              " AND ( (%s OR %s) OR %s ) ",
              $wpdb->prepare( "{$wpdb->posts}.post_title like '%%%s%%'", $title),
              $wpdb->prepare( "{$wpdb->posts}.post_content like '%%%s%%'", $title),
              mb_substr( $sql['where'], 5, mb_strlen( $sql['where'] ) )
          );

          return $sql;
        });
      }
    });

Ve işte kullanımı:

$args['_meta_or_title'] = $get['search']; //not using 's' anymore

$args['meta_query'] = array(
  'relation' => 'OR',
  array(
    'key' => '_ltc_org_name',
    'value' => $get['search'],
    'compare' => 'LIKE'
  ),
  array(
    'key' => '_ltc_org_school',
    'value' => $get['search'],
    'compare' => 'LIKE'
  ),
  array(
    'key' => '_ltc_district_address',
    'value' => $get['search'],
    'compare' => 'LIKE'
  )
);

$get['search']Arama değerinizle değiştirin

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.