Tell me more ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I have a search problem :(

I have 4 text columns, mix of Varchar and Text and I need to find rows in a table where all words searched for are present across the 4 columns.

The 4 columns are :

name
type
keywords
description

So if someone searches for "london wildlife museum" it would only return rows where all words were found across the 4 columns

Current code to manage the multiple words is

$words = $_GET['freetext'];
if(empty($words)){
//redirect somewhere else!
}
$parts = explode(" ",trim($words));
$clauses1=array();
foreach ($parts as $part){
    //function_description in my case ,  replace it with whatever u want in ur table
    $clauses1[]="vname LIKE '%" . mysql_real_escape_string($part) . "%'";
}
$clause1=implode(' OR ' ,$clauses1);

$parts = explode(" ",trim($words));
$clauses2=array();
foreach ($parts as $part){
    //function_description in my case ,  replace it with whatever u want in ur table
    $clauses2[]="vtype LIKE '%" . mysql_real_escape_string($part) . "%'";
}
$clause2=implode(' OR ' ,$clauses2);

$parts = explode(" ",trim($words));
$clauses3=array();
foreach ($parts as $part){
    //function_description in my case ,  replace it with whatever u want in ur table
    $clauses3[]="vdesc LIKE '%" . mysql_real_escape_string($part) . "%'";
}
$clause3=implode(' OR ' ,$clauses3);

$parts = explode(" ",trim($words));
$clauses4=array();
foreach ($parts as $part){
    //function_description in my case ,  replace it with whatever u want in ur table
    $clauses4[]="vkeywords LIKE '%" . mysql_real_escape_string($part) . "%'";
}
$clause4=implode(' OR ' ,$clauses4);
//select your condition and add "AND ($clauses)" .
$sql="SELECT vid, vname, vsuburb, vtype, vlogo, suburb.sname 
      FROM venue, suburb 
      WHERE
      venue.vsuburb = suburb.sid
      AND (($clause1) OR ($clause2) OR ($clause3) OR ($clause4))";

Obviously this creates a long list of OR's but I can't see how to only choose the rows where all words appear.

Any help would be appreciated

Cheers

share|improve this question
What have you tried? Show us how far you've gotten so we can help you out – jprofitt Aug 7 '12 at 23:58
Hi, added code used at the moment, cheers – Deepweb Aug 8 '12 at 0:09

migrated from stackoverflow.com Aug 8 '12 at 0:26

4 Answers

up vote 2 down vote accepted

I would suggest you have a look at MySQL's full-text search. Its a better approach then just combining each criteria. It do think it only works on MyISAM.

http://dev.mysql.com/doc/refman/5.0/en/fulltext-natural-language.html

share|improve this answer
and for innodb stuff look here: stackoverflow.com/questions/1381186/fulltext-search-with-innodb – Rogier Aug 8 '12 at 0:20
Hi @Rogier, I had tried with this with no success, but went back and have managed to get it working. Major issue getting the Fulltext indexes across the columns but ended up working – Deepweb Aug 8 '12 at 8:46

Easy! Change your ORs to ANDs so that the final statement reads:

Your statement essentially needs to look like this:

SELECT * FROM table

WHERE (vname LIKE '%value1%' OR vtype LIKE '%value1%' OR vdesc LIKE '%value1%')

AND (vname LIKE '%value2%' OR vtype LIKE '%value2%' OR vdesc LIKE '%value2%')

AND (vname LIKE '%value3%' OR vtype LIKE '%value3%' OR vdesc LIKE '%value3%')

so it ensures that value1 exists in one of the 4 fields, that value2 exists in one of the 4 fields, and value3 exists in one of the 4 fields, etc.

If that's not sufficient, use PHP to iterate a larger number of results and filter down the results.

$words = $_GET['freetext'];
if(empty($words)){
//redirect somewhere else!
}
$parts = explode(" ",trim($words));
$clauses1=array();
foreach ($parts as $part){
    //function_description in my case ,  replace it with whatever u want in ur table
    $clauses1[]="vname LIKE '%" . mysql_real_escape_string($part) . "%'";
}
$clause1=implode(' AND ' ,$clauses1);

$parts = explode(" ",trim($words));
$clauses2=array();
foreach ($parts as $part){
    //function_description in my case ,  replace it with whatever u want in ur table
    $clauses2[]="vtype LIKE '%" . mysql_real_escape_string($part) . "%'";
}
$clause2=implode(' AND ' ,$clauses2);

$parts = explode(" ",trim($words));
$clauses3=array();
foreach ($parts as $part){
    //function_description in my case ,  replace it with whatever u want in ur table
    $clauses3[]="vdesc LIKE '%" . mysql_real_escape_string($part) . "%'";
}
$clause3=implode(' AND ' ,$clauses3);

$parts = explode(" ",trim($words));
$clauses4=array();
foreach ($parts as $part){
    //function_description in my case ,  replace it with whatever u want in ur table
    $clauses4[]="vkeywords LIKE '%" . mysql_real_escape_string($part) . "%'";
}
share|improve this answer
Wouldn't this make it so it returns only when a single column has every word in the search query, not where every search query is found in a whole row? – Johnnyoh Aug 8 '12 at 0:16
Hi there, thanks for the response, but that requires that all words appear in one column together rather than spanned over all columns. – Deepweb Aug 8 '12 at 0:17
Hi @Johnnyoh yes, that's the problem – Deepweb Aug 8 '12 at 0:18
Look at my answer Deepweb. – Johnnyoh Aug 8 '12 at 0:19

Change the ORs to ANDs on the final SQL statement.

$sql="SELECT vid, vname, vsuburb, vtype, vlogo, suburb.sname 
      FROM venue, suburb 
      WHERE
      venue.vsuburb = suburb.sid
      AND (($clause1) AND ($clause2) AND ($clause3) AND ($clause4))";

This would make the final query look something like this:

SELECT vid, vname, vsuburb, vtype, vlogo, suburb.sname 
FROM venue, suburb 
WHERE
venue.vsuburb = suburb.sid
AND
(vname = 'london' OR vname = 'wildlife' OR vname='museum') AND (vtype = 'london' OR vtype = 'wildlife' OR vtype ='museum') AND (vkeywords='london' OR vkeywords='wildlife' OR vkeywords='museum') AND (vdescription='london' OR vdescription='wildlife' OR vdescription='museum')
share|improve this answer
Sorry, @johnnyoh this means that one word of the phrase must appear in all columns – Deepweb Aug 8 '12 at 0:55

+1 to @Rogier, full-text search seems the best solution. Another idea could be concatenating the name, type, keywords and description attributes and searching in the concatenated attribute:

SELECT ..., CONCAT_WS(' ', name, type, keywords, description) AS c
FROM ...
WHERE c LIKE '%value1%' AND c LIKE '%value2%' ...

It may possible to store the result of concatenation in a persistent attribute etc.

share|improve this answer
Concat is a good idea, fulltext using MATCH is working now – Deepweb Aug 8 '12 at 8:47

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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