GET FREE VERSION GET THE PRO VERSION

How To Filter Search Results Based On Search Query

Use custom code to exclude the products on your choice depending on users search query terms.

In this article

Overview

In this article we will describe one specific problem and its solution, namely, how to exclude certain products from search depending on search words.

For example - we have products called Phone and Phone case. By default when a user searches for phone he will see both products in the plugin search results. But we want to show only the product Phone when users only use the search term phone. This is possible to achieve by using code snippets that we provide below.

Search results for 'phone' query

Search results for 'phone' query

Search results for 'phone case' query

Search results for 'phone case' query

Exclude products by product ID

For example we have product Phone with ID 1 and product Phone case with ID 2.
When a user searches for phone we want to exclude the product Phone case.
When a user searches for phone case we want to exclude the product Phone.

To get such behaviour we can use the following code snippet:

class AWS_Exclude_By_terms {

    public $terms = '';

    public function __construct() {
        add_filter( 'aws_search_terms', array( $this, 'aws_search_terms' ) );
        add_filter( 'aws_search_query_array', array( $this, 'aws_search_query_array' ) );
    }

    function aws_search_terms( $terms ) {
        $this->terms = $terms;
        return $terms;
    }

    function aws_search_query_array( $query ) {

        $exclude_array = array(
            'phone' => array( 2 ),
            'phone case' => array( 1 )
        );

        $exclude_ids_array = array();

        if ( $this->terms ) {
            foreach ( $exclude_array as $term => $ids ) {

                $term_arr = explode(' ', $term );
                $exclude = false;

                if ( $term_arr && count($term_arr) === count( $this->terms ) ) {
                    foreach ( $term_arr as $term_i ) {
                        if ( array_search( $term_i, $this->terms ) !== false ) {
                            $exclude = true;
                        } else {
                            $exclude = false;
                        }
                    }
                }

                if ( $exclude ) {
                    $exclude_ids_array = array_merge($exclude_ids_array, $ids);
                }

            }
        }

        if ( ! empty( $exclude_ids_array ) ) {
            $ids = implode( ',', $exclude_ids_array );
            $query['exclude_products'] = " AND ( id NOT IN ( {$ids} ))";
        }

        return $query;

    }

}

new AWS_Exclude_By_terms();

edit lines

$exclude_array = array(
    'phone' => array( 2 ),
    'phone case' => array( 1 )
);

to exclude more products based on search terms.

Exclude products by product category

Let's update our example and now exclude not single products, but all products from certain categories.
For example we have product category Phone with ID 1 and product category Phone case with ID 2.
When a user searches for phone we want to exclude all products with category Phone case.
When a user searches for phone case we want to exclude all products with category Phone.

In such case code snippet can looks like that:

class AWS_Exclude_By_terms {

    public $terms = '';

    public function __construct() {
        add_filter( 'aws_search_terms', array( $this, 'aws_search_terms' ) );
        add_filter( 'aws_search_query_array', array( $this, 'aws_search_query_array' ) );
    }

    function aws_search_terms( $terms ) {
        $this->terms = $terms;
        return $terms;
    }

    function aws_search_query_array( $query ) {

        $exclude_array = array(
            'phone' => array( 2 ),
            'phone case' => array( 1 )
        );

        $exclude_ids_array = array();

        if ( $this->terms ) {
            foreach ( $exclude_array as $term => $ids ) {

                $term_arr = explode(' ', $term );
                $exclude = false;

                if ( $term_arr && count($term_arr) === count( $this->terms ) ) {
                    foreach ( $term_arr as $term_i ) {
                        if ( array_search( $term_i, $this->terms ) !== false ) {
                            $exclude = true;
                        } else {
                            $exclude = false;
                        }
                    }
                }

                if ( $exclude ) {
                    $exclude_ids_array = array_merge($exclude_ids_array, $ids);
                }

            }
        }

        if ( ! empty( $exclude_ids_array ) ) {
            global $wpdb;

            $taxonomies = implode( ',', $exclude_ids_array );

            $query['exclude_products'] = " AND ( id NOT IN (
                   SELECT $wpdb->posts.ID
                   FROM $wpdb->term_relationships
                   JOIN $wpdb->posts
                   ON ( $wpdb->term_relationships.object_id = $wpdb->posts.post_parent OR $wpdb->term_relationships.object_id = $wpdb->posts.ID )
                   WHERE $wpdb->term_relationships.term_taxonomy_id IN ( 
                       select term_taxonomy_id from $wpdb->term_taxonomy WHERE term_id IN ({$taxonomies})
                   )
                ))";

        }

        return $query;

    }

}

new AWS_Exclude_By_terms();

As and in previous example edit line

$exclude_array = array(
    'phone' => array( 2 ),
    'phone case' => array( 1 )
);

to set your own rules based on different search terms.

Exclude archive pages

Now lets create a bit different exclude logic and filter not products but archive pages.

For example we have product category Phone with ID 1 and product category Phone case with ID 2.
When a user searches for phone we want to exclude product category Phone case from search results.
When a user searches for phone case we want to exclude product category Phone from search results.

Search results for 'phone' query

Search results for 'phone' query

Search results for 'phone case' query

Search results for 'phone case' query

To create such logic you just need to use the following code snippet:

class AWS_Exclude_By_terms {

    public $terms = '';

    public function __construct() {
        add_filter( 'aws_search_terms', array( $this, 'aws_search_terms' ) );
        add_filter( 'aws_terms_search_query', array( $this, 'aws_terms_search_query' ), 10, 2  );
    }

    function aws_search_terms( $terms ) {
        $this->terms = $terms;
        return $terms;
    }

    function aws_terms_search_query( $sql, $taxonomy ) {

        $filter_taxonomy = 'product_cat';

        if ( array_search( $filter_taxonomy, $taxonomy ) === false ) {
            return $sql;
        }

        $exclude_array = array(
            'phone' => array( 2 ),
            'phone case' => array( 1 )
        );

        $exclude_ids_array = array();

        if ( $this->terms ) {
            foreach ( $exclude_array as $term => $ids ) {

                $term_arr = explode(' ', $term );
                $exclude = false;

                if ( $term_arr && count($term_arr) === count( $this->terms ) ) {
                    foreach ( $term_arr as $term_i ) {
                        if ( array_search( $term_i, $this->terms ) !== false ) {
                            $exclude = true;
                        } else {
                            $exclude = false;
                        }
                    }
                }

                if ( $exclude ) {
                    $exclude_ids_array = array_merge($exclude_ids_array, $ids);
                }

            }
        }

        if ( ! empty( $exclude_ids_array ) ) {
            global $wpdb;
            $ids = implode( ',', $exclude_ids_array );
            $query = " AND ( " . $wpdb->terms . ".term_id NOT IN ( {$ids} ))";
            $sql = str_replace( 'WHERE 1 = 1', 'WHERE 1 = 1' . $query, $sql );
        }
        
        return $sql;

    }

}

new AWS_Exclude_By_terms();

As always, edit the following line to adjust that filtering logic.

$exclude_array = array(
    'phone' => array( 2 ),
    'phone case' => array( 1 )
);