Display post matching the exact search term

Here’s a solution that I came up with that uses a hidden field on the search form which we will later check in our filters to modify WP’s default search/redirect behavior.

This is the search form I used. Note the addition of the hidden field barcode-reader with a value of 1. I added the search form to a page template for testing and demonstration purposes.

<form role="search" method="get" class="search-form" action="<?php echo esc_attr( home_url( "https://wordpress.stackexchange.com/" ) ); ?>">
    <label>
    <span class="screen-reader-text"><?php echo _x( 'Search for:', 'screen reader search label', 'textdomain' ); ?></span>
    <div class="input-group">
        <input type="search" class="search-field input-group-field" placeholder="<?php echo esc_attr_x( 'Search...', 'search placeholder', 'textdomain' ); ?>" value="<?php echo get_search_query(); ?>" name="s" title="<?php echo esc_attr_x( 'Search for:', 'textdomain' ); ?>" />

        <input type="hidden" value="1" name="barcode-reader" />

        <div class="input-group-button">
            <button type="submit" class="search-submit button" value="<?php echo esc_attr_x( 'Search', 'search button', 'textdomain' ); ?>">Search</button>
        </div>
    </div>
    </label>
</form>

(It’s not clear from the original post, but from the research I’ve done, it should be possible for the barcode reader to send a parameter via $_GET which could be used instead of the hidden field I’ve used for testing.)

Here, we will check if this search is being performed by the barcode scanner using the helper function wpse_is_barcode_search(). The helper function is used so that we don’t have to duplicate the logic across the multiple hooks that are used in this implementation.

If we are doing a search using the scanner, we wire up our posts_search filter and do some modifications to the WP_Query instance before the query is performed.

/**
 * Wire up our posts_search filter and change the $query.
 * 
 * @param WP_Query $query The WP_Query instance (passed by reference).
 */
add_action( 'pre_get_posts', 'wpse_barcode_search_pre_get_posts' );
function wpse_barcode_search_pre_get_posts( $query ) {
    // Bail if this search is not performed by the barcode scanner.
    if ( ! wpse_is_barcode_search( $query ) ) {
        return;
    }

    // Wire up the filter to modify the search SQL.
    add_filter( 'posts_search', 'wpse_barcode_posts_search', 10, 2 );

    // Set posts_per_page to one since we're only looking for an exact match anyway.
    $query->set( 'posts_per_page', 1 );

    // (suggestion) Limit search to a particular post type.
    // $query->set( 'post_type', array( 'product', ) );
}

Here is the filter that actually modifies the search SQL. Since we only want this to fire once, the filter is immediately unooked.

/**
 * Modify search query so that the search term looks for an exact
 * match with the post title.
 *
 * @param string   $search Search SQL for WHERE clause.
 * @param WP_Query $wp_query   The current WP_Query object.
 */
function wpse_barcode_posts_search( $search,  $wp_query ) {
    // We only want the search filter to fire once, so unhook it.
    remove_filter( 'posts_search', 'wpse_barcode_posts_search', 10, 2 );

    // Change the search SQL so that it checks if the search is equal to the post title.
    $search = " AND (wp_posts.post_title="" . esc_sql( $wp_query->query_vars["s'] ) . "')";

    return $search;
}

This code redirects the user to the permalink of our found post rather than the search results page.

/**
 * Redirect to the permalink of the searched item if it was found using
 * the barcode search.
 *
 * @link https://wordpress.stackexchange.com/a/128578/2807
 */
add_action( 'template_redirect', 'wpse_barcode_search_success_redirect' );
function wpse_barcode_search_success_redirect() {
    global $wp_query;
    if ( ! wpse_is_barcode_search( $wp_query ) ) {
        return;
    }

    // If we have a post, redirect to it.
    if ( '1' === $wp_query->found_posts ) {
        wp_redirect( get_permalink( $wp_query->posts['0']->ID ) );
        exit;
    }
}

Finally, here is the helper function used to determine if we are performing a search using the barcode scanner. This should be modified so that it works with the scanner used ( particularly, the check using $_REQUEST['barcode-reader'] ).

/**
 * Helper function used to determine if a search is performed using
 * the barcode scanner.
 *
 * @uses array $_REQUEST
 * @param object $query
 * @return bool
 */
function wpse_is_barcode_search( $query ) {
    // Bail if $query is not an instance of WP_Query.
    if ( ! ( $query instanceof WP_Query ) ) {
        return false;
    }

    // Bail if this is the admin area.  
    if ( $query->is_admin() ) {
        return false;
    }

    // Bail if this is not the search page or main query.
    if ( ! $query->is_search() || ! $query->is_main_query() ) {
        return false;
    }

    // Bail if this is not our special barcode search.
    if ( ! isset( $_REQUEST['barcode-reader'] ) || '1' !== $_REQUEST['barcode-reader'] ) {
        return false;
    }

    return true;
}