I'm trying to build a MySQL search to match keywords occurring in any order in the column being searched (not just whole phrases as would normally be the case). My class function is:
public function keywords_author($keywords, $author) {
$keywords = explode(" ", trim($keywords));
$keywords = array_filter($keywords);
$count_keywords = count($keywords);
if ($count_keywords != 0) {
$query = "SELECT * FROM `table` WHERE ";
$query_echo = $query;
$a = 0;
while ($a < $count_keywords) {
$query .= "`column` LIKE :keyword ";
$query_echo .= "`column` LIKE '%" . $keywords[$a] . "%' ";
$a++;
if ($a < $count_keywords) {
$query .= " && ";
$query_echo .= " && ";
}
}
$stmt = $this->db->prepare($query);
for ($a=0; $a<$count_keywords; $a++) {
$keyword = "%" . $keywords[$a] . "%";
$stmt->bindParam(':keyword', $keyword);
}
$stmt->execute();
$output = '';
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
// build $output
}
echo $output;
echo $query_echo;
}
}
I have just added $query_echo to check the query being built, which is:
SELECT * FROM `table`
WHERE `column` LIKE '%php%'
&& title LIKE '%mysql%'
&& title LIKE '%jquery%'
&& title LIKE '%ajax%'
This works fine when I copy that into the SQL command line in phpMyAdmin, returning only those records where ALL keywords are matched, but when I run the class file in my site it behaves like an OR select and returns results where ANY of the keywords occurs in the column.
I'm confused! Any ideas what's going on would be a huge help!
David -
Thanks, David Kmenta - that's certainly a step in the right direction and now I'm getting the correct query:
SELECT * FROM table WHERE column LIKE :keyword0 AND column LIKE :keyword1 AND column LIKE :keyword2 AND column LIKE :keyword3 AND column LIKE :keyword4
But it is still returning the result for the last value only. I am sure it is a basic, probably obvious error in the loop enclosing the new bindParam statement:
for ($a=0; $a<$count_keywords; $a++) {
$keyword = "%" . $keywords[$a] . "%";
$stmt->bindParam(':keyword'.$a, $keyword);
}
I'm very tired - can you spot the problem?
But
Problem is probably here:
for ($a=0; $a<$count_keywords; $a++) {
$keyword = "%" . $keywords[$a] . "%";
$stmt->bindParam(':keyword', $keyword);
}
Every occurrence of :keyword is replaced with last item in $keywords array.
Related
I know this may seem like a duplicated, out of stackoverflow, etc question but here we go.
I'm trying to make an SQL sentence that can find a coincidence between two strings
function getProductos($keyWords){
$keyWords = addslashes(strtolower($keyWords));
$keyWordsExploded = explode(" ",$keyWords);
$sql = "SELECT * FROM PRODUCTOS WHERE HOMBRE_MUJER LIKE :keyWords OR CATEGORIA LIKE :keyWords" OR NOMBRE LIKE :keyWords;
$query = self::$conn->prepare($sql);
$query->execute(array(":keyWords"=> "%" . $keyWords . "%"));
return $query;
}
In other part of the page I have this code:
<?php
if(isset($_GET['buscar'])){
require(PAGES_DIR . "queries_products.php");
$consultaProducts = new QueryProductos();
$productos = $consultaProducts->getProductos($_GET['buscar']);
if($productos->rowCount()!=0){
$arrayProductos = $productos->fetchAll(PDO::FETCH_ASSOC);
echo "<h3>Productos encontrados</h3>";
foreach($arrayProductos as $fila){
echo $fila['NOMBRE'] . " " . $fila['HOMBRE_MUJER'] . "<br>";
}
}else{
echo "<p class='alert alert-warning'>No results found <strong>" . $_GET['buscar'] . "</strong>";
}
}
?>
Everything works fine, In my database I store only 2 values in CATEGORIA which are: "hombres" and "mujeres", if i search for hombres I get all records which have a CATEGORIA of hombres but when i search for hombres y mujeres I get no results, I have tried using different sentences that i read but I haven't had any luck, I hope you can greatly save me by helping me solve this problem.
try this:
function getProductos($keyWords) {
$keyWordsExploded = explode(" ",$keyWords);
$sql = "SELECT * FROM PRODUCTOS WHERE ";
$likes = array("HOMBRE_MUJER", "CATEGORIA", "NOMBRE"); // build up our columns which will be used in our condition in sql statment
$params = array();
// building up our sql statment and collecting our params
foreach($likes as $like) {
foreach($keyWordsExploded as $kw) {
$sql .="$like like ? or ";
$params[] = "%".$kw."%";
}
}
$sql = rtrim($sql, "or "); // trim the last "or" condition in sql statment
$query = self::$conn->prepare($sql);
$query->execute($params);
return $query;
}
My code let me perform search, as long as the order of the words is correct.
Let's say I'm searching for big dog, but I also want to search for dog big. It get more complicated with 3 or more words.
Is there a way to create a SQL query which would let me search through values with any order?
Only way I can think of this is by having multiple queries, where I change order of PHP variables manually...
<?php
if(isset($_GET['query']) && !empty($_GET['query'])) {
$query = $_GET['query'];
$query_array = explode(' ', $query);
$query_string = '';
$query_counter = 1;
foreach($query_array as $word) {
$query_string .= '%' . $word . (count($query_string) == $query_counter++ ? '%' : '');
}
$query = "SELECT * FROM pages WHERE Name LIKE '$query_string'";
$result = sqlsrv_query($cms->conn, $query);
while($row = sqlsrv_fetch_array($result)) {
extract($row);
echo ''.$Name.'<br>';
}
sqlsrv_free_stmt($stmt);
}
else {
//echo 'NO GET';
}
?>
You could assemble your conditions and check for each word on it's own:
$query_array = explode(' ', $query);
$queryParts = array();
foreach ($query_arra AS $value){
$queryParts[]="Name like '%".mysql_real_escape_string($value)."%'";
}
$searchString = implode(" AND ", $queryParts);
The Search string would now be Name like '%big%' AND Name like '%dog%' ... depending on how much search-keywords have been there.
I use the same approach very often, also when it is required that ALL keywords appear in at least ONE of the columns. Then you need one more loop to create the required AND conditions:
$search = "Big Dog";
$keywords = explode (" ", $search);
$columns = array("Name", "description");
$andParts = array();
foreach ($keywords AS $keyword){
$orParts = array();
foreach($columns AS $column){
$orParts[] = $column . " LIKE '%" . mysql_real_escape_string($keyword) . "%'";
}
$andParts[]= "(" . implode($orParts, " OR ") . ")";
}
$and = implode ($andParts, " AND ");
echo $and;
this would produce the query part (Name like '%Big%' OR description like '%Big%') AND (Name like '%Dog%' or description like '%Dog%')
So, it will find any row, where dog and big are appearing in at least one of the columns name or description (could also be both in one column)
Since your original querystring is something like %big%dog%, so I assume you are okay with matching big wild dog. In this case, you can just use the AND operator.
(Name LIKE '%big%" and Name LIKE '%dog%")
myisam supports full text search:
http://dev.mysql.com/doc/refman/5.0/en/fulltext-search.html
One thing you could look into is Full Text Search for ms sql server.
https://msdn.microsoft.com/en-us/library/ms142571.aspx
it's similar to a "search engine" in that it works off of an algorithm to rank results and even similar words (think thesaurus type lookups)
It's not exactly trivial to set up, but it's easy enough to find a tutorial on the subject and how to query from FTS (as the syntax is different than say LIKE '%big%dog%')
Here's a sample query from the page linked above:
SELECT product_id
FROM products
WHERE CONTAINS(product_description, ”Snap Happy 100EZ” OR FORMSOF(THESAURUS,’Snap Happy’) OR ‘100EZ’)
AND product_cost < 200 ;
I have to create a dynamic query based on the value received by the user's input, the value of the variables are posted by GET
When I simply run this
$qry = "SELECT* FROM LAPTOP WHERE 1=1";
$resul = mysqli_query($qry);
retrieve($resul);
all the content of this table are displayed without any error,(retrieve function here displays all the results based on the query) but when I try to modify it like this, I get a blank page
$qry = "SELECT * FROM LAPTOP WHERE 1=1";
if(!empty($company))
{
$qry .= " AND company='$company'" ;
}
if(!empty($cpu))
{
$qry.= " AND cpu='$cpu' " ;
}
if(!empty($lifestyle))
{
$qry.= " AND lifestyle='$lifestyle' " ;
}
if(!empty($display))
{
$qry.= " AND display='$display'" ;
}
if(!empty($ram))
{
$qry.= " AND ram='$ram' " ;
}
if(!empty($HDD))
{
$qry.= " AND HDD='$HDD' " ;
}
echo $qry;
$result= mysqli_query($qry) || die(mysqli_error()) ;
retrieve($result) ;
$p = basename($_SERVER['REQUEST_URI']) ;
The result of echo $qry; is as expected, it displays this
SELECT * FROM LAPTOP WHERE 1=1 AND company='Asus' AND cpu='intel i3'
Is there a way to correct this? The reason I tried using WHERE 1=1 clause is that when all the variables are equal to NULL then the query returns all the rows from the table.
i guess you have no data in your database matched with your conditions . OR you have case sensitive with names.
example :
cpu='Intel i3' // with big I
cpu='intel I3' // with big I
cpu='intel i3' // double space.
OR if you have big string , think to use LIKE
$qry.= " AND cpu LIKE '%$cpu%' " ;
Is this a typo also ($resul_T_)?
$resul = mysqli_query($qry);
retrieve($resul);
How you managed to lose that 't' and later get it back? ;)
As others have pointed out it may simply be that your query does not match any records.
Anyway what I usually do in a similar case is put all the conditions in an array, and then implode the array with 'AND'. That way you don't have to bother with 1=1 and it doesn't matter whether you have 0, 1 or more conditions.
<?php
$qry = "SELECT * FROM LAPTOP";
$conditions = array();
$cpu = 'Intel';
$ram = '24GB';
if(!empty($cpu))
{
$conditions[] = "cpu='$cpu'";
}
if(!empty($lifestyle))
{
$conditions[] = "lifestyle='$lifestyle'";
}
if(!empty($display))
{
$conditions[] = "display='$display'";
}
if(!empty($ram))
{
$conditions[] = "ram='$ram'";
}
if(!empty($HDD))
{
$conditions[] = "HDD='$HDD'";
}
if( count( $conditions ) > 0 )
{
$qry .= " WHERE ";
$qry .= implode( " AND ", $conditions );
}
print_r($qry);
?>
I am working on a site that allows users to list boats and yachts for sale. There is a mysql database that has a table "yachts" and among other fields ther are "make" and "model".
When people come to the site to look for boats for sale there is a search form, one of the options is to enter the make and/or model into a text field. The relevant where clause on the results page is the following
WHERE ( make LIKE '%$yacht_make%' OR model LIKE '%$yacht_make%')
This is working if someone enters either the make or model but not if they enter both.
For example, if someone enters "Jeanneau", the make, it finds the boat with that make, or if they enter "Sun Odyssey", the model, it finds the boat of that model, but if they enter "Jeanneau Sun Odyssey" it comes up empty.
Is there is a way to write a query where all three ways of entering the above search criteria would find the boat?
Here is the site http://yachtsoffered.com/
Thanks,
Rob Fenwick
Edit:
The query is built with a php script here is the script
if(!empty($yacht_id)) {
$where = " WHERE yacht_id = $yacht_id ";
} else {
$where = " WHERE ( make LIKE '%$yacht_make%' OR model LIKE '%$yacht_make%') ";
if(!empty($year_from) && !empty($year_to)){
$where .= "AND ( year BETWEEN $year_from AND $year_to ) ";
}
if(!empty($length_from) && !empty($length_to)){
$where .= "AND ( length_ft BETWEEN $length_from AND $length_to ) ";
}
if(!empty($price_from) && !empty($price_to)){
$where .= "AND ( price BETWEEN $price_from AND $price_to ) ";
}
if ($sail_power != 2){
$where .= "AND ( sail_power = $sail_power ) ";
}
if (count($material_arr) > 0){
$material = 'AND (';
foreach ($material_arr as $value) {
$material .= ' material LIKE \'%' . $value . '%\' OR';
}
$material = substr_replace ( $material , ') ' , -2 );
$where .= $material;
}
if (count($engine_arr) > 0){
$engine = 'AND (';
foreach ($engine_arr as $value) {
$engine .= ' engine LIKE \'%' . $value . '%\' OR';
}
$engine = substr_replace ( $engine , ') ' , -2 );
$where .= $engine;
}
if (count($type_arr) > 0){
$type = 'AND (';
foreach ($type_arr as $value) {
$type .= ' type LIKE \'' . $value . '\' OR';
}
$type = substr_replace ( $type , ') ' , -2 );
$where .= $type;
}
if (count($region_arr) > 0){
$region = 'AND (';
foreach ($region_arr as $value) {
$region .= ' region LIKE \'' . $value . '\' OR';
}
$region = substr_replace ( $region , ') ' , -2 );
$where .= $region;
}
$where .= 'AND ( active = 1 ) ORDER BY yacht_id DESC';
}
$sql = "SELECT * FROM $tbl_name $where LIMIT $start, $limit";
$result = mysql_query($sql);
There are many ways to do it, the easiest one in my opinion is:
$search = preg_replace('/\s+/','|', $yacht_make);
$sql = "select * from yacht where concat_ws(' ',make,model) rlike '$search'";
This replaces all whitespace with |, that is used as OR in regexp-powered-like query on concatenation of all searchable fields. The speed of it may be questionable in heavy trafic sites but is quite compact and easy to add more fields.
Your problem is that in your query you are searching for the exact substring "Jeanneau Sun Odyssey", which is neither a make nor a model.
The easiest solution is to use to separate input boxes for make and model. But if you really need to use a single input box, your best bet would be to split on spaces and add clauses for each separate word, so your query will end up looking something like
WHERE make like '%sun%' OR model like '%sun%'
OR make like '%odyssey%' OR model like '%odyssey%'
OR make like '%Jeanneau%' OR model like '%Jeanneau%'
Thanks everyone I ended up using dev-null-dweller's solution above
I revised my code as following,
to get the value and escape here is the code,
if (isset($_GET['yacht_make'])) {
$yacht_make = cleanString($_GET['yacht_make']);
$yacht_make = preg_replace('/\s+/','|', $yacht_make);
} else {
$yacht_make = NULL;
}
And I revised the first few lines of the php query building code I posted above to read,
if(!empty($yacht_id)) {
$where = " WHERE yacht_id = $yacht_id ";
} else {
$where = " WHERE ( yacht_id LIKE '%' )";
if(!empty($yacht_make)){
$where .= "AND ( CONCAT_WS(' ',make,model) RLIKE '$yacht_make') ";
}
It is working nicely, although it brings up more results than I would like for "Jeanneau Sun Odyssey" as it brings up "Bayliner Sunbridge", I assume because it is matching "Sun".
But it is a big improvement from what I had.
Thanks All
Rob Fenwick
You could also use:
WHERE make LIKE '%$yacht_make%'
OR model LIKE '%$yacht_make%'
OR '$yacht_make' LIKE CONCAT('%', make, '%', model, '%')
OR '$yacht_make' LIKE CONCAT('%', model, '%', make, '%')
It will not be very efficient and still not catch all possibilities, e.g. if the user provide the make and a part of the model name, like 'Jeanneau Odyssey' or in wrong order: Sun Jeanneau Odyssey.
After checking out your site it appears you are only taking input from one text field and searching for that string in each field.
So, you can either use full text searches (linkie) or you could split your string by spaces and generate a WHERE cause on the fly. Here is a rough example:
$searchterms = explode (' ', $input);
$sql .= "... WHERE"; /* obviously need the rest of your query */
foreach ($searchterms as $term) {
if ((substr ($string, -5) != 'WHERE') &&
(substr ($string, -3) == ' ||') { $sql .= " ||"; }
$sql .= " make LIKE '%$term%' || model LIKE '%$term%'";
}
I am trying to check if the POST or the GET has my search variables and then add the variables to my query. I then want to pass the array name of those variables into the URL for paginating my search results. With someone's help, this is how far I have gone.
$criteria = array('ctitle', 'csubject', 'creference', 'cat_id', 'cmaterial', 'ctechnic', 'cartist', 'csource', 'stolen');
$likes = "";
$url_criteria = '';
foreach ( $criteria AS $criterion ) {
if ( ! empty($_POST[$criterion]) ) {
$value = ($_POST[$criterion]);
$likes .= " AND `$criterion` = '%$value%'";
$url_criteria .= '&'.$criterion.'='.htmlentities($_POST[$criterion]);
} elseif ( ! empty($_GET[$criterion]) ) {
$value = mysql_real_escape_string($_GET[$criterion]);
$likes .= " AND `$criterion` = '%$value%'";
$url_criteria .= '&'.$criterion.'='.htmlentities($_GET[$criterion]);
}
}
$sql = "SELECT * FROM collections WHERE c_id>0" . $likes . " ORDER BY c_id ASC";
echo $sql;
The problem I have here is that after modifying the query I had before, any criteria I use to search does not return any records even when those records exist. I also echoed thequery and it printed the following line:
SELECT * FROM collections WHERE c_id>0 AND `cmaterial` = '%wood%' ORDER BY c_id ASC
Please, what am I missing here?
You should use LIKE keyword instead of = when concatenating parts of criteria. Your condition means searching for exact match including % symbols, while LIKE means searching by pattern.
$likes .= " AND `$criterion` LIKE '%$value%'";