Multiple keywords search - php

I'm getting trouble to add the search in my site. I cannot figure out the way to make it done.
I 've a table as follow:
TABLE A
id text
1 Hello there whats up. I'm trying to code.
2 there need to be this code
Now I want search using the
Keywords = hello code
And the results should provide me both the rows because both the rows contains some portion of the keyword as follow:
id text
1 **Hello** there whats up. I'm trying to **code**
2 there need to be this **code**
Also the result should provide the row with max number of keywords matched first.
I tried doing this but it only provide me some of my desire results.
<?php
$keyword = 'hello code';
$exloded = explode(' ', $keyword);
foreach($exploded as value):
$sth = $db->query("SELECT * FROM A WHERE `text` LIKE :value");
$sth->execute(array(':value' => '%'.$value.'%'));
$rows = $sth->fetchAll();
endforeach;
echo $rows;
?>
Updated
I simply Did this and it worked fine for me. But I want to know whether this is the correct way to get the work done.
$keyword = hello code;
$query ="SELECT *, MATCH(`page_content`) AGAINST('$keyword' IN BOOLEAN MODE) AS score FROM super_pages WHERE MATCH(`page_content`) AGAINST('$keyword' IN BOOLEAN MODE) ORDER BY score DESC";
$sth = $this->db->query($query);
$result = $sth->fetchAll();

$rows will have the data where your keyword code matches in your table you can rewrite your code to match for both keywords as
$keyword = 'hello code';
$exloded = explode(' ', $keyword);
$query = 'SELECT * FROM A ';
$i = 0;
$params = array();
foreach ($exploded as $value):
if ($i == 0) {
$query .= ' WHERE `text` LIKE :value_'.$i;
} else {
$query .= ' OR `text` LIKE :value_'.$i;
}
$params[':value_'.$i] = '%'.$value .'%';
$i++;
endforeach;
$sth = $db->query($query);
$sth->execute($params);
$rows = $sth->fetchAll();
echo '<pre>';print_r($rows);echo '</pre>';
Build your query in loop(over your provided keywords) and assign unique placeholders in query to match for all values
Edit for full text search
Using full text search you can match exact same phrase with provided keyword,In order to work with full text search you need an index of type FULLTEXT.
ALTER TABLE `A` ADD FULLTEXT INDEX `fulltextindex` (`text`);
And query will be like
$keyword = 'hello code';
$exloded = explode(' ', $keyword);
$where = '';
$i = 0;
$select = array();
$params = array();
foreach ($exploded as $value):
$select[]= ' MATCH(`text`) AGAINST(:value_'.$i.' IN BOOLEAN MODE) ';
if ($i == 0) {
$where .= ' WHERE MATCH(`text`) AGAINST(:value_'.$i.' IN BOOLEAN MODE)';
} else {
$where .= ' OR MATCH(`text`) AGAINST(:value_'.$i.' IN BOOLEAN MODE)';
}
$params[':value_'.$i] = $value ;
$i++;
endforeach;
$query ='SELECT *,'. implode( ' + ',$select).' AS score FROM A '.$where.' ORDER BY score DESC';
$sth = $db->query($query);
$sth->execute($params);
$rows = $sth->fetchAll();
echo '<pre>';print_r($rows);echo '</pre>';
Above code will produce a query like
SELECT *,
MATCH(`text`) AGAINST('hello' IN BOOLEAN MODE)
+
MATCH(`text`) AGAINST('code' IN BOOLEAN MODE) AS score
FROM A
WHERE MATCH(`text`) AGAINST('hello' IN BOOLEAN MODE)
OR MATCH(`text`) AGAINST('code' IN BOOLEAN MODE)
ORDER BY score DESC
Alias score in above query will have value for each row and its matched score thus you can order your result in descending manner to show the records first which has a highest score.
Note: You can use Full text search in Myisam but for innodb you have
to upgrade your Mysql to 5.6 which supports full text searching in
innodb too

you can use the following code:
<?php
$keyword = 'hello code';
$exloded = explode(' ', $keyword);
$sql = "SELECT * FROM A WHERE ";
foreach($exploded as $value):
$sql .= "text LIKE '" . $value "%' OR ";
endforeach;
// remove last 'OR '
$sql = substr($sql, -3);
$result = mysqli_query($con, $sql);
//................
?>

Related

Searching Mysql database with multiple keywords

I am trying to search a database for one or multiple keywords passed in $_POST['search'], I am not able to loop through the keywords.
The code was modified from https://code-boxx.com/php-mysql-search/
My script is:
$str = $_POST['search'];
$keywords = preg_split('/[\s]+/', $str);
$totalKeywords = count($keywords);
$query = "";
$i = 0;
while($keywords[i] != null)
{
$query .= " AND name LIKE %".$keywords[i]."%" ;
}
$stmt = $pdo->prepare("SELECT * FROM MYTABLE WHERE MATCH (name) AGAINST (?) ". $query ." ");
$stmt->execute([$_POST['search']]);
$results = $stmt->fetchAll();
Ideally the search should look like:
SELECT * FROM MYTABLE WHERE MATCH (name) AGAINST (Keyword1 Keyword2 Keyword3) AND name LIKE '%Keyword1%' AND name LIKE '%Keyword2%' AND name LIKE '%Keyword3%
The search page:
<?php
if (isset($_POST['search'])) {
require "2.php";
}
?>
<!DOCTYPE html>
<html>
<body>
<!-- [SEARCH FORM] -->
<form method="post">
<input type="text" name="search" required/>
<input type="submit" value="Search"/>
</form>
<!-- [SEARCH RESULTS] -->
<?php
if (isset($_POST['search'])) {
if (count($results) > 0) {
echo $sql;
foreach ($results as $r) {
printf("<div>%s</div>", $r['name']);
}
} else {
echo "No results found";
}
}
?>
</body>
</html>
Appreciate any help.
When you use prepared statements, avoid directly injecting your values into the query statement. You'll defeat the purpose of having them prepared.
So, first you can treat you statement into two parts.
One is the match against, and two is the where like part.
So first build off the base query:
$sql = "SELECT * FROM MYTABLE WHERE 1=1"; // base query
From there you can append the match against and where like clauses as you go along with the construction.
Then, create the match against bit:
MATCH (name) AGAINST (Keyword1* Keyword2* Keyword3* IN BOOLEAN MODE) // the ideal query
MATCH (name) AGAINST (? ? ? IN BOOLEAN MODE) // convert to question mark placeholders
To create it, just simply implode (glue) the question marks with spaces according to the number of input:
$against_placeholder = implode(' ', array_fill(0, $totalKeywords, '?')); // = ? ? ?
And to create the where like clause:
(name LIKE ? OR name LIKE ? OR name LIKE ?) // = like keyword1 or like keyword2 or like keyword3
So in your code:
$like_placeholder = implode(' OR ', array_fill(0, $totalKeywords, 'name LIKE ?'));
So to piece them all together:
$against_placeholder = implode(' ', array_fill(0, $totalKeywords, '?'));
$like_placeholder = implode(' OR ', array_fill(0, $totalKeywords, 'name LIKE ?'));
$sql .= " AND MATCH (name) AGAINST ({$against_placeholder}) AND ({$like_placeholder})"; // build the query with placeholders
This will yield a complete query statement like this:
SELECT * FROM MYTABLE WHERE 1=1 AND (MATCH (name) AGAINST (? ? ? IN BOOLEAN MODE)) AND (name LIKE ? OR name LIKE ? OR name LIKE ?)
And then the rest is just building the actual payload in the execution. Here's the rest:
$sql = "SELECT * FROM MYTABLE WHERE 1=1"; // base query
$against_placeholder = implode(' ', array_fill(0, $totalKeywords, '?'));
$like_placeholder = implode(' OR ', array_fill(0, $totalKeywords, 'slug LIKE ?'));
$sql .= " AND (MATCH (slug) AGAINST ({$against_placeholder} IN BOOLEAN MODE)) OR ({$like_placeholder})"; // build the query with placeholders
// prep input
$against_keywords = array_map(function($value) {
return "{$value}*";
}, $keywords);
$where_keywords = array_map(function($value) {
return "%{$value}%";
}, $keywords);
$keywords = array_merge($keywords, $where_keywords);
$stmt = $pdo->prepare($sql);
$stmt->execute($keywords);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);

Search only reads one keyword: SQLSTATE[HY093]: Invalid parameter number

I'm having an issue with my search feature of my website. It's not handling search terms with more than one word. try a live example of the search bar problem over at http://mobile.mixtapemonkey.com/
search "It's Better This Way"
as soon as you type in B I get an error. other than that it works, what do you think the issue is?
if (empty($errors)) {
$name_explode = explode(' ', $name); // explode keywords to get each individual keyword, and put into array
$name_count = count($name_explode); // count keywords from array
foreach($name_explode as $name_single) {
$x++; // increment x each loop
$keyword = "%".$name_single."%";
$where .= '`keywords` LIKE :keyword'; // append to where clause
if ($name_count!=$x) {
$where .= ' AND '; // as long as keyword isn't the last, append AND.
}
}
$sql = "SELECT `name`, `thumb`, `id`, `title` FROM `mixtapes` WHERE ".$where." ORDER BY `id` DESC LIMIT 40";
$search = $db->prepare($sql); // perform query
$search->bindParam(':keyword', $keyword, PDO::PARAM_STR);
$search->execute();
$search_num_rows = $search->rowCount(); // get number of rows (results) returned
Please check the code I have added.the reason you got that error is due to your foreach loop. In that loop you add :keyword everytime. But that count dont match with the bindParam value count. So I changed you code and posting below
if (empty($errors)) {
$name_explode = explode(' ', $name); // explode keywords to get each individual keyword, and put into array
$name_count = count($name_explode); // count keywords from array
$x = 0;
foreach($name_explode as $name_single) {
$x++; // increment x each loop
$keyword[':keyword'.$x] = "%".$name_single."%";
$where .= '`keywords` LIKE :keyword'.$x; // append to where clause
if ($name_count!=$x) {
$where .= ' OR '; // as long as keyword isn't the last, append AND.
}
}
$sql = "SELECT `name`, `thumb`, `id`, `title` FROM `mixtapes` WHERE ".$where." ORDER BY `id` DESC LIMIT 40";
$search = $db->prepare($sql); // perform query
$search->execute($keyword);
$search_num_rows = $search->rowCount(); //

How to limit search results while using foreach and while loops

The following code works well, but I couldn't find a way to limit the number of results. Any ideas please?
$q = "some keywords for search"; // always escape
$keys = explode( " ",$q );
$query = "SELECT * FROM table WHERE para LIKE '%$q%' ";
foreach($keys as $k)
{
$query .= " OR para LIKE '%$k%'";
}
$result = $mysqli->query($query);
while( $row = $result->fetch_assoc())
{
if ($row != 0) {
$title = $row['title'];
}
}
Any help while be appreciated.
Note: the $q holds the search keywords, and then the code explode it, and search for the keywords in 2 steps:
1- as one sentence using ($q as it is).
2- it searches for each keyword as an array after exploding the $q (here is the part that the "foreach" does).
After that the code loops using "while" to find all results match the search request.
Use LIMIT after completing your query.
Also, if you want to get results sorted by some fields in your table, you could also say " ORDER BY fieldname ASC|DESC"
As follows:
$q = "some keywords for search"; // always escape
$keys = explode( " ",$q );
$query = "SELECT * FROM table WHERE para LIKE '%$q%' ";
foreach($keys as $k)
{
$query .= " OR para LIKE '%$k%'";
}
$query .= " LIMIT 10"; //<<<<<<<<<<<<<<<
$result = $mysqli->query($query);
while( $row = $result->fetch_assoc())
{
if ($row != 0) {
$title = $row['title'];
}
Use LIMIT.
SELECT * FROM table WHERE para LIKE '%$q%' LIMIT 2
You can limit the number of results in your MySQL query, like so:
$query = "SELECT * FROM table WHERE para LIKE '%$q%' LIMIT 5";
this will limit it to 5 results. If you want 10, change it to 10

How to eliminate duplicate search results in MySQL?

I am having some trouble displaying text from database using PHP and SQL. Below is a script similar to what I have.
$search_split = explode(" ", $search); //$search is what user entered
foreach ($search_split as $searcharray) {
$searched = mysqli_query($connect, "SELECT * FROM people WHERE `description` LIKE '%$searcharray%'");
while($info = mysqli_fetch_array($searched)) {
echo $info['description'];
}
}
So, for example the user enter 'He is male'. I split the word into three part 'He', 'is' and 'male' using 'explode' function. After that, I search the database for words that is similar to those three word. However, if a row have all the three words, it would display the row three times. How can I make it to display only once?
You could do something like this:
$search = 'test search me';
$search_split = array_map(function($piece) use ($mysqli_connection){
return "'%" . $mysqli_connection->real_escape_string($piece) . "%'";
}, explode(' ', $search)); //$search is what user entered
$search_split = implode(' OR `description` LIKE ', $search_split);
$sql = "SELECT * FROM people WHERE `description` LIKE $search_split";
echo $sql; // SELECT * FROM people WHERE `description` LIKE '%test%' OR `description` LIKE '%search%' OR `description` LIKE '%me%'
$searched = mysqli_query($connect, $sql);
Can you use full text search?
Add a full text index to the table
ALTER TABLE people ADD FULLTEXT(description);
Then you can use a query like this
SELECT *
FROM people
WHERE
MATCH ( description )
AGAINST ('+He +is +male' IN BOOLEAN MODE)
First store your results into one array then display it. Refer below code.
$search_split = explode(" ", $search); //$search is what user entered
foreach ($search_split as $searcharray) {
$searched = mysqli_query($connect, "SELECT * FROM people WHERE `description` LIKE '%$searcharray%'");
while($info = mysqli_fetch_array($searched)) {
$results[$info['YOUR_PRIMARY_KEY']] = $info['description']; // this will over write your previous record
}
}
foreach($results as $result){
echo $result;
}
Now every records display only once.
You have put your db query in a foreach loop, which loops 3 times (with the current data: he, is and male). What you want to do is put all the search variables in one query, something like:
$search_split = explode(" ", $search); //$search is what user entered
$querypart = "'%" . implode("%' AND '%", $search_split) . "%'"; // use OR or AND, to your liking
$searched = mysqli_query($connect, "SELECT * FROM people WHERE `description` LIKE " . $querypart);
while($info = mysqli_fetch_array($searched)) {
echo $info['description'];
}
This does not take any escaping/sanitizing of the query input, be aware...
$result = array();
$search_split = explode(" ", $search); //$search is what user entered
foreach ($search_split as $searcharray) {
$searched = mysqli_query($connect, "SELECT * FROM people WHERE `description` LIKE '%$searcharray%'");
while($info = mysqli_fetch_array($searched)) {
$result[] = $info['description'];
}
}
$finalres = array_unique($result);
so, finalres contains unique results
for($i = 0; $i < count($finalres); $i++)
echo $finalres[$i];

Checking for empty fields in mysql table

I have a table with 12 columns and 200 rows. I want to efficiently check for fields that are empty/null in this table using php/mysql. eg. "(col 3 row 30) is empty". Is there a function that can do that?
In brief: SELECT * FROM TABLE_PRODUCTS WHERE ANY COLUMN HAS EMPTY FIELDS.
empty != null
select * from table_products where column is null or column='';
SELECT * FROM table WHERE COLUMN IS NULL
As far as I know there's no function to check every column in MySQL, I guess you'll have to loop through the columns something like this...
$columns = array('column1','column2','column3');
foreach($columns as $column){
$where .= "$column = '' AND ";
}
$where = substr($where, 0, -4);
$result = mysql_query("SELECT * FROM table WHERE $where",$database_connection);
//do something with $result;
The = '' will get the empty fields for you.
you could always try this approach:
//do connection stuff beforehand
$tableName = "foo";
$q1 = <<<SQL
SELECT
CONCAT(
"SELECT * FROM $tableName WHERE" ,
GROUP_CONCAT(
'(' ,
'`' ,
column_name,
'`' ,
' is NULL OR ',
'`' ,
column_name ,
'`',
' = ""' , ')'
SEPARATOR ' OR ')
) AS foo
FROM
information_schema.columns
WHERE
table_name = "$tableName"
SQL;
$rows = mysql_query($q1);
if ($rows)
{
$row = mysql_fetch_array($rows);
$q2 = $row[0];
}
$null_blank_rows = mysql_query($q2);
// process the null / blank rows..
<?php
set_time_limit(1000);
$schematable = "schema.table";
$desc = mysql_query('describe '.$schematable) or die(mysql_error());
while ($row = mysql_fetch_array($desc)){
$field = $row['Field'];
$result = mysql_query('select * from '.$schematable.' where `'.$field.'` is not null or `'.$field.'` != ""');
if (mysql_num_rows($result) == 0){
echo $field.' has no data <br/>';
}
}
?>
$sql = "SELECT * FROM TABLE_PRODUCTS";
$res = mysql_query($sql);
$emptyFields = array();
while ($row = mysql_fetch_array($res)) {
foreach($row as $key => $field) {
if(empty($field)) || is_null($field) {
$emptyFields[] = sprintf('Field "%s" on entry "%d" is empty/null', $key, $row['table_primary_key']);
}
}
}
print_r($emptyFields);
Not tested so it might have typos but that's the main idea.
That's if you want to know exactly which column is empty or NULL.
Also it's not a very effective way to do it on a very big table, but should be fast with a 200 row long table. Perhaps there are neater solutions for handling your empty/null fields in your application that don't involve having to explicitly detect them like that but that depends on what you want to do :)
Check this code for empty field
$sql = "SELECT * FROM tablename WHERE condition";
$res = mysql_query($sql);
while ($row = mysql_fetch_assoc($res)) {
foreach($row as $key => $field) {
echo "<br>";
if(empty($row[$key])){
echo $key." : empty field :"."<br>";
}else{
echo $key." =" . $field."<br>"; 1
}
}
}
Here i'm using a table with name words
$show_lang = $db_conx -> query("SHOW COLUMNS FROM words");
while ($col = $show_lang -> fetch_assoc()) {
$field = $col['Field'];
$sel_lan = $db_conx -> query("SELECT * FROM words WHERE $field = '' ");
$word_count = mysqli_num_rows($sel_lan);
echo "the field ".$field." is empty at:";
if ($word_count != 0) {
while($fetch = $sel_lan -> fetch_array()){
echo "<br>id = ".$fetch['id']; //hope you have the field id...
}
}
}
There is no function like that but if other languages are allowed, you can extract the structure of a table and use that to generate the query.
If you only need this for a single table with 30 columns, it would be faster to write the query by hand...

Categories