Searching Mysql database with multiple keywords - php

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);

Related

How to search for multiple keywords using sql search

I have a current SQL search query that lets users enter keywords to search for my SQL database. At the moment, the search will work with multiple words, but will show all results for either keyword. If you type in "Ford Mustang" it will show all results that have either "Ford" or "Mustang", but I need it to only show results that show both "Ford" and "Mustang".
What I have tried is below
public function getProductByName($name){
$stmt = $this->pdo->prepare('SELECT * FROM tbl_products WHERE name REGEXP :names');
$names = "[[:<:]](" . str_replace(" ", "|", $name) . ")[[:>:]]";
$stmt->execute(array('names' => $names));
return $stmt;
}
maybe this what you're looking for
select * from example where name like "%mustang%ford%"
You can write the query
select * from tbl_products where name like "%Mustang%" and name like "%ford%";
PHP code
//you may split search string like
$searchArray = explode(' ', $name);
//for loop for preparing the query
$query = 'SELECT * FROM tbl_products WHERE ';
$searchParams = array();
$conditions = [];
for($searchArray as $searchStr){
$conditions[] = 'name like ?';
$searchParams[] = "%$searchStr%";
}
//attach the conditions
$query .= implode(" and ", $conditions);
//execute the query
$stmt = $this->pdo->prepare($query);
$stmt->execute($searchParams);

PHP SELECT query of array of named placeholders

I would like to use name placeholder to pass the value of an array to a select statement, such as:
$keywords=("word1", "word2", etc);
$sql = "SELECT * FROM TABLE WHERE name LIKE :word1 AND name LIKE :word2 etc"
Below is the script that I am working on:
$symptoms=$_POST['search'];
$keywords = preg_split('/[\s]+/', $symptoms);
$totalKeywords = count($keywords);
$sql = "SELECT * FROM TABLE_3 WHERE MATCH (symptoms) AGAINST (:symptoms) ";
$like_placeholder = implode(' AND ', array_fill(0, $totalKeywords, 'symptoms LIKE ?'));
$sql .= " AND ({$like_placeholder})"; // build the query with placeholders
// prep input
$where_keywords = array_map(function($value) {
return "%{$value}%";
}, $keywords);
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':symptoms', $symptoms);
$stmt->execute([$_POST['search']]);
$stmt->execute($keywords);
$results = $stmt->fetchAll();
You can't use both named and question-mark placeholders in the same query. Change the :symptoms placeholder to ? and insert $symptoms at the beginning of your $keywords array.
You're never using $where_keywords. You can concatenate the % wildcard in the SQL code itself.
$symptoms=$_POST['search'];
$keywords = preg_split('/[\s]+/', $symptoms);
$totalKeywords = count($keywords);
$sql = "SELECT * FROM TABLE_3 WHERE MATCH (symptoms) AGAINST (?) ";
$like_placeholder = implode(' AND ', array_fill(0, $totalKeywords, "symptoms LIKE CONCAT('%', ?, '%')"));
$sql .= " AND ({$like_placeholder})"; // build the query with placeholders
$stmt = $pdo->prepare($sql);
array_unshift($keywords, $symptoms);
$stmt->execute($keywords);
$results = $stmt->fetchAll();

how to search multiple username in a single query

I want to search for some username in my database like this->
$skip = $_POST['username'];
$_SESSION['skip_user'] = array();
array_push($_SESSION['skip_user'],$skip);
$str = $_SESSION['skip_user'];
$string = rtrim(implode(',', $str), ',');
Now string variable looks like "name1, name2, name3";
mysqli_query($db, "SELECT * FROM users WHERE username in ({$string}) ORDER BY id DESC");
This fetches the users but i don't want these users. I mean is there any query where i can i write WHERE username !in ({$string})!
get all users except "name1, name2, name3" these users
Now after adding NOT IN I'm receiving error
mysqli_query($db, "SELECT * FROM users WHERE username NOT IN ({$string}) ORDER BY id DESC")or die(mysqli_error($db)); php is giving error Unknown column 'name1' in 'where clause'
Try NOT IN in the SQL query.
First though try to add quotes to the values you are trying in the NOT IN part of the sql query.
$str = '';
foreach ($_SESSION['skip_user'] AS $word) {
$str .= "'$word',";
}
$str = rtrim($str, ',');
Then use this $str in your query. Also, try to make a habit out of using `` for column names, like this:
SELECT `SOMETHING` FROM `TABLE_NAME` WHERE <CONDITION>
I hope that helps!
You should use NOT IN to exclude certain values.
mysqli_query($db, "SELECT * FROM users WHERE username NOT IN ('name1', 'name2') ORDER BY id DESC");
yep, just type "not" instead of "!"
select * from table where junk not in ('item1', 'item2', 'item3');
1) You have a few other problems though you're not adding quotes to your implode:
// you need quotes here
$string = implode("','", $str);
// And here
mysqli_query($db, "SELECT * FROM users WHERE username in ('{$string}') ORDER BY id DESC");
However, this is what you should really be doing.
2) You should bind your parameters instead as you're open to SQL injection:
$params = array();
$params[0] = "";
$sql = "SELECT * FROM users WHERE username NOT IN (";
foreach($str as $s){
$params[0] .= "s";
array_push($params, $s);
$sql .= "?, ";
}
$sql = rtrim($sql, " ,").") ORDER BY id DESC";
$stmt = $conn->prepare($sql);
// this is the same as doing: $stmt->bind_param('s', $param);
call_user_func_array(array($stmt, 'bind_param'), $params);
// execute and get results
$stmt->execute();

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(); //

Multiple keywords search

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);
//................
?>

Categories