Php mysql match (multiple) words in multiple columns with spaces - php

I am trying to figure out how and what function i need for my query, still not sure on using Like, concat or what part etc.
My situation is as such
1.) I have multiple columns(Country, City, State, Location)
2.) Only 1 search input
3.) Search input can be 1 word, or multiple words also ignore spacing(e.g. "Center City or CenterCity or Center City Philadelphia) etc
And it will return the rows that matches the words from the different columns.
Below is my attempt, but it is not returning anything at the moment. Thanks for your time
Php:
<?php
ini_set('display_errors', 1); error_reporting(E_ALL);
include 'connect.php';
if($_POST)
{
$searchaddress = ($_POST['searchaddress']);
$result=mysqli_query($con,"SELECT *
FROM Listing WHERE CONCAT(country,state,city,Location) LIKE '%$searchaddress%' AND Status='Open'") or die( mysqli_error($con));
$output = array();
// fetch your results
while( $row = mysqli_fetch_assoc($result) )
{
// add result row to your output's next index
$output[] = $row;
}
// echo the json encoded object
echo json_encode( $output );
}
?>

Without knowing your exact data and what $searchaddress is like, it is hard to tell why it fails.
You are talking about ingoring whitespaces, but just pass in a single searchtag - and the expression LIKE '%something something else%' will not ignore whitespaces.
If you want to have the least amount of results with all given words matching, you should put in more effort and use a or/and combination of searchtags / columns. You can do this programmatically.
Assuming, you have 2 keywords entered: Center Detroid, you basically want to generate the searchquery:
FROM Listing WHERE
(
country LIKE '%Center%' OR
state LIKE '%Center%' OR
city LIKE '%Center%' OR
Location LIKE '%Center%'
)
AND
(
country LIKE '%Detroid%' OR
state LIKE '%Detroid%' OR
city LIKE '%Detroid%' OR
Location LIKE '%Detroid%'
)
To achieve that, you need to know two things:
The fieldnames you want to search in.
The keywords.
Then, the following snippet will generate the where part as required:
$search = "Detroid City Center";
$keywords = explode (" ", $search);
$columns = array("country", "state", "city", "location");
$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;
The example given in the array would produce
(
country LIKE '%Center%' OR
state LIKE '%Center%' OR
city LIKE '%Center%' OR
location LIKE '%Center%'
)
AND
(
country LIKE '%City%' OR
state LIKE '%City%' OR
city LIKE '%City%' OR
location LIKE '%City%'
)
AND
(
country LIKE '%Detroid%' OR
state LIKE '%Detroid%' OR
city LIKE '%Detroid%' OR
location LIKE '%Detroid%'
)
This will Match ANY row, where Center, City or Detroid is appearing AT LEAST ONCE in ONE of all (search-)fields per row.

Updated answer for searching each word in the address fields:
$searchaddress = "some address to find";
$address_parts = explode(" ", trim($searchaddress));
$sql_parts = array();
foreach($address_parts as $part) {
$sql_parts[] = 'full_address LIKE "%'.$part.'%"';
}
$query = 'SELECT *, CONCAT(country,state,city,Location) AS full_address FROM Listing WHERE `Status` = "Open" HAVING '.implode(' OR ', $sql_parts);

Related

How to search multiple words?

I was trying to make a search engine so far I had done with it and the feature I lack in it was, when user queries a long sentence or two or three words with on not in line-wise in my DB is not going to show and it shows 0 results.
So far it is working fine with the one-word search it is doing a great job with that.
public function getResultsHtml($page, $pageSize, $term){
$fromLimit = ($page - 1) * $pageSize;
//page 1: (1-1) * 20
//page 2:(2-1) * 20
$query = $this->con->prepare("SELECT *
FROM sites WHERE title LIKE :term
OR url LIKE :term
OR keywords LIKE :term
OR description LIKE :term
ORDER BY clicks DESC
LIMIT :fromLimit,:pageSize");
$searchTerm = "%" . $term ."%";
$query->bindParam(":term",$searchTerm);
$query->bindParam(":fromLimit",$fromLimit,PDO::PARAM_INT);
$query->bindParam(":pageSize",$pageSize,PDO::PARAM_INT);
$query->execute();
$resultsHtml ="<div class='siteResults'>";
while($row = $query->fetch(PDO::FETCH_ASSOC)){
$id = $row["id"];
$url = $row["url"];
$title = $row["title"];
$description = $row["description"];
$title = $this->trimField($title,55);
$description = $this->trimField($description,230);
$resultsHtml .="<div class='resultContainer'>
<h3 class='title'>
<a class='result' href='$url' data-linkId='$id'>
$title
</a>
</h3>
<span class='url'>$url</span>
<span class='description'>$description</span>
</div>";
}
$resultsHtml .= "</div>";
return $resultsHtml;
}
So when user searches apple it is retrieving the data and we can see the search result in "apple store search" in the first image.But in second image when we search "apple search" it should be able to show us the "apple store search".
first image
Second image
First you need to breakdown your $term into separated words. With European languages, you can simply use explode:
<?php
$terms = explode(' ', $term);
// lets say your $term is 'apple orange lemon banana'
// $terms would be ['apple', 'orange', 'lemon', 'banana']
Prepare Terms List for Binding
Then, I'd build a key-value array for the terms binding:
<?php
// build a term list with term key (:term{number}) and associated term
// for binding
$term_params = [];
foreach ($terms as $key => $term) {
$term_params[":term{$key}"] = "%{$term}%";
}
// $term_params would be:
// [
// ':term0' => '%apple%',
// ':term1' => '%orange%',
// ':term2' => '%lemon%',
// ':term3' => '%banana%',
// ]
Prepare the Where Clause in SQL for Binding
Now, supposed the logics is all the terms need to show up in either of the target fields:
<?php
// a list of fields to search from
$fields = ['title', 'url', 'keywords', 'description'];
// build a where clause SQL based on the terms and fields
$or_where_clauses = [];
foreach (array_keys($term_params) as $term_key) {
$or_where_clauses[] = implode(' OR ', array_map(function ($field) use ($term_key) {
return "{$field} LIKE {$term_key}";
}, $fields));
}
$where_clauses_sql = implode(' AND ', array_map(function ($term_clause) {
// quote each term clause with bracket for proper OR logic to work
return '(' . $term_clause . ')';
}, $or_where_clauses);
The resulting where clauses sql would be:
(title like :term0 OR url like :term0 OR keywords like :term0 OR description like :term0)
AND
(title like :term1 OR url like :term1 OR keywords like :term1 OR description like :term1)
AND
...
...
(title like :termN OR url like :termN OR keywords like :termN OR description like :termN)
Putting Everything Together
I can then build the SQL string and bind the terms to the query accordingly:
<?php
// prepare the select statement
$query = $this->con->prepare("SELECT *
FROM sites WHERE {$where_clauses_sql}
ORDER BY clicks DESC
LIMIT :fromLimit,:pageSize");
// bind the terms
foreach ($term_params as $key => $value) {
$query->bindParam($key, $value);
}
$query->bindParam(":fromLimit", $fromLimit, PDO::PARAM_INT);
$query->bindParam(":pageSize", $pageSize, PDO::PARAM_INT);
$query->execute();
// ...
Shortcomings and Potential Solution
Please note that this approach does not understand the number of occurrence of terms in the field. And the way to separate words is far from perfect. For example:
It didn't handle punctuation marks.
It couldn't search for plural terms with the singular, or visa verse.
It could be matching part of a word by mistake (e.g. search "sing" and match "dressing").
It couldn't deal with CJK or languages without space).
You can use software like Elastic Search for better feature. With plugin and proper config, you can give even sort results with relevance, or give different field a different importance in the search process etc.
But that is an entirely different software than SQL server to use. You'd need to learn and plan how to index your contents there as well as just saving your data to SQL.
we dont know how u get search words from user, but as i guess u get them as an array. so you can try below code:
<?php
...
$textSearch = "";
for($i = 0 ; $i< count($userInputs);$i++){
if($i !== count($userInputs) -1){
$userInputData = $userInputs[$i];
$textSearch .= "'%$userInputData%' OR";
}else{
$textSearch .= "'%$userInputData%'";
}
}
and put $textSearch into your query.
remember , $userInputs is an array that u had before.
UPDATE
as your images shown, you can add $userInput = explode(" ",$textFromUser) in very begin of given code.

php/sqlite3 database query - duplicates ignored

Using single Chinese characters in my search.
分坨坨 is my example here. The last two (坨坨) are completely the same - duplicates, if you will.
My first variable is $where which looks like this:
$where = array();
foreach ( $qtwo as $word ) {
$where[] = "CHS LIKE '%" . $word . "%'";
}
$where = implode(' OR ', $where);
Which prints:
CHS LIKE '%分%' OR CHS LIKE '%坨%' OR CHS LIKE '%坨%'
(The following is not really consequential but helps to explain my variables:)
I get them into an array called $where3 - which prints like this:
ORDER BY CASE CHS
WHEN '分' THEN 1
WHEN '坨' THEN 2
WHEN '坨' THEN 3
My query looks like this:
{$results4 = $db->query("SELECT * FROM FOUR WHERE $where $where3
END;");
while ($row4 = $results4->fetchArray()) {
So they print in the order that they came in - and all duplicates are represented in both variables.
When I run the query though - only the first of the duplicates gets printed back (坨).
How can I get it to print both of the duplicates?

PHP: search comma separated string in mysql?

I have a mysql query which simply looks into mysql to find LIKE strings and displays the result.
Within the same mysql query, I have 2 LIKE.
1 is always a single string and the other one can be single and sometimes multiple strings separated by commas.
when I use my code, I get no results at all even though I have all the fields in the mysql database and I also have all the search strings in the columns.
This is my code:
$area = 'London';
$res = 'santandar, HSBC, RBS, ';
$sql = "SELECT * FROM banks WHERE location LIKE '%$area%' AND name LIKE '%$res'";
I also tried it with preg_match and it didn't return anything:
$sql = "SELECT * FROM banks WHERE location LIKE '%$area%' AND name LIKE '".preg_match($res)."'";
If I remove the second LIKE and my code looks like below, it works just fine:
sql = "SELECT * FROM banks WHERE location LIKE '%$area%'";
So the issue starts when I try to search using a comma separated string.
Could someone please advise on this issue?
EDIT:
The PHP varibles are POSTS so they can be anything in each post.
they are like so:
$area = $_POST['area'];
$res = $_POST['res'];
you should use an OR condition:
$res_array = explode(',' $res)
$num_elem= count($res_array) // with this value you can build dinamically the query
"SELECT * FROM banks WHERE location LIKE '%$area%'
AND ( name LIKE concat('%', $res_array[0]),
OR LIKE concat('%', $res_array[1])
OR LIKE concat('%', $res_array[2]) ";
You are going to need to blow this out into separate LIKEs with an OR, such as:
...WHERE location LIKE '%{$area}' AND (name LIKE '%{$name1}%' OR name LIKE '%{$name2}' OR ...)
You could write this fairly simply with some PHP logic:
function build_like_or( $values, $field_name ) {
// Create an array from the comma-separated values
$names = explode( ',', $values );
// Trim all the elements to remove whitespaces
$names = array_map( 'trim', $names );
// Remove empty elements
$names = array_filter( $names );
$where = array();
// Loop over each, placing the "LIKE" clause into an array
foreach( (array)$names AS $name ) {
$where[] = "{$field_name} LIKE '%{$name}%'";
}
// Glue up the LIKE clauses.
$where = '(' . implode(' OR ', $where) . ')';
// Results will be something like:
// $where = "(name LIKE '%santadar%' OR name LIKE '%HSBC%')"
return $where;
}
Usage:
$area = 'London';
$res = 'santandar, HSBC, RBS, ';
$name_where = build_like_or( $res, 'name');
$sql = "SELECT * FROM banks WHERE location LIKE '%$area%' AND {$name_where}";
// echo $sql outputs "SELECT * FROM banks WHERE location LIKE 'London' AND (name LIKE '%santadar%' OR name LIKE '%HSBC%' OR name LIKE '%RBS%')

Is there a way to use SQL query that would return results if values can be in any order?

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 ;

Live search engine logic behind query

I have problems creating a multiple field live search engine using AJAX and PHP.
Up to now there was no need to search for multiple fields, so I had a simple query that worked fine for only one field.
Because of a live search using onkeyup function I recognized some problems. The first problem i need to explain:
The structure of the table is quite simple: zipcode | city
For example, someone enters 12345; of course this would be the zipcode. But what in case if someone enters 12345 hometown so the first keyword would be the zipcode, second the city?
The keywords will be split by using preg_split('/[\s]+/', $search_term) so as a result I receive an array with the single keywords that will be searched for. in the case above it would be: key[0] => 12345 and key[1] => hometown.
The query I use is like:
$where = "";
$search_term = preg_split('/[\s]+/', $search_term); //splits the whole string into single keywords
$total_search_terms = count($search_term); //counts the array-keys from $search_term
foreach ($search_term as $key=>$single_term) {
$where .= "`zipcode` LIKE '$single_term%' OR `city` LIKE '$single_term%' ";
if ($key != ($total_search_terms - 1)){ //adds AND to the query in case in case of a non empty array-key
$where .= " AND ";
}
}
$query = $db->query("SELECT COUNT(*) FROM table WHERE $where");
...
Okay, so far so good. Now the problem is that the keywords can match each field again and again.
To give further example:
In case from above entering 12345 hometown it means that key[0] => 12345 can match field zipcode OR city. This condition is egual to key[1] => hometown, even this can match field zipcode OR city. So ,even when entering other way round: hometown 12345 means the same.
And this is the first problem I have.
I'm looking for a logic to structure the query. So in case of entering 12345 hometown I would like to have something like that:
When key[0] => 12345 matches field zipcode dont check key[1] => hometown if that matches in zipcode OR city because key[0] matches already in zipcode so it´s quite logical that key[1] needs to be city.
UPDATE
Okay, to tell my second problem I would like you to have a look at the answer from david strachan
He mentioned that a problem will be caused when the city contains more than one string. Let's say the search string would be something like:
12345 New York
The keys would be:
key[0] => 12345, key[1] => New, key[2] => York
Okay, now the problem is that I could check if one of the keys contains integers or not and in case of a string-length of exactly 5 I know that it will be the zipcode.
key[0] => 12345 //if ( stringlen(key[0|) === 5) === true && is_int(key[0]) === true) {zipcode}
So far so good but the real problem is the logic behind the strings of the cities. My first thoughts were that I could say that all keys that do not contain integers must be the city so I could convert them into one key.
key[1] => New, key[2] => York //if ( !is_int(key[1|) === true && !is_int(key[2|) === true) {$create_one_key = array_fill_keys(key[1], key[1]+key[2]);}
Alright, but what in case I would like to add street names in the future? I do not know how to separate and test for street name and even for the city name.
Another approach is to use JQuery Autocomplete to search MySQL database. The following code uses PDO.
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.2/themes/smoothness/jquery-ui.css" />
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script src="http://code.jquery.com/ui/1.10.2/jquery-ui.js"></script>
<link rel="stylesheet" href="/resources/demos/style.css" />
<script type="text/javascript">
jQuery(document).ready(function(){
$('#zipsearch').autocomplete({source:'suggest.php', minLength:2});
});
</script>
<style type="text/css">
li.ui-menu-item { font-size:10px}
</style>
</head>
<body>
<h2>jQuery UI Autocomplete - With MySQL </h2>
<form onsubmit="return false;">
Search:
<input id="zipsearch" type="text" size ="60"/>
</form>
suggest.php
require("dbinfo.php");//db connection strings
// if the 'term' variable is not sent with the request, exit
if ( !isset($_REQUEST['term']) )
exit;
$term = $_REQUEST['term'];
if (is_numeric($term)){
$query = "SELECT * from ziptest WHERE zip LIKE ? OR address LIKE ? LIMIT 0,10";
}else{
$query = "SELECT * from ziptest WHERE city LIKE ? OR state LIKE ? LIMIT 0,10";
}
$term.="%";
// connect to the database
try {
$dbh = new PDO("mysql:host=$host;dbname=$database", $username, $password);
$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
// Prepare statement
$stmt = $dbh->prepare($query);
// Assign parameters
$stmt->bindParam(1,$term);
$stmt->bindParam(2,$term);
// setting the fetch mode
$stmt->setFetchMode(PDO::FETCH_ASSOC);
$stmt->execute();
$data = array();
while($row = $stmt->fetch()) {
$data[] = array(
'label' => $row['address'] .', '. $row['city'] .', '. $row['state'] .', '.$row['zip'] ,
'value' => "ID ".$row['id'] .': '.$row['address'] .', '. $row['city'] .', '. $row['state'] .', '.$row['zip']
);
}
echo json_encode($data);
}
catch(PDOException $e) {
echo "I'm sorry I'm afraid you can't do that.". $e->getMessage() ;// Remove or modify after testing
file_put_contents('PDOErrors.txt',date('[Y-m-d H:i:s]'). $e->getMessage()."\r\n", FILE_APPEND);
}
// close the connection
$dbh = null;
DEMO
Check to see if $search_term[0] is string(city) or int(zipcode) by addition of 0 Then format query accordingly.
$search_term = '1234 paisley';
$search_term = preg_split('/[\s]+/', $search_term); //splits the whole string into single keywords
$total_search_terms = count($search_term); //counts the array-keys from $search_term
$test = $search_term[0]+0;//If string returns 0
if($total_search_terms == 1){
if ($test == 0 ){
$where = "WHERE `city` LIKE `%$search_term[0]%`";
}else{
$where = "WHERE `zipcode` LIKE `%$search_term[0]%` ";
}
}else{
if ($test == 0 ){
$where = "WHERE `zipcode` LIKE `%$search_term[1]%` AND `city` LIKE `%$search_term[0]%`";
}else{
$where = "WHERE `zipcode` LIKE `%$search_term[0]%` AND `city` LIKE `%$search_term[1]%`";
}
}
$query = "SELECT COUNT(*) FROM table $where";
echo $query;
One problem will be how you treat New York. I will leave you how to work this out.
EDIT
New York $total_search_terms > 2

Categories