I have a simple search feature that will search a table for a hit. Basically the one flaw of this search is that it tries to match exactly the string. I want it to take a string of say "large red bird" and search "large", "red" and "bird" separately against the table. here's my search query...
$result = mysql_query("SELECT * FROM files
WHERE (tags LIKE '%$search_ar%' OR
name LIKE '%$search_ar%' OR
company LIKE '%$search_ar%' OR
brand LIKE '%$search_ar%')
$str_thera $str_global $str_branded $str_medium $str_files");
any Ideas? thanks
Edit
OK here's my updated query but it doesnt return anything.
$result = mysql_query("SELECT * FROM files
WHERE MATCH(tags, name, company, brand)
AGAINST ('$seach_ar' IN boolean MODE)");
A fulltext search will indeed help, given you do have a lot of data to search by. A good reference for the full text can be found here: http://forge.mysql.com/w/images/c/c5/Fulltext.pdf
The reason I say a lot of data, or a fair amount, is that if the search results yields above a certain percentage of returned rows to total rows, nothing is returned.
If you want to continue using the LIKE method, you can. You just have to seperate the words (explode) and the join them in the sql query using AND:
...(tags LIKE '%$search_ar[0]%' AND tags LIKE '%$search_ar[1]%') OR ....
In a fashion like that. This method can get overly complicated especially if say, you want to return matches which has any of the words and not all of them. So yea, it will take some customization to do and to automate, but it is possible.
If, for whatever reason, you can't use full text searching (nice idea, Nick and Brad, but only MyISAM supports it, and from what I hear, it's not really all that good), it's not too hard to rig up basic searching with explode() and implode():
$search_arr = explode(' ',$search_str);
$search_str = implode('%',$search_arr);
//query where fields like $search_arr
Or:
$search_arr = explode(' ', $search_str);
$sql = 'select * from files where ';
foreach($search_arr = $term){
$sql .= "tags like '%{$term}%' or " //fill in rest of fields
}
As a side note: If you plan on growing your searching and can't use MySQL's full text search, I recommend checking out one of the search servers, such as Solr or Lucene.
Related
I have a table with columns named: "ID, Track Name, City, County" among others. I'm looking for a way for a user to query the database and get results back relating to those 4 columns, with the most relevant results appearing at the top.
Say there was a row with: "1, Track Number One, Taunton, Somerset"
and a row with: "2, Circuit Number Two, Taunton, Somerset"
and the user searched "circuit in taunton"
Obviously I would like both rows to show, but I would like the second row to appear at the top because it has matched 2 keywords.
This is the current code I am using but it is only working on one column or another and if the user queried "karting in taunton" it would not work.
$searchterm = htmlspecialchars($getArray['searchterm']);
$searchterm = $mysqli->real_escape_string($searchterm);
$terms = explode(' ', $searchterm);
$bits = array();
$cityBits = array();
$countyBits = array();
foreach ($terms as $term) {
$bits[] = "name LIKE '%".mysql_real_escape_string($term)."%'";
}
foreach ($terms as $cityTerms) {
$cityBits[] = "city LIKE '%".mysql_real_escape_string($cityTerms)."%'";
}
foreach ($terms as $countyTerms) {
$countyBits[] = "county LIKE '%".mysql_real_escape_string($countyTerms)."%'";
}
$this->query = "SELECT * FROM tracks WHERE (".implode(' AND ', $bits).")
union SELECT * FROM tracks WHERE (".implode(' AND ', $cityBits).")
union SELECT * FROM tracks WHERE (".implode(' AND ', $countyBits).")";
I know this code is not ideal but it all I've managed to get working at the moment.
Firstly, your requirement is a much better match for full text searching than "like" comparisons. As soon as you get this working, you'll want to boost "Taunton" over "Aunton" and then deal with different spellings, wildcards, and then you'll notice that the wildcards in your like statement slow the whole thing down. Full text searching does all that for you, and is pretty quick.
The specific answer to your question is a nasty one; I'm reluctant to give you code PHP code because I can't easily test it. I've created a little SQLFiddle to show how you can do it in SQL, though.
In essence, you need to turn your "hits" on individual searches into a derived table, and count each row's number of entries; the more times it turns up in your derived table, the more likely it is to be a "good" hit.
In one of the places i've been working at, i had to do a search engine to search songs into a database (so basically what you're trying to do). The solution i came up with was : when you insert a song into the database, you also put some relevant keywords which should make the song appear when you type them.
So what's happening when someone search for let's say : MJ, my code was searching for exact matches into the artist name / song title. If i had results, i was displaying them by alphabetical number or by whatever the user wanted to sort them by.
If no results were found (and here's the interesting part for you) my function was doing a query into the keywords of each songs. Once i had all the results and especially their associated keywords into an array i was just counting the number of keywords matching the query for each result and only then i could sort by relevance.
Hope this helps you, i guess you're only missing a "keywords" row in your db.
Your query is setup to return 2 identical rows if 2 matches are found. You might reword it to return the row once, along with an ORDER BY clause that counts the matches.
"SELECT * FROM tracks WHERE (".implode(' AND ', $bits).")
OR (".implode(' AND ', $cityBits).")
OR (".implode(' AND ', $countyBits).")
ORDER BY ((".implode(' AND ', $bits).")
+ (".implode(' AND ', $cityBits).")
+ (".implode(' AND ', $countyBits).")) DESC"
This isn't exact, because I'm not sure why you are imploding with "AND" rather than "OR", but you get the jist.
I am trying to search a table for multiple keywords. However, I am not looking for one keyword or even 10. It is around one thousand keywords. These keywords are also in a table and can be controlled. I would rather not hard-code these keywords into my SQL command...
The target table I am searching contains a lot of text and a cell could contain an entire sentences or paragraph... so doing something like a 'full text' search in mySQL seems like a good start.
Very similar to this question, but again, when I speak of multiple keywords, I mean hundreds to thousands. mysql FULLTEXT search multiple words
Can I dump my keyword table into an array and run a FULLTEXT search? Can this even be approached with mySQL or are there limits im not considering? Im open to other technology suggestions too. Sorry that I dont have code or errors to post. I am first trying to understand conceptually how to approach this. -tia
Recently i had to do a similar decision. I decided to go with lucene. I store the indexable fields in lucene, and return an id for mysql row.
Other choice is sphinx , a full tutorial can be found here.
See a related post here. And here.
SELECT * FROM articles WHERE body LIKE '%$keyword%';
you just need to use a for loop in your mysql query to read all keyworks.
its clear that you separated your keywords with comma or -
so you need to explode keyword by comma and put them in a variable .
for example :
$keywords = "key1,key2,key3,..."; // values come from keywords column from db.
now you just need to explode $keywords.
$keys=explode(',',$keywords);
and finally in your query you need to use a for loop:
$query = "SELECT * From targettable";
$i = 0;
foreach ($keys as $key)
{
$query .= "WHERE keywords LIKE '%".$key."%' )";
$i++;
}
i named your keywords column = "keywords".
also you can easily add your other condition into $query.
Watch out with mysql fulltext searches, if your result set is > 50% of the entire data set, mysql sees this is a failed search, and you wont get anything.
This sounds likely to occur if you have a hit list of 1000s of possible words.
I'd say you would be better off investigating the extraction of keywords from text (lets imagine they are articles) as they are stored.
Then store these keywords in their own table.
For best results though, you'd likely want to investigate Natural Language Processing and extract meaning from articles rather than just words.
My users put search terms in an input field. The script I'm using stores that in a $find variable. It then searches mysql like so:
"select * from mytable WHERE title LIKE '%$find%' or description LIKE '%$find%'";
I want to be able to pull a result, even if 1 word is in the title and another in the description, which currently doesn't happen. So.. I'm guessing I need to break the $find variable into an array with a function. After that, how would I do the search in mysql? Since I never know how many words will be in the search (they might decide to search for 8 words at once), how do I reference the array in the mysql query?
Thx in advance!
You should use a FULLTEXT index and build a proper boolean search query on it.
You MAY achieve the effect you want without it, but this would be very slow compared to using FULLTEXT index. You would have to use explode to get the list of words, then build a WHERE condition like concat(title, description) LIKE '%word1%' AND concat(title, description) LIKE '%word2%' etc.
(In my first answer I stated that achieving this effect is impossible without FULLTEXT. I was wrong and edited this answer.)
I am making a search function on a website to let users search values held in a mysql database.
As an example:
There are two products in this table.
One has a description value of "coke can 330ml" and the other "sprite can 330ml".
Now when searching for these items, say a user enters can as their string. Both items are displayed because they both contain can.
If the user enters coke, the coke can will be returned.
Now imagine that you had every can of soda ever made inside the database, each as a product.
Search functionality like this, would be a bit troublesome. say a user wanted only two products, these being the coke and sprite cans.
They would need to search something like "coke OR sprite AND can AND 330ml"
This should only return the two products used in my example.
Not the problem that i face is this. My search feature runs like my original example. I am trying to include some kind of and, or, not functionality, and have not quite decided how this should all work out.
Currently, a seach of "coke can" will return only the results containing coke and can. But if I searched "coke sprite can" No results will be returned as there are no results that contain all 3 of the words used in the string.
Here is the query:
$dataQuery = "
SELECT *
FROM `products`
WHERE upper(`desc`)
LIKE '%"
. implode("%'
AND upper(`desc`)
LIKE '%",
$keywords_array)
. "%'
ORDER BY `desc`";
$keywords_array is an array built up from the user entered search string, after validation, the code for this is as follows:
$find = strtoupper($find);
$find = strip_tags($find);
$find = trim ($find);
$_SESSION['find'] = $find;
$keywords_array = explode(' ', $find);
$find is the string set by the post value of the search string entered by the user.
Does anyone have any ideas on a good way to accomplish what I have explained?
If you need any additional code, or a better explanation, please just ask!
Thanks!!
You are looking for mysql fulltext search with the limitation that it has a default minimum word length of 4 characters, so you can't search for "can" if you can't edit the server configuration.
Also, it has a "stop word" policy that doesn't deliver results on words that appear in more than 50% of the overall data.
I have an array with a list of keywords,
$arr = array('london','england','football',...);
The array could have Nth keywords.
I need to use this on a sql query but i dont want to do too many repetitions like this.
select * from t1 where title LIKE('%$arr[0]%') OR title LIKE('%$arr[1]%') [...]
just need a better solution as each keyword filters out the records.
thnx
One solution would be to create a lookup table that is populated with the rowid and keywordid whenever a row is created or changed.
Then you can skip all the LIKE and get much more performance.
A trigger on insert and update should work but it will cost a little more performance on insert and update.
You would just use
SELECT * FROM t1 WHERE id in (SELECT id FROM lookup WHERE keywordid IN ())
And if the keywords are many you could use a sub query more to get the keyword id's or if fewer use a cached lookup structure directly in code, dictionary or likewise.
http://www.webdevelopersnotes.com/tutorials/sql/tutorial_mysql_in_and_between.php3
I'm unsure whether you wanted to optimize the actual query itself or the means to insert keywords into the query.
If it's the latter, then what comes to mind quickly is:
$query = "SELECT * FROM t1 WHERE title";
foreach($arr as $keyword)
{
$stack[] = " LIKE '%$keyword%' ";
}
$query .= implode(' OR title ', $stack);
If you want to avoid full table scans based on keywords, that requires a different topic in itself with more explanation at your end.
Depending on the kind (and amount) of searches you'll be doing, you might need to consider using something else than pure-MySQL for that.
MySQL by itself is not quite well-suited when it comes to fulltext search :
Using like '%...%' will result in bad performances (scanning all the lines of your table)
Using a FULLTEXT index means using MyISAM as storage engine.
If you need to do a lot of searches, it might become more interesting to invest in a solution dedicated to indexing/searching through text, like Solr, or Sphinx.