So I'm working on a search function for a social networking site, and it searches user posts. Some users like to put hyphens instead of spaces, but I would like for the search function look for both hyphens and spaces in a result.
For example, if they have a post named "SQL-IS AWESOME" and I search for "SQL IS AWESOME", can I still find that post? I tried using 2 sql queries, one for the original search query, and one modified to change all spaces to hyphens.
But if I search "SQL IS-AWESOME" it still won't find it. Is there an easier way?
My current code:
$sql = "SELECT * FROM posts
WHERE (post_title='".$query."'
OR post_title LIKE '%".$query."'
OR post_title LIKE '%".$query."%'
OR post_title LIKE '".$query."%')
".$locquery."
".$cat."
ORDER BY date DESC
LIMIT 18";
As someone has suggested, you could just adapt and use fulltext searching.
If you choose to take this route, you will need to enable fulltext searching on the fields required.
I'll assume you will check post_title and post_body (?), which needs you to run this;
ALTER TABLE `posts` ADD FULLTEXT KEY `post_search` (`post_title`,`post_body`);
When that is done, your search query can easily be edited to become;
$sql = "SELECT * FROM `posts` WHERE MATCH(post_title,post_body) AGAINST '$search'";
If you'd like better matching, it is also possible to give it a score and order by that, which would require code similar to this:
$sql = "SELECT *, MATCH(post_title, post_body) AGAINST ('{$search}') AS score ".
"FROM `posts` WHERE MATCH(post_title, post_body) AGAINST ('{$search}') ORDER BY `score` DESC";
--- NOTES
For the search, you need to work out how you will be searching.
In the last instance I used similar, I simply had a form for the search term (Named "Search") resulting in $_POST['search'] being sent to the server.
I then used;
$search = (array_key_exists('search', $_POST) && is_string($_POST['search'])) ? mysql_real_escape_string($_POST['search'], $c) : FALSE ;
if ($search) {
// Do the fulltext query requires (See above)
}
Since fulltext search will disregard the hyphen, you are left with just spaces, which works great for fulltext, if you opt to use scored results.
Related
I was writing a products search engine for a pet project website and ran into a problem. How do I return products ordered by "how much of a match" they were for the query? Right now I have this, which will only return products where the query matches the beginning.
$sql = "SELECT * FROM `Products` WHERE name LIKE '$query%'";
Placing a % in front of $query like so
$sql = "SELECT * FROM `Products` WHERE name LIKE '%$query%'"
achieves the desired output but it is not ordered correctly. For example, the user searches for "p" and they get a list like: [A-word with p in it], [B-word with p in it], [P-word]. I would like the P-word to show up first, and the A & B words to show up after it. Is this possible with only MySQL? I've looked into ORDER BY but that orders by columns AFAIK.
You can do this with order by:
SELECT *
FROM `Products`
WHERE name LIKE '%$query%'"
ORDER BY (name like '$query%') desc,
name;
The first clause will put the names that start with $query first.
I develope a website for E-books, i have in database table for authors and table for publishers .. sometimes the author name is added also in publishers table as a publisher
Now i have his name as an author and a publisher .. when i search in the site for his name, it return twice because i search in authors table and in publishers table then merge two queries
this is my code :-
function generate_results($keyword, $row = 0) {
$result1 = $this->db->query("SELECT au_id,au_name,au_state,SUBSTR(au_info,1,190) AS au_info,au_img FROM d_author where (au_name LIKE '%$keyword%' or au_info LIKE '%$keyword%') and au_state = '1' limit $row,20");
$result2 = $this->db->query("SELECT pub_id,pub_name,pub_state,SUBSTR(pub_info,1,190) AS pub_info,pub_img FROM d_publishing where (pub_name LIKE '%$keyword%' or pub_info LIKE '%$keyword%') and and pub_state = '1' limit $row,20");
$results = array_merge($result1->result_array(), $result2->result_array());
return $results;
}
Now i want to modify the second query to something like that :
select all publishers from "publishers table" where the name of publisher is like $keyword and this $keyword doesn't exist in authors table ..
I mean if this name exist in authors don't select it in publishers
How can i translate that meaning to Mysql Query
First, check out sql-injections before continuing to develop in your ebook-application. Looks like your keyword is not checked to be a safe parameter. And just to be sure, do you know about csrf and xss? If not, check about that too. This is very important.
Secondaly, you should consider working on your database design to avoid having duplicated values. Check out "database normalization" for more information. Seems like you could do another table to extract your "contact information" like name, state, id etc. This would make it possible for your author-table and publisher-table to use a "contact_id" referencing the contact-information-table.
Last but not least, to answer your question, you can generelly solve such problems with an "anti join". Use a left join on the authors table in the second query and check for "IS NULL" on matches with the publisher table. More information here: http://explainextended.com/2009/09/18/not-in-vs-not-exists-vs-left-join-is-null-mysql/
I do not know if I understand your database design completly right, but it also seems like an UNION combined with a DISTINCT could help you - and you wouldn't even need this array_merge-stuff. I would suggest you to check out these two commands in the mysql docs.
I don't know why I can't find this anywhere. I would think this would be pretty common request. I am writing a search engine in PHP to search a MySQL database of For Sale listings for keywords inputted by the user.
There are several columns in the table but only 2 that will need to be searched. They are named file_Title & file_Desc. Think of it like a classified ad. An item title and a description.
So for example a user would search for 'John Deere Lawn Tractor'. What I would like to happen is classifieds that have all 4 of those words show up at the top of the list. Then results that only have 3 an so on.
I've read a very good webpage at http://www.roscripts.com/PHP_search_engine-119.html
From that authors example I have the following code below:
<?php
$search = 'John Deere Lawn Tractors';
$keywords = split(' ', $search);
$sql = "SELECT DISTINCT COUNT(*) As relevance, id, file_Title, file_Desc FROM Listings WHERE (";
foreach ($keywords as $keyword) {
echo 'Keyword is ' . $keyword . '<br />';
$sql .= "(file_Title LIKE '%$keyword%' OR file_Desc LIKE '%$keyword%') OR ";
}
$sql=substr($sql,0,(strLen($sql)-3));//this will eat the last OR
$sql .= ") GROUP BY id ORDER BY relevance DESC";
echo 'SQL is ' . $sql;
$query = mysql_query($sql) or die(mysql_error());
$Count = mysql_num_rows($query);
if($Count != 0) {
echo '<br />' . $Count . ' RESULTS FOUND';
while ($row_sql = mysql_fetch_assoc($query)) {//echo out the results
echo '<h3>'.$row_sql['file_Title'].'</h3><br /><p>'.$row_sql['file_Desc'].'</p>';
}
} else {
echo "No results to display";
}
?>
The SQL String outputted is this:
SELECT DISTINCT COUNT(*) As relevance, id, file_Title, file_Desc FROM Listings
WHERE ((file_Title LIKE '%John%'
OR file_Desc LIKE '%John%')
OR (file_Title LIKE '%Deere%'
OR file_Desc LIKE '%Deere%')
OR (file_Title LIKE '%Lawn%'
OR file_Desc LIKE '%Lawn%')
OR (file_Title LIKE '%Tractors%'
OR file_Desc LIKE '%Tractors%') )
GROUP BY id
ORDER BY relevance DESC
With this code I get 275 results from my DB. My problem is it really doesn't order by the number of keywords found in the row. It seems to order the results by id instead. If I remove 'GROUP BY id' then it only returns 1 result instead of all of them, which is really messing with me!
I've also tried shifting to FULLTEXT in the db but can't seem to get that going either so I'd prefer to stick with LIKE %Keyword% syntax.
Any help is appreciated! Thanks!
I would suggest a totally different approach. Your approach is cumbersome, inefficient, heavy on the DB and will likely be very slow with more and more records added to your database.
What I would suggest is the following:
Create a separate table for keywords.
Create a list of non keywords you don't want to index (like the common English prepositions etc.) so that they are not included. You
can probably find a list of them online, readily available.
When a new entry is added, you split the string into separate keywords, omitting the ones in step 2., and inserting them in the
table created in step 3 (if not already in it).
In a separate table, with a foreign key pointing to the keywords table, associate the classifed_ad to the keyword.
Steps 3 and 4 must happen again if your classified_ad is edited (i.e. any keywords inserted in step 4 deleted from the association table and the keywords analysed again and reassociated with the classified ad).
Once you have this structure, all you have to do is search the association table and order by the number of matched keywords. You can even add an extra column to it and put the number of occurrences of that keyword in the article, so that you order by that too.
That will be much faster.
I had used a script once called Sphider which does something similar. Not sure if it is still maintained, but it works in a very similar way on web pages it parses.
I know you said you had problems with FULLTEXT, but I would highly encourage you to go back and try that again. FULLTEXT indexes and search is designed to do what you are doing, and when the MATCH command is used in the WHERE clause, MySQL automatically sorts the rows from highest to lowest relevance.
For more information on FULLTEXT, check out http://dev.mysql.com/doc/refman/5.0/en/fulltext-search.html
Also, pay special note to the comment by Patrick O'Lone on the same page, some of which is quoted below...
It should be noted in the documentation that IN
BOOLEAN MODE will almost always return a
relevance of 1.0. In order to get a relevance that is
meaningful, you'll need to:
SELECT MATCH('Content') AGAINST ('keyword1
keyword2') as Relevance FROM table WHERE MATCH
('Content') AGAINST('+keyword1 +keyword2' IN
BOOLEAN MODE) HAVING Relevance > 0.2 ORDER
BY Relevance DESC
Notice that you are doing a regular relevance query
to obtain relevance factors combined with a WHERE
clause that uses BOOLEAN MODE. The BOOLEAN
MODE gives you the subset that fulfills the
requirements of the BOOLEAN search, the relevance
query fulfills the relevance factor, and the HAVING
clause (in this case) ensures that the document is
relevant to the search (i.e. documents that score
less than 0.2 are considered irrelevant). This also
allows you to order by relevance.
I used wildcards for the query
$query = mysql_query("SELECT id FROM products WHERE name LIKE '%$term%'");
and it gives me the results.
but when I use
$query = mysql_query("SELECT id FROM products WHERE MATCH (name) AGAINST ('$term')");
it does not show me any.
Why is that ?
As there is only one product, you might run into the 50% threshold:
In addition, words that are present in 50% or more of the rows are
considered common and do not match. Full-text searches are natural
language searches if no modifier is given.
Full Text Search
I'm trying to select posts using MATCH to find strings that contain the exact word. Currently it selects strings that contains the word no matter what it looks like. I want it to select strings that contains the exact value as defined. the sql code currently looks like this:
$searchstring = "#cat";
SELECT * FROM posts WHERE MATCH(content) AGAINST ('$searchstring' IN BOOLEAN MODE)
How do I get it to select the strings that contains the exact value that is defined?
You could switch over to using LIKE
$sql="SELECT * FROM `posts` WHERE `content` LIKE '%{$searchstring}%'"
That would find only posts with at least that search string in it.
$sql="SELECT * FROM `posts` WHERE `content` LIKE '%{$searchstring}%'"
But the above query will search and display all the results having $searchstring in it.
could use :
$sql="SELECT * FROM `posts` WHERE `content` LIKE '::{$searchstring}::'"