I have installed the Whole Word Search mod (http://www.zen-cart.com/downloads.php?do=file&id=712)--or rather, since all it does is change two lines of code in one file (includes/modules/pages/advanced_search_result/header_php.php), I have made those changes to the 1.5.1 version of that file. While the result works for the most part, for some reason it treats exact match searches, i.e., keywords between double quotation marks, as if the keywords were separated by the Boolean operator "or" instead. For example, if you modify the demo version, a search for "vengeance weapon" pulls up both "Die Hard with a Vengeance" and "Lethal Weapon," when it shouldn't return either one. It does this, incidentally, regardless of whether Admin/Configuration/My Store/Default Search Operator is set to "and" or "or," so it isn't just a question of its somehow stripping out the quotation marks and going for the default. I have gone through the code in header_php.php and the relevant bit in functions_general.php (function zen_parse_search_string()), and for the life of me, I cannot figure out why this is happening. Does anyone have any idea what's going on here?
What the mod does to header_php.php is to take this bit:
$where_str .= "(pd.products_name LIKE '%:keywords%'
OR p.products_model
LIKE '%:keywords%'
OR m.manufacturers_name
LIKE '%:keywords%'";
and change it to this:
$where_str .= "(MATCH(pd.products_name) AGAINST ('%:keywords%' IN BOOLEAN MODE)
OR p.products_model
LIKE '%:keywords%'
OR m.manufacturers_name
LIKE '%:keywords%'";
Then a few lines later it does the same thing for the product-description search. I don't see why these alterations should change the way it treats exact match searches, since those are supposed to exist in MySQL's Boolean Mode, too.
Here is the relevant part of functions_general.php, in case that helps:
// Parse search string into indivual objects
function zen_parse_search_string($search_str = '', &$objects) {
$search_str = trim(strtolower($search_str));
// Break up $search_str on whitespace; quoted string will be reconstructed later
$pieces = preg_split('/[[:space:]]+/', $search_str);
$objects = array();
$tmpstring = '';
$flag = '';
for ($k=0; $k<count($pieces); $k++) {
while (substr($pieces[$k], 0, 1) == '(') {
$objects[] = '(';
if (strlen($pieces[$k]) > 1) {
$pieces[$k] = substr($pieces[$k], 1);
} else {
$pieces[$k] = '';
}
}
$post_objects = array();
while (substr($pieces[$k], -1) == ')') {
$post_objects[] = ')';
if (strlen($pieces[$k]) > 1) {
$pieces[$k] = substr($pieces[$k], 0, -1);
} else {
$pieces[$k] = '';
}
}
// Check individual words
if ( (substr($pieces[$k], -1) != '"') && (substr($pieces[$k], 0, 1) != '"') ) {
$objects[] = trim($pieces[$k]);
for ($j=0; $j<count($post_objects); $j++) {
$objects[] = $post_objects[$j];
}
} else {
/* This means that the $piece is either the beginning or the end of a string.
So, we'll slurp up the $pieces and stick them together until we get to the
end of the string or run out of pieces.
*/
// Add this word to the $tmpstring, starting the $tmpstring
$tmpstring = trim(preg_replace('/"/', ' ', $pieces[$k]));
// Check for one possible exception to the rule. That there is a single quoted word.
if (substr($pieces[$k], -1 ) == '"') {
// Turn the flag off for future iterations
$flag = 'off';
$objects[] = trim($pieces[$k]);
for ($j=0; $j<count($post_objects); $j++) {
$objects[] = $post_objects[$j];
}
unset($tmpstring);
// Stop looking for the end of the string and move onto the next word.
continue;
}
// Otherwise, turn on the flag to indicate no quotes have been found attached to this word in the string.
$flag = 'on';
// Move on to the next word
$k++;
// Keep reading until the end of the string as long as the $flag is on
while ( ($flag == 'on') && ($k < count($pieces)) ) {
while (substr($pieces[$k], -1) == ')') {
$post_objects[] = ')';
if (strlen($pieces[$k]) > 1) {
$pieces[$k] = substr($pieces[$k], 0, -1);
} else {
$pieces[$k] = '';
}
}
// If the word doesn't end in double quotes, append it to the $tmpstring.
if (substr($pieces[$k], -1) != '"') {
// Tack this word onto the current string entity
$tmpstring .= ' ' . $pieces[$k];
// Move on to the next word
$k++;
continue;
} else {
/* If the $piece ends in double quotes, strip the double quotes, tack the
$piece onto the tail of the string, push the $tmpstring onto the $haves,
kill the $tmpstring, turn the $flag "off", and return.
*/
$tmpstring .= ' ' . trim(preg_replace('/"/', ' ', $pieces[$k]));
// Push the $tmpstring onto the array of stuff to search for
$objects[] = trim($tmpstring);
for ($j=0; $j<count($post_objects); $j++) {
$objects[] = $post_objects[$j];
}
unset($tmpstring);
// Turn off the flag to exit the loop
$flag = 'off';
}
}
}
}
// add default logical operators if needed
$temp = array();
for($i=0; $i<(count($objects)-1); $i++) {
$temp[] = $objects[$i];
if ( ($objects[$i] != 'and') &&
($objects[$i] != 'or') &&
($objects[$i] != '(') &&
($objects[$i+1] != 'and') &&
($objects[$i+1] != 'or') &&
($objects[$i+1] != ')') ) {
$temp[] = ADVANCED_SEARCH_DEFAULT_OPERATOR;
}
}
$temp[] = $objects[$i];
$objects = $temp;
$keyword_count = 0;
$operator_count = 0;
$balance = 0;
for($i=0; $i<count($objects); $i++) {
if ($objects[$i] == '(') $balance --;
if ($objects[$i] == ')') $balance ++;
if ( ($objects[$i] == 'and') || ($objects[$i] == 'or') ) {
$operator_count ++;
} elseif ( ($objects[$i]) && ($objects[$i] != '(') && ($objects[$i] != ')') ) {
$keyword_count ++;
}
}
if ( ($operator_count < $keyword_count) && ($balance == 0) ) {
return true;
} else {
return false;
}
}


Reply With Quote
