Verwenden einer Meta-Abfrage ('meta_query') mit einer Suchabfrage ('s')


25

Es wurde versucht, eine Suche zu erstellen, die nicht nur die Standardeinstellungen (Titel, Inhalt usw.) durchsucht, sondern auch ein bestimmtes benutzerdefiniertes Feld.

Meine aktuelle Anfrage:

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

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

Dies gibt Beiträge zurück, die sowohl der Suchabfrage als auch der Metaabfrage entsprechen, aber ich möchte auch, dass Beiträge zurückgegeben werden, bei denen einfach einer von beiden übereinstimmt.

Irgendwelche Ideen?


Was Sie tun möchten, ist nicht mit nur einer Suchanfrage möglich. Sie müssten beide Abfragen separat ausführen und dann zusammenführen. Dies wurde in dieser anderen Antwort beschrieben. Es sollte Ihnen dabei helfen, wie es geht. wordpress.stackexchange.com/questions/55519/…
Nick Perkins

Antworten:


19

Ich habe stundenlang nach einer Lösung für dieses Problem gesucht. Das Zusammenführen von Arrays ist nicht der richtige Weg, insbesondere wenn die Abfragen komplex sind und Sie in Zukunft in der Lage sein müssen, Metaabfragen hinzuzufügen. Die einfach schöne Lösung besteht darin, 's' in eine zu ändern, mit der sowohl nach Titeln als auch nach Metafeldern gesucht werden kann.

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;
        });
    }
});

Verwendung:

$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)

Dies ist der richtige Weg,
Zorox

Fantastisch, das hat bei mir sehr gut funktioniert. Tolle Lösung! Vielen Dank!
Fabiano

Dies ist eine Abfrage, keine Suche. Wenn Sie Plugins haben, die bei der Suche funktionieren, funktioniert dies nicht. Liege ich falsch?
marek.m

5

Mit einer geänderten Version dieser Antwort kann viel Code reduziert werden .

$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 );

Dies hat bei mir sehr gut funktioniert (zumal ich WP_Query anstelle von get_posts verwenden musste). Ich musste Ihre post_count-Zeile ändern, um zu sein: $result->post_count = count( $result->posts );weil ich sonst nur 1 Ergebnis erhielt.
GreatBlakes

4

Ich habe @Stabir Kira Antwort ein wenig optimiert

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');

Jetzt können Sie nach (Titel, Inhalt, Auszug) oder (Metafeld) oder (beiden) suchen.


3

Wie pro Nick Perkins' Vorschlag, hatte ich zwei Abfragen zu fusionieren , wie so:

$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
Geht das noch nicht, ohne 2016 zu fusionieren? Ich bearbeite die Suchanfrage über pre_get_posts, also ist dies wirklich keine Option ...
trainoasis

@trainoasis Ich glaube nicht. Ich habe es seit 2 Stunden ausprobiert und eine Google-Suche hat mich hierher gebracht.
Umair Khan Jadoon

2

Nun, es ist eine Art Hack, aber es funktioniert. Sie müssen den Filter posts_clauses hinzufügen. Diese Filterfunktionsprüfung für ein beliebiges Abfragewort ist im benutzerdefinierten Feld "speel" vorhanden, und die verbleibende Abfrage bleibt erhalten.

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

ich hatte das gleiche problem, für meine neue website habe ich gerade einen neuen meta "title" hinzugefügt:

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)); 
}

Und dann ... füge einfach so etwas hinzu:

$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;

Was halten Sie von dieser Problemumgehung?


1
Das ist eigentlich nicht schlecht, erfordert aber, dass Sie entweder alle vorhandenen Beiträge erneut speichern oder sie verwenden, bevor Sie mit dem Hinzufügen von Inhalten beginnen.
Berend

@Berend könntest du wahrscheinlich eine Funktion schreiben, die alle Beiträge und Schleifen durchläuft und die post_meta für jeden aktualisiert. Fügen Sie es in eine benutzerdefinierte Vorlage oder Funktion ein, führen Sie es einmal aus und verwerfen Sie es dann.
Slam

1

Alle oben genannten Lösungen geben nur dann Ergebnisse zurück, wenn im Speel-Metaschlüssel eine Übereinstimmung vorliegt. Wenn Sie Ergebnisse an anderer Stelle haben, jedoch nicht in diesem Bereich, erhalten Sie nichts. Niemand will das.

Ein linker Join ist erforderlich. Im Folgenden wird eine erstellt.

           $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

Dies ist eine großartige Lösung, aber Sie müssen eine Sache beheben. Wenn Sie 'post__in' aufrufen, müssen Sie ein Array von IDs festlegen, und $ unique ist ein Array von Posts.

Beispiel:

$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 Antwort funktioniert gut, aber es wird nur durch die Meta-und Post-Titel suchen. Wenn Sie möchten, dass Meta, Titel und Inhalt durchsucht werden, finden Sie hier die geänderte Version.

    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;
        });
      }
    });

Und hier ist seine Verwendung:

$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'
  )
);

Ersetzen Sie $get['search']durch Ihren Suchwert

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.