Advance Searching, PHP & MySQL - php

I'm trying to create an Advanced Searching form that sort of look like this ;
http://img805.imageshack.us/img805/7162/30989114.jpg
but what should I write for the query?
I know how to do it if there is only two text box but three, there's too many probability that user will do.
$query = "SELECT * FROM server WHERE ???";
What should I write for the "???"
I know how to use AND OR in the query but lets say if the user only fill two of the textbox and one empty. If I write something like this ;
$query = "SELECT * FROM server WHERE model='".$model."' and brand='".$brand."' and SN='".$SN.'" ";
The result will return as empty set. I want the user can choose whether to fill one,two or three of the criteria. If I use OR, the result will not be accurate because if Model have two data with the same name (For example :M4000) but different brand (For example : IBM and SUN). If I use OR and the user wants to search M4000 and SUN, it will display both of the M4000. That's why it is not accurate.

If the user can decide how many criteria he wants to enter for your search and you want to combine those criteria (only those actually filled by the user), then you must dynamically create your SQL query to include only those fields in the search that are filled by the user. I'll give you an example.
The code for a simple search form could look like this:
$search_fields = Array(
// field name => label
'model' => 'Model',
'serialNum' => 'Serial Number',
'brand' => 'Brand Name'
);
echo "<form method=\"POST\">";
foreach ($search_fields as $field => $label) {
echo "$label: <input name=\"search[$field]\"><br>";
}
echo "<input type=\"submit\">";
echo "</form>";
And the code for an actual search like this:
if (isset($_POST['search']) && is_array($_POST['search'])) {
// escape against SQL injection
$search = array_filter($_POST['search'], 'mysql_real_escape_string');
// build SQL
$search_parts = array();
foreach ($search as $field => $value) {
if ($value) {
$search_parts[] = "$field LIKE '%$value%'";
}
}
$sql = "SELECT * FROM table WHERE " . implode(' AND ', $search_parts);
// do query here
}
else {
echo "please enter some search criteria!";
}
In the above code we dynamically build the SQL string to do a search ("AND") for only the criteria entered.

Try this code
<?php
$model="";
$brand="";
$serialNum="";
$model=mysql_real_escape_string($_POST['model']);
$brand=mysql_real_escape_string($_POST['brand']);
$serialNum=mysql_real_escape_string($_POST['serialNum']);
$query=" select * from server";
$where_str=" where ";
if($model == "" && $brand == "" && $serialNum == "")
{
rtrim($where_str, " whrere ");
}
else
{
if($model != "")
{
$where_str.= " model like '%$model%' AND ";
}
if($brand != "")
{
$where_str.= " brand like '%$brand%' AND ";
}
if($serialNum != "")
{
$where_str.= " serialNum like '%$serialNum%' AND ";
}
rtrim($where_str, " AND ");
}
$query.= $where_str;
$records=mysql_query($query);
?>

For those framiliar with mysql, it offers the ability to search by regular expressions (posix style). I needed an advanced way of searching in php, and my backend was mysql, so this was the logical choice. Problem is, how do I build a whole mysql query based on the input? Here's the type of queries I wanted to be able to process:
exact word matches
sub-string matches (I was doing this with like "%WORD%")
exclude via sub-string match
exclude via exact word match
A simple regexp query looks like:
select * from TABLE where ROW regexp '[[:<:]]bla[[:>:]]' and ROW
regexp 'foo';
This will look for an exact match of the string "bla", meaning not as a sub-string, and then match the sub-string "foo" somewhere.
So first off, items 1 and 4 are exact word matches and I want to be able to do this by surrounding the word with quotes. Let's set our necessary variables and then do a match on quotes:
$newq = $query; # $query is the raw query string
$qlevel = 0;
$curquery = "select * from TABLE where "; # the beginning of the query
$doneg = 0;
preg_match_all("/\"([^\"]*)\"/i", $query, $m);
$c = count($m[0]);
for ($i = 0; $i < $c; $i++) {
$temp = $m[1][$i]; # $temp is whats inside the quotes
Then I want to be able to exclude words, and the user should be able to do this by starting the word with a dash (-), and for exact word matches this has to be inside the quotes. The second match is to get rid of the - in front of the query.
if (ereg("^-", $temp)) {
$pc = preg_match("/-([^-]*)/i", $m[1][$i], $dm);
if ($pc) {
$temp = $dm[1];
}
$doneg++;
}
Now we will set $temp to the posix compliant exact match, then build this part of the mysql query.
$temp = "[[:<:]]".$temp."[[:>:]]";
if ($qlevel) $curquery .= "and "; # are we nested?
$curquery .= "ROW "; # the mysql row we are searching in
if ($doneg) $curquery .= "not "; # if dash in front, do not
$curquery .= "regexp ".quote_smart($temp)." ";
$qlevel++;
$doneg = 0;
$newq = ereg_replace($m[0][$i], "", $newq);
}
The variable $newq has the rest of the search string, minus everything in quotes, so whatever remains are sub-string search items falling under 2 and 3. Now we can go through what is left and basically do the same thing as above.
$s = preg_split("/\s+/", $newq, -1, PREG_SPLIT_NO_EMPTY); #whitespaces
for ($i = 0; $i < count($s); $i++) {
if (ereg("^-", $s[$i])) { # exclude
sscanf($s[$i], "-%s", $temp); # this is poor
$s[$i] = $temp;
$doneg++;
}
if ($qlevel) $curquery .= "and ";
$curquery .= "ROW "; # the mysql row we are searching in
if ($doneg) $curquery .= "not ";
$curquery .= "regexp ".quote_smart($s[$i])." ";
$qlevel++;
$doneg = 0;
}
# use $curquery here in database
The variable $curquery now contains our built mysql query. You will notice the use of quote_smart in here, this is a mysql best practice from php.net. It's the only mention of security anywhere in this code. You will need to run your own checking against the input to make sure there are no bad characters, mine only allows alpha-numerics and a few others. DO NOT use this code as is without first fixing that.

You have to provide $model, $brand, $serial which come from your search-form.
$query = "SELECT * FROM `TABLE` WHERE `model` LIKE '%$model%' AND `brand` LIKE '%$brand%' AND `serial` LIKE '%$serial%'";
Also take a look at the mysql doc
http://dev.mysql.com/doc/refman/5.1/en/string-comparison-functions.html

A basic search would work like this:
"SELECT * FROM server WHERE column_name1 LIKE '%keyword1%' AND column_name2 LIKE '%keyword2%' .....";
This would be case for matching all parameters.For matching any one of the criteria, change ANDs to ORs

Related

Mysql search keyword with suffix in csv string stored in database

I have in mysql database table column keywords there are csv keywords like "hotel, new hotel, good hotel".
Now when user enter hotel it works(select data) but not for hotels(it shouldn't). Now I want user enter hotels then it should also match hotel keyword.
In-short with suffix search should work. currently i implemented following.
$queried = trim(mysqli_real_escape_string($con,$_POST['query']));
$keys = explode(" ",$queried);
$sql = 'SELECT name FROM image WHERE keyword LIKE "%$queried%"';
foreach($keys as $k){
$k= trim(mysqli_real_escape_string($con,$k));
if(count($keys) > 1)
{
$sql .= ' OR keyword LIKE "%$k%" ';
}
}
you'd have to (additionally) ask whether the search term contains any of the words in the rows. Currently you're doing the opposite (which is fine for the opposite situation, so don't get rid of it)
Something like:
$sql = 'SELECT name FROM image WHERE $queried LIKE "%" + keyword + "%"';
(Apologies if MySQL syntax isn't quite right, not used it for a while).
It might occasionally throw up unwanted things though, e.g. if the user wrote "aparthotel" it'd still return "hotel", you may or may not want that. Or it could even something entirely irrelevant depending on the words involved.
Once you get onto anything more complex than that though, you're probably into the realms of search engines and natural language processing.
i did this way it's not what i want but it works for my criteria.
$suffix = array('','s','es','ing','ment'); // suffix you want to ad
$sql = 'SELECT name FROM image WHERE keyword LIKE "%$queried%"';
foreach($keys as $k)
{
$k= trim(mysqli_real_escape_string($con,$k));
for ($i=1; $i < sizeof($suf) ; $i++)
{
if(substr($k, (-1 * strlen($suf[$i])))==$suf[$i])
{
$wp=substr( $k, 0, (-1 * $i));
}
}
if($wp!="")
{
$sql .= " OR keyword LIKE '%$k%' OR keyword LIKE '%$wp%' ";
}
}

PHP Search Engine no results are returned even if the input matches data in the table

I'm trying to create a search engine for my website where users can enter keywords and the PHP script will search the 'name' and 'description' columns of my fyp_items table. So far I have managed to break down the input to an array of words and am try to execute a SELECT query on my database.
THE ISSUE is that it fails to find the items even if the keyword matches that of the data in the table. Below is my script...I hope someone can help me.
if(empty($_POST)=== false){
$output = '';
$error = '';
$input = $_POST['search_input'];
$i=0;
if($input){
$keyword = explode(" ", $input);
require ('core/dbconnection.php');
//If a user is logged in check if the user is Admin or Customer.
if(isset($_SESSION['userid'])){
if($admin == 1){
}
}else{
//If user is not logged in search items table only.
$search_items = "SELECT * FROM fyp_items WHERE ";
foreach($keyword as $k){
$k = mysql_real_escape_string($k);
$i++;
if($i == 1){
$search_items .= "name LIKE '$k' OR description LIKE '$k'";
}else
$search_items .= " OR name LIKE '$k' OR description LIKE '$k'";
}
$item_qry = mysql_query("$search_items")or die(mysql_error());
$numrows = mysql_num_rows($item_qry);
if($numrows > 0){
$output = 'found it';
}else
$error = '<p class="pageerror">Sorry, what you were looking for was not found.</p>';
}
}else
$error = '<p class="pageerror">Please enter your search terms.</p>';
I have tested the post by echoing the output and I have also echoed the $search_items variable to get this...
http://awesomescreenshot.com/05c2fwytac
Your help will be much appreciated!
You're building an incorrect query string, that'll be something like
SELECT ... WHERE name LIKE 'foo' OR description LIKE 'foo'
a LIKE comparison without wilcards is pointless. You've functionally got the equivalent of
SELECT ... WHERE name='foo' OR description='foo'
The wildcards allow substring matching in the fields:
SELECT ... WHERE name='%foo%' OR description = '%foo%'
so the word foo can appear ANYWHERE in the field and match. Right now your query will only match if foo is the ONLY thing in either field.
And these sorts of queries are highly inefficient, especially as the number of search terms climbs. You should be suing a fulltext search: https://dev.mysql.com/doc/refman/5.0/en/fulltext-search.html

Query returns different results between PL/SQL Developer and PHP

I have made a small intranet website to collect and store data to be used to expedite our logistics processes. I'm now in the process of adding search functionality which, if records are found that match that criteria, will allow the user to quickly select parts of that data to pre-populate a new shipping request with data (e.g, the user types 'Mar' in the Recipient Name input textbox and '109' in the Street Address input textbox and the query returns two records: {"Mary Smith", "1090 South Central St"} and {"Mark Swanson", "109 E. 31st St."}).
At the moment, when search criteria is entered and submitted, the data returned from the query in PHP is 100% accurate if and only if a single criteria is entered (such as Recipient Name). When I attempt to use two different search criterias in PHP, the record results do not match the results when running the same query in Oracle PL/SQL Developer. If three different search criterias are used, the query ran in PHP will return 0 records. In all three of the aforementioned scenarios, the query is executed without error in Oracle PL/SQL Developer.
The following code is from my PHP search function. The input data to this function is an associate array of field names and the user inputted search criteria data for that field.
public function Search()
{
if($this->dbcon)
{
$query = "SELECT * FROM ship_request ";
$postCount = count($this->post_data);
$counter = 0;
if ($postCount > 0)
{
$query .= "WHERE ";
}
foreach ($this->post_data as $k => $v)
{
$counter++;
if (strlen($v) > 0)
{
if ($k == 'SR_DATE')
{
$query .= $k . " = :" . $k . " AND ";
} else {
$query .= "upper(" . $k . ") like upper(:" . $k . ") AND ";
}
}
}
if (substr($query,-4) == "AND ")
{
$query = substr($query, 0, strlen($query) - 4);
}
$stid = oci_parse($this->ifsdb, $query);
foreach ($this->post_data as $k => $v)
{
if (strlen($v) > 0)
{
if ($k == 'SR_DATE')
{
$this->post_data[$k] = date("d-M-y", strtotime($this->post_data[$k]));
$placeHolder = $this->post_data[$k];
} else {
$placeHolder = '%' . $this->post_data[$k] . '%';
}
oci_bind_by_name($stid, $k, $placeHolder);
}
}
oci_execute($stid);
$nrows = oci_fetch_all($stid, $recordsFound);
$recordsFound = json_encode($recordsFound);
oci_free_statement($stid);
echo $recordsFound;
} else {
die("Could not connect to database!");
}
}
}
I've done a var_dump on $query to see what my query actually looks like when I enter multiple search criteria values. This is an example of what I see:
select * from HOL_SHIP_REQUEST where upper(sr_shipper_name) like upper(:sr_shipper_name) and upper(sr_recipient_name) like upper(:sr_recipient_name) and sr_recipient_phone like upper(:sr_recipient_phone)
That query returns 0 records when I enter "a" for Shipper Name, "m" for Recipient Name, and "2" for Phone Number.
This query, when executed in Oracle PL/SQL Developer, however, returns 27 records.
select * from HOL_SHIP_REQUEST where upper(sr_shipper_name) like upper('%a%') and upper(sr_recipient_name) like upper('%m%') and sr_recipient_phone like upper('%2%')
Is there something wrong with the way that I'm trying to bind the parameters in PHP? Is there something different I have to do when using multiple like statements?
You've forgotten the % wildcard chars in your built query string. The DB interface libraries do NOT parse the query you're building, and do NOT look for LIKE clauses - it's not their job to guess what kind of match you're trying to do. e.g. are you doing
WHERE a LIKE 'b'
WHERE a LIKE 'b%'
WHERE a LIKE '%b'
WHERE a LIKE '%b%'
It's up to you to provide the appropriate wildcards, and since you're using placeholders, you'll have to do it yourself, e.g.
WHERE UPPER(sr_shipper_name) LIKE CONCAT('%', :sr_shipper_name, '%')
If you were to do it something like this:
$shipper = '%foo%';
WHERE ... LIKE :shipper
you'd end up with the equivalent of:
WHERE ... LIKE '\%foo\%'
The placeholder system also doesn't parse your provided text and try to figure out if you're really trying to use a wilcard or just passing in a literal % char. That's why you have to use the CONCAT hack to build a proper wildcarded construct.

mysql PHP query question

Ok, i have a problem here...
I am sending values of drop down lists via ajax to this PHP file.
Now I want to search a mysql database using these values, which I have managed to do, BUT, only if I set the values to something...
Take a look:
$query = "SELECT * FROM cars_db WHERE price BETWEEN '$cars_price_from' AND '$cars_price_to' AND year BETWEEN '$cars_year_from' AND '$cars_year_to' AND mileage BETWEEN '$cars_mileage_from' AND '$cars_mileage_to' AND gearbox = '$cars_gearbox' AND fuel = '$cars_fuel'";
now, what if the user doesnt select any "price_from" or "year_from"... The fields are only optional, so if the user doesnt enter any "price from" or "year from", then the user wants ALL cars to show...
Do I have to write a query statement for each case or is there another way?
I do something similar to davethegr8 except I put my conditions in an array and then implode at the end just so I don't have to worry about which conditions got added and whether I need to add extra AND's.
For example:
$sql = "SELECT * FROM car_db";
// an array to hold the conditions
$conditions = array();
// for price
if ($car_price_from > 0 && $car_price_to > $car_price_from) {
$conditions[] = "(price BETWEEN '$cars_price_from' AND '$cars_price_to')";
}
elseif ($car_price_from > 0) {
$conditions[] = "(price >= '$cars_price_from')";
}
elseif ($car_price_to > 0) {
$conditions[] = "(price <= '$cars_price_from')";
}
else {
//nothing
}
// similar for the other variables, building up the $conditions array.
// now append to the existing $sql
if (count($conditions) > 0){
$sql .= 'WHERE ' . implode(' AND ', $conditions);
}
You could simply detect which parameters are missing in your PHP code and fill in a suitable default. eg
if (!isset($cars_mileage_to))
$cars_mileage_to = 500000;
You can build you query, adding the "where" part only if your variables are different from "".
or if you're using mysql 5.x, you can also use subselects:
http://dev.mysql.com/doc/refman/5.0/en/subqueries.html
don't forget to validate the input. It's trivial with firebug, for example, to inject some tasty sql.

PHP/MySQL: Highlight "SOUNDS LIKE" query results

Quick MYSQL/PHP question. I'm using a "not-so-strict" search query as a fallback if no results are found with a normal search query, to the tune of:
foreach($find_array as $word) {
clauses[] = "(firstname SOUNDS LIKE '$word%' OR lastname SOUNDS LIKE '$word%')";
}
if (!empty($clauses)) $filter='('.implode(' AND ', $clauses).')';
$query = "SELECT * FROM table WHERE $filter";
Now, I'm using PHP to highlight the results, like:
foreach ($find_array as $term_to_highlight){
foreach ($result as $key => $result_string){
$result[$key]=highlight_stuff($result_string, $term_to_highlight);
}
}
But this method falls on its ass when I don't know what to highlight. Is there any way to find out what the "sound-alike" match is when running that mysql query?
That is to say, if someone searches for "Joan" I want it to highlight "John" instead.
Note that SOUNDS LIKE does not work as you think it does. It is not equivalent to LIKE in MySQL, as it does not support the % wildcard.
This means your query will not find "John David" when searching for "John". This might be acceptable if this is just your fallback, but it is not ideal.
So here is a different suggestion (that might need improvement); first use PHPs soundex() function to find the soundex of the keyword you are looking for.
$soundex = soundex($word);
$soundexPrefix = substr($soundex, 0, 2); // first two characters of soundex
$sql = "SELECT lastname, firstname ".
"FROM table WHERE SOUNDEX(lastname) LIKE '$soundexPrefix%' ".
"OR SOUNDEX(firstname) LIKE '$soundexPrefix%'";
Now you'll have a list of firstnames and lastnames that has a vague similarity in sounding (this might be a lot entries, and you might want to increase the length of the soundex prefix you use for your search). You can then calculate the Levenshtein distance between the soundex of each word and your search term, and sort by that.
Second, you should look at parameterized queries in MySQL, to avoid SQL injection bugs.
The SOUND LIKE condition just compares the SOUNDEX key of both words, and you can use the PHP soundex() function to generate the same key.
So, if you found a matching row and needed to find out which word to highlight, you can fetch both the firstname and lastname, and then use PHP to find which one matches and highlight just that word.
I made this code just to try this out. (Had to test my theory xD)
<?php
// A space seperated string of keywords, presumably from a search box somewhere.
$search_string = 'John Doe';
// Create a data array to contain the keywords and their matches.
// Keywords are grouped by their soundex keys.
$data = array();
foreach(explode(' ', $search_string) as $_word) {
$data[soundex($_word)]['keywords'][] = $_word;
}
// Execute a query to find all rows matching the soundex keys for the words.
$soundex_list = "'". implode("','", array_keys($data)) ."'";
$sql = "SELECT id, firstname, lastname
FROM sounds_like
WHERE SOUNDEX(firstname) IN({$soundex_list})
OR SOUNDEX(lastname) IN({$soundex_list})";
$sql_result = $dbLink->query($sql);
// Add the matches to their respective soundex key in the data array.
// This checks which word matched, the first or last name, and tags
// that word as the match so it can be highlighted later.
if($sql_result) {
while($_row = $sql_result->fetch_assoc()) {
foreach($data as $_soundex => &$_elem) {
if(soundex($_row['firstname']) == $_soundex) {
$_row['matches'] = 'firstname';
$_elem['matches'][] = $_row;
}
else if(soundex($_row['lastname']) == $_soundex) {
$_row['matches'] = 'lastname';
$_elem['matches'][] = $_row;
}
}
}
}
// Print the results as a simple text list.
header('content-type: text/plain');
echo "-- Possible results --\n";
foreach($data as $_group) {
// Print the keywords for this group's soundex key.
$keyword_list = "'". implode("', '", $_group['keywords']) ."'";
echo "For keywords: {$keyword_list}\n";
// Print all the matches for this group, if any.
if(isset($_group['matches']) && count($_group['matches']) > 0) {
foreach($_group['matches'] as $_match) {
// Highlight the matching word by encapsulatin it in dashes.
if($_match['matches'] == 'firstname') {
$_match['firstname'] = "-{$_match['firstname']}-";
}
else {
$_match['lastname'] = "-{$_match['lastname']}-";
}
echo " #{$_match['id']}: {$_match['firstname']} {$_match['lastname']}\n";
}
}
else {
echo " No matches.\n";
}
}
?>
A more generalized function, to pull out the matching soundex word from a strings could look like:
<?php
/**
* Attempts to find the first word in the $heystack that is a soundex
* match for the $needle.
*/
function find_soundex_match($heystack, $needle) {
$words = explode(' ', $heystack);
$needle_soundex = soundex($needle);
foreach($words as $_word) {
if(soundex($_word) == $needle_soundex) {
return $_word;
}
}
return false;
}
?>
Which, if I am understanding it correctly, could be used in your previously posted code as:
foreach ($find_array as $term_to_highlight){
foreach ($result as $key => $result_string){
$match_to_highlight = find_soundex_match($result_string, $term_to_highlight);
$result[$key]=highlight_stuff($result_string, $match_to_highlight);
}
}
This wouldn't be as efficient tho, as the more targeted code in the first snippet.

Categories