Text Search using Mysql - php

I am trying to create a search blog system where blogs are displayed based on user suggestions. Suggestions are basic hashtags which is stored in user_information table. While blogs will also have some hashtags input by blogger which will be stored in blog table.
Now as I have to match hashtags from user_information table to blog table, I am not finding a way to do this.
I have tried mysql LIKE CLAUSE but I am not able to get even single result.
My Query was [just representation]
SELECT * FROM blog WHERE hashtags LIKE $hashtags_from_user

You are not correctly escaping the value, you require the hash tags to be enclosed in quotations
"LIKE '". $hashtags_from_user ."'";
Additionally you will pay the price in the SELECT statement when you have denormalized database (hash-tags should have their own row in a hash_tags table and the blog would references these via a many-to-many or many-to-one association)
Currently the output of the query is:
SELECT * FROM blog WHERE hashtags LIKE '#hashtag1,#hashtag2,#hashtag'
This isn't going to give you the results you want if you need to match on the individual tags.
The only soultion would be to read the users has tag string, break it up into each tag (using $tags = explode(',', $hash_tags_from_user); and then build up a query to include all of them
$where = array();
$select = 'SELECT * FROM foo';
foreach($tags as $tag) {
$where[] = sprintf('tag LIKE \'%s\'', $tag);
}
if (! empty($where)) $select .= 'WHERE ' . implode(' OR ', $where);
Please don't use the above as it's just and example (and open to simple SQL injection) consider using prepared statements, with PDO/MySQLi.

Try this:
$sql = 'SELECT * FROM blog WHERE hashtags LIKE "'.$hashtags_from_user.'";';
You have to put the string you are searching for between ' or " you can do it without only with numbers.

Related

How to search something in the Database independent of the sequence of words?

I am working on a search filter that connects to a MySQL database. It accepts a keyword parameter; however, it only searches for the keyword in the order it is typed in. For example: If I type in "house rental", it looks for the term in xyz column in the order it is typed it.
However, I would like to change it so that it searches for both those terms are independent of the order they are typed in. Example, if typed in "house rental", the result should contain listings that have either, "house rental" or "rental house" mentioned somewhere in the xyz columns.
I have tried to break the keywords, put it in an array and do a foreach loop on the array to get the result but it generates correct but undesired results. The results that are generated are not the ones that I required.
$samp_text = 'House Rental';
$split_string_array = preg_split('/[\s,]+/', $samp_text);
foreach ($split_string_array as $each_sql_query) {
print_r('SELECT * FROM XYZ WHERE $keyword LINK = %' . $each_sql_query . '% '. "\r");
}
I would like a suggestion on how to tackle this problem.
While the method Sloan Thrasher suggests works, it does not scale.
The solution is to create a table contains each keyword from a searchable document as a single row with a foreign key to the original document with an index on the search word then the foreign key. Split your search term into an equivalent table, join the 2 and count the matches:
Select doc.txt, count(*)
From doc
Inner join keywords
On doc.id=keywords.doc_id
Inner join search
On keywords.word=search.word
Where search.query_id=?
Group by doc.txt
Order by count(*) desc
Alternatively just use the built-in fulltext capability of mysql.
You can build a query that will check for all keywords at once and give you list of rows that contain all of the keywords.
If you want all rows that contain one or more keywords, change the AND in the implode function to OR.
$samp_text = 'House Rental';
$split_string_array = preg_split('/[\s,]+/', $samp_text);
$qstr = "SELECT * FROM XYZ WHERE ";
$keywords = array();
foreach ($split_string_array as $each_sql_query) {
$keywords[] = " LINK = '%" . $each_sql_query . "%'\r")
}
$qstr .= implode(" AND ",$keywords);
// Code to execute query and use results.

Trigger database search result with multiple keywords

I have a PHP search script that searches a MySQL database to answer questions. Currently my database is set out like this...
idquestion = what is the timeanswer = The time is 12:00
This data is then sent to my PHP code:
<?php
mysql_connect("localhost","username","password");
mysql_select_db("database");
if(!empty($_GET['q'])){
$query=mysql_real_escape_string(trim($_GET['q']));
$searchSQL="SELECT * FROM questions WHERE `question` LIKE '%{$query}%' LIMIT 1";
$searchResult=mysql_query($searchSQL);
while($row=mysql_fetch_assoc($searchResult)){
$results="{$row['answer']}";
}
echo $results;
}
?>
Currently, the users query must contain exactly the same text as the question field. My question is; how can I make this work so it triggers a certain for several keywords?
I hope you can understand my question
I think full test search index can help
http://dev.mysql.com/doc/refman/5.0/en/fulltext-search.html SOUNDEX function can be useful in this context too.
You can split query to keywords and generate dynamic SQL but it is inefficient (performance issues SQL injection attacks ) see http://php.net/manual/en/function.split.php
Alternative is to add table QUESTION_KEYWORD(QUESTION_ID,KEYWORD), split question and search this table to find best QUESTION_ID, but full text search index is more efficient. In fact full text search index uses similar data structure to optimize test search.
For an easy quick-fix based on your comment you could do something as simple as this:
// Assuming query string is ?q=make,sponge,cake
$searchSQL="SELECT * FROM questions WHERE ";
$count = 0;
foreach (explode(',', $_GET['q']) as $query) {
$searchSQL .= "`question` LIKE '%" .mysql_real_escape_string(trim($query)) . "%' ";
if ($count++ > 0) {
$searchSQL .= ' OR ';
}
}
$searchSQL .= ' LIMIT 1';
$searchResult = mysql_query($searchSQL);

Using PHP & MySQL to sort Events by multiple types.

I need to create a small piece of code to allow me to filter my events database based on category types users have selected.
I currently have it working for users who have only one category selected...
$user_qstring = "SELECT types FROM tbl_users WHERE user_id='".$_SESSION['id']."'";
$user_result = mysql_query($user_qstring);
$user_row = mysql_fetch_array($user_result);
$type_filter = $user_row['types'];
if(isset($type_filter) && $type_filter !="") {
$day_events = "SELECT COUNT(*) FROM tbl_events WHERE day='".$day_id."' AND
type='".$type_filter."'";
}else{
$day_events = "SELECT COUNT(*) FROM tbl_events WHERE day='".$day_id."'";
}
I need to alter this code so that if $type_filter is set and contains multiple categories in the following format.
Festivals,Sports,Education
And have the query automatically add...
OR type='".$type_filter[2]."' OR type='".$type_filter[3]."' OR ect...
I have been able to solve the problem using multiple...
elseif(){
}
Statements, but need a solution that is scalable to unlimited types.
I know I need to start by changing $type_filter to a list using explode...
$type_filter = explode(",", $user_row['types']);
But I'm still having trouble putting it all together for a short elegant solution.
You will need to confirm that $type_filter does not contain single quotes first otherwise you're an easy target for sql injection attacks.
$day_events = "SELECT COUNT(*) FROM tbl_events WHERE day='".$day_id."' AND type IN ('" . implode("','", explode(',', $type_filter)) . "')";
try something like the follwing sql
select * from ... where type in ('one', 'two', ...) ...
and as a remark - always escape get/post data using mysql_real_escape_string or you are vulnerable to injection attacks.

query MySQL database with variables from an array

I have my database field named tag_info in it I will have data that appears like so:
arm leg head forearm foot
Tags are separated by spaces. (not sure if that is the best way so I am open to suggestions)
Now I need to write a query that will return all the rows that have specific tags in the tag_info field. But the tags I am looking for do not all need to be present just at least one of them does.
$find_tags = array("arm,"leg");
$query = "SELECT * FROM mydatabase WHERE tag_info = '$find_tags'
Is this possible?
You should probably change your database schema around first. Instead of storing tags separated by spaces.
Could you show me the schema (column names) for your table. I think this is a case where you should be having two tables. A separate table just for tags.
That's not a good way to do tagging. You should have a tags table (id, name), yourdatabase_tags (yourdatabaserecord_id, tag_id) so you can associate a record in yourdatabase table to a tag in tags table.
So when you have a list of tags, do a query to get their ids, then query yourdatabase_tags to get the record_ids that have that tag_ids, then query yourdatabase for the records. Or you can combine them into one query (That's an exercise left for the readers :)) )
Mysql syntax does not work that way
here what will work.
$query = "SELECT * FROM mydatabase
WHERE tag_info = '".$find_tags[0]."' OR tag_info = '".$find_tags[1]."'";
In your case since the tags are all in one field you can use a wild card: %
$query = "SELECT * FROM mydatabase
WHERE tag_info LIKE '%".$find_tags[0]."%' OR tag_info LIKE '%".$find_tags[1]."%'";
Query with where tag_info like '%arm%' or tag_info like '%leg%'
but this will be quite inefficient, better to store tags seperately one tag per row, have an index on it.
It's not possible to write a query like that because you will have to dynamically change the amount of OR statements in WHERE blah OR blah. One way to do this would be to grab all the data from the database into a PHP Array. Once in the array you can programatically search through the results and eliminate the ones that do not match.
That is if you want to search for different tags, if you only need a specific query for
$find_tags = array("arm,"leg"); the other answers will work.
you can try below query.
$query = "SELECT * FROM mydatabase WHERE tag_info IN ('$find_tags');
if you are using a table name and field name correct then this may be needful to you.
Thanks.
$keywords = array("arm", "leg");
$length = count($keywords);
$searchCriteria = '';
for($i = 0; $i < $length; $i++)
{
$searchCriteria = $searchCriteria . "tag_info like '%" . $keywords[$i] . "%'";
if( ($i+1) < $length)
$searchCriteria = $searchCriteria . " OR ";
}
$query = "SELECT * FROM mydatabase
WHERE $searchCriteria";
You may need to add some additional checking to ensure that you don't run into error if the array containing the tags is empty.
The more correct approach would be to have a tag table with the availeble tags and a link table between tag_info and tag_table.
But here is a solution with a query
$query = "SELECT * FROM mydatabase WHERE ";
foreach($find_tags as $tag)
{
$query .= "tag_field LIKE %$tag% OR ";
}
$query = substr($query, 0, strlen($query) - 3);
Not tested!!
Sure, use a LIKE in the WHERE clause instead of =.
SELECT * FROM mydatabase WHERE tag_info LIKE '% foot %' or tag_info LIKE '% leg %'
your query should look something like
SELECT * FROM mydatabase WHERE tag_info = 'arm' OR tag_info = 'leg'
You could also use the MySQL IN clause:
SELECT * FROM mydatabase WHERE tag_info IN ('arm', 'leg')
This way is easier to build the query string
Edit
I just saw that you are holding the tags serialized in the DB. Now, that is definitely not good because you are destroying the concept of the structured data.
You should create a table called tags and hold each tag on its own row. Than create another pivot table between the table you wish to tag (the one that holds the serialized tags now) and the tags table.
The pivot table can hold just 2 fields - main_table_id and tag_id - this way, you will be able to query your DB with a single join like:
SELECT t.* FROM tags t
INNER JOIN pivot_table pt ON pt.tag_id = t.id
INNER JOIN main_table mt ON mt.id = pt.main_table_id
WHERE tag IN ('arm', 'leg')

Advanced MySQL Search Help

I've been trying to come up with something for a while now to no avail. My MySQL knowledge is rudimentary at best so I could use some guidance on what I should use for the following:
I have 2 tables ('bible' and 'books') that I need to search from. Right now I am just searching 'bible' with the following query:
SELECT *
FROM bible
WHERE text LIKE '%" . $query . "%'
ORDER BY likes DESC
LIMIT $start, 10
Now, I need to add another part that searches for some pretty advanced stuff. Here is what I want to do in pseudocode which I am aware doesn't work:
SELECT *
FROM bible
WHERE books.book+' '+bible.chapter+':'+bible.verse = '$query'
$query would equal something like Genesis 1:2, Genesis coming from books.book, 1 coming from bible.chapter and 2 coming from bible.verse
Any help/guidance on this is much appreciated =)
I would recommend designing a way in your application code to break up that query so that you search for the book, chapter, and verse separately from the keyword.
That means you need columns for book, chapter, and verse that are separate from the verse text.
You should also use a FULLTEXT index because the LIKE wildcard searches are extremely inefficient.
Here's how I'd run the query in PHP using PDO:
$quoted_query = $pdo->quote($query);
$sql = "SELECT * FROM bible
WHERE book = ? AND chapter = ? AND verse = ?
AND MATCH(text) AGAINST ({$quoted_query})"
$stmt = $pdo->prepare($sql);
$stmt->execute(array($book, $chapter, $verse));
I'd rather use a parameter for the fulltext query too, but MySQL doesn't support that.
You're close. To concatenate the fields use the CONCAT() function:
SELECT * FROM bible WHERE CONCAT(books.book, ' ', bible.chapter, ':', bible.verse) = '$query'
You can use MySQL concatenation:
SELECT *
FROM bible JOIN books
WHERE CONCAT(books.book, ' ', bible.chapter, ':', bible.verse) = '$query'
I'm not sure what your foreign key is linking books to bible, but that may need specification as well.
You need to parse the query into book, chapter and verse in php first.
A regular expression should work:
preg_match("/(.+)([0-9]+):([0-9]+)/",$query,$matches);
$book = trim(matches[1]); // use trim to remove extra spaces
$chapter = matches[2];
$verse = matches[3];
Then your sql query becomes:
SELECT *
FROM bible
WHERE books.book = '$book' AND bible.chapter= '$chapter' AND bible.verse ='$verse'
-- watch out for sql injection here! use prepared statements!

Categories