Home > Mobile >  Highlight Multiple Search Terms In Order Given
Highlight Multiple Search Terms In Order Given

Time:11-06

Have seen simple preg_replace (or even just str_replace) examples of adding <span > around an array of searched words. But a lot of search queries will search for terms in the order written:

Search: blue shoes

MySQL query: SELECT * FROM my_table WHERE title LIKE '%blue%shoes%'

Matches:

MATCH - "Don't step on my blue suede shoes"

NO MATCH - "... his fancy shoes and his baby blues..." (the keywords are not in the right order)

MATCH - "blue shoes under the blue sky" (second "blue" should not be highlighted)

So the SERP should highlight the matches the same way. Is there a REGEX way that will accomplish this with n search terms in order? Or does this need to be handled with a PHP loop somehow?

Incorrect Attempt:

foreach ( ['blue', 'shoes'] as $term ) {
    $result = str_replace($term, "<span style=\"background:yellow\">$term</span>", $title);
}
echo $result;
// This will highlight any instances of "blue" or "shoes" in any order in the result,
// even though the MySQL query searched for those terms in that specific order

CodePudding user response:

You have to generate the regexp pattern with PHP:


function highlightMatches(array $searchTerms, string $string): string {
    $result = $string;

    foreach ($searchTerms as $i => $term) {
        $previous = array_map(function($item) {
            return "($item)";
        }, array_slice($searchTerms, 0, $i   1));
        
        $pattern = implode($previous, ". ");
        
        $matches = [];
        preg_match("/$pattern/", $string, $matches);
        
        $lastMatch = array_pop($matches);
        
        if ($lastMatch) {
            $result = str_replace($lastMatch, "<span style=\"color: red;\">$lastMatch</span>", $result);
        }
    }
    
    return $result;
}


$searchTerms = ["first", "second", "third"];

$strings = [
    "first second third",
    "third second first",
    "second third first",
    "third first second",
];

foreach ($strings as $str) {
    echo $str . ":\t" . highlightMatches($searchTerms, $str) . "\n";
}



CodePudding user response:

You mentioned [mysql], so I present what probably comes quite close to your request in finding the row in question.

WHERE MATCH(col) AGAINST( "blue shoes" IN BOOLEAN MODE)

If your question is only about PHP code, then remove the [mysql] tag.

CodePudding user response:

Another version using implode() for building the search pattern and preg_replace() for doing the actual replacing:

$haystacks = [
    "Don't step on my blue suede shoes",
    "... his fancy shoes and his baby blues...",
    "blue shoes under the blue sky",
];
$needles = ['blue', 'shoes'];
$format = '<span style="background:red">%s</span>';

function highlightMatches($needles, $haystack, $format)
{
    $search = '('.implode(')(. ?)(', $needles).')';
    $replace = '';
    $gaps_count = count($needles) - 1;
    foreach ($needles as $k => $needle) {
        $replace .= sprintf($format, '\\'.($k * 2   1));
        if ($k < $gaps_count) {
            $replace .= '\\'.($k *2   2);
        }
    }

    return preg_replace("/$search/", $replace, $haystack);
}


foreach ($haystacks as $haystack) {
    echo highlightMatches($needles, $haystack, $format)."<br>";
}

If the database query is available for editing, I'd try to use the database query to do the highlighting.

  • Related