5

I'm currently running Wordpress on version 4.6.1 and I'm attempting to search for posts that contains the character - (hyphen). However, the search parameter is taking my hyphen for a negation.

From the WP_Query documentation:

Prepending a term with a hyphen will exclude posts matching that term. Eg, 'pillow -sofa' will return posts containing 'pillow' but not 'sofa' (available since Version 4.4).

Here's my query:

$query = new WP_Query(array(
    'post_type' => array('product', 'product_variation'),
    's' => '-',
    'posts_per_page' => 36
));

I've tried to escape the hyphen by doing:

's' => '\-'

However, the result stays the same (var_dump($query->request)):

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID
FROM wp_posts
WHERE
    1=1
    AND (((wp_posts.post_title NOT LIKE '%%')
    AND (wp_posts.post_excerpt NOT LIKE '%%')
    AND (wp_posts.post_content NOT LIKE '%%')))
    AND (wp_posts.post_password = '')
    AND wp_posts.post_type IN ('product', 'product_variation')
    AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'acf-disabled')
ORDER BY
    wp_posts.post_date DESC
LIMIT 0, 36
  • 1
    very good and real question! – prosti Dec 7 '16 at 10:59
4

One approach is to modify the exclusion prefix through the wp_query_search_exclusion_prefix filter that's supported in WP 4.7+. See ticket #38099.

Here's an example how we can change it from - to e.g. !:

add_filter( 'wp_query_search_exclusion_prefix', function( $prefix )
{
    return '!'; // adjust to your needs (default is -)
} );

where we would use !apple instead of -apple to exclude apple from the search results. Then you can search by -.

It looks like the exclusion prefix must be a single character (or empty to disable this feature), because of this check in the core:

// If there is an $exclusion_prefix, terms prefixed with it should be excluded.
$exclude = $exclusion_prefix && ( $exclusion_prefix === substr( $term, 0, 1 ) );
if ( $exclude ) {
    $like_op  = 'NOT LIKE';
    $andor_op = 'AND';
    $term     = substr( $term, 1 );
} else {
    $like_op  = 'LIKE';
    $andor_op = 'OR';
}

Otherwise it sounds like a bug, not to be able to search only for the term exclusion prefix.

I wonder if it would be better to add an extra $exclusion_prefix !== $term check to support that:

$exclude = $exclusion_prefix 
    && $exclusion_prefix !== $term 
    && ( $exclusion_prefix === mb_substr( $term, 0, 1 ) );
if ( $exclude ) {
    $like_op  = 'NOT LIKE';
    $andor_op = 'AND';
    $term     = mb_substr( $term, 1 );
} else {
    $like_op  = 'LIKE';
    $andor_op = 'OR';
}

where we would also use mb_substr() instead of substr() for a wider char support for the exclusion prefix.

I guess I should just create ticket for this ...

  • 1
  • mb_substr, in PHP < 7 that function needs a treatment stackoverflow.com/questions/13953248/…. – prosti Dec 7 '16 at 14:09
  • thanks @prosti, I guess get_option( 'blog_charset' ) could be an option for that, The mb_substr() is actually used in core in few places without the encoding argument, e.g. here and here. For setups without mb_substr and mb_strlen(), WordPress ships with fallbacks with the same function names in /wp-includes/compat.php. – birgire Dec 7 '16 at 14:29
  • it's all about that bass, about that base, which is >>PHP is not a superior lang at the moment. – prosti Dec 7 '16 at 14:32
  • yes, general encoding issues can be hard to deal with. Maybe we should consider to start writing in pure binary code instead ... on a second thought I'm afraid that could give far worse translation problems ;-) @prosti – birgire Dec 7 '16 at 14:43
2

You can use the fact that NOT LIKE '%%' is very unique. It can happen only in cases where you search for -.

add_filter( 'posts_where', function( $where, $q )
{        
    $where = str_replace( "NOT LIKE '%%'", "LIKE '%-%'", $where);           
    return $where;

}, 10, 2 );

$args = array(
    'post_type'     => 'post',
    'post_status'   => 'publish',
    's' => '-'

);

$q = new WP_Query( $args );
var_dump($query->request);

The resulting query will be:

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID
FROM wp_posts 
WHERE 1=1 
AND (((wp_posts.post_title LIKE '%-%')
AND (wp_posts.post_excerpt LIKE '%-%')
AND (wp_posts.post_content LIKE '%-%'))) 
AND wp_posts.post_type = 'post'
AND ((wp_posts.post_status = 'publish')) 
ORDER BY wp_posts.post_date DESC
LIMIT 0, 10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.