mySql - search query based on keywords - php

I wasn't able to find anything that really helped me out with this.
I have a database with the following structure:
and a simple query to search and display some results from the DB:
if ($_REQUEST['search']) {
$q = $_REQUEST['search'];
$q_comma = explode(", ", $q);
$where_in_set = '';
$count = count($q_comma);
foreach ($q_comma as $q) {
$counter++;
if ($counter == $count) {
$where_in_set .= "FIND_IN_SET('$q','keywords')";
} else {
$where_in_set .= "FIND_IN_SET('$q','keywords') AND ";
}
}
$sql_res = "select link, description, keyword from myDB where $where_in_set or description like '%$q%'";
This code works, but not really as I wanted.
In the keyword column, I have different comma separated keywords, and i'd like to be able to search for them even if the order is different.
Here's an example: Let's say I have into my keyword column
Google, Facebook, twitter
With my current code if I type Google, I can see the result, but if i type twitter, I don't see it.
Is there anything I can do to make it work without taking into account the order of the keywords, but having a pretty fast search as well?
Any help will be really appreciated.
PS. I'd like to keep only one DB if possible, cause I read about creating a new table with only ID and keywords, and on my search join the tables on the ID's, but I would prefer a better solution if possible.
Thanks
EDIT
Some updates:
as pointed out by #Frayne Konok, i have the query in lowcase and all the value in the db in lowcase as well, so case cannot be the problem
As suggested by #mkaatman, i wrapped keyword column around backticks (`)
I changed my query so that now it looks like the one suggested by #user2272989 in the answer
So my query now looks like this:
select link, description, keyword from myDB
where (FIND_IN_SET('google',`keyword`) or
FIND_IN_SET('facebook', `keyword`) or
FIND_IN_SET('twitter', `keyword`))
OR description like 'google, facebook, twitter'
And it is returning values even if the order is different, but it is not showing only the one i want.
For example, if i write twitter, google, facebook, i have as a return something like 28 rows, where only 2 have all of the three words as a keyword, while the other my have only one or two
EDIT 2 - Updates
I just want to "reopen" this question since I didn't manage to solve this. If I change all the keywords into an object, will then be better to use them as keywords? or what is the absolute best and more reliable way to search a database for keywords? Having different DBs and use an INNER JOIN?
At this point I'm willing to change the structure and the code if it helps.
Thanks

This may help you
$sql_res = "select link, description, keyword from myDB
where (FIND_IN_SET('yahoo',keyword) or
FIND_IN_SET('twitter',keyword)) or
description like '%$q%'";
You are using AND condition in FIND_IN_SET

I ended up using this structure and query here:
$q = $_REQUEST['search'];
$q_comma = array_filter(explode(' ', str_replace(',', ' ', $q)));
$count = count($q_comma);
$dbQuery = "select id, link, description, tags from db where ";
$searchTerms = '';
foreach ($q_comma as $q) {
$counter++;
if ($counter == 1) {
$searchTerms .= "tags LIKE '%$q%'";
} else {
$searchTerms .= "and tags LIKE '%$q%'";
}
}
$sql_res = $dbQuery . $searchTerms;
$result = mysqli_query($db, $sql_res) or die(mysqli_error($db));
$return_arr = array();
$data = array();
if (mysqli_num_rows($result) == 0) {
echo "Nothing";
} else {
while ($row = mysqli_fetch_object($result)) {
$data[] = $row;
}
mysqli_free_result($result);
echo json_encode($data);
}

Related

How to both use a DISTINCT row and a non DISTINCT row

all! I have a bit of a tricky one for you today, I want to use the select DISTINCT statement to both select a row that needs to be distinct but also in the same statement (or the way I a have tried?) a row that doesn't/can't be distinct. My desired result is to only have one of each of the classnames. Currently it outputs like this:
English: textbook, folder, laptop
English: textbook
Media: textbook, folder
English: textbook, folder
English: textbook, folder
Art: textbook
And this is how I want it to output:
English: textbook, folder, laptop
Media: textbook, folder
Art: textbook
This is the layout of the database:
|ID|classname|Book
|49|English |textbook, folder, laptop
|50|English |textbook
|53|Media |textbook, folder
|54|English |textbook, folder
|55|Art |folder
I'm obviously VERY new to php so any help would be appreciated!
This is my approach so far:
$sql = "SELECT DISTINCT classname FROM classes ORDER BY Due;";
$result1 = mysqli_query($conn, $sql);
$resultCheck = mysqli_num_rows($result1);
if ($resultCheck > 0){
while ($row = mysqli_fetch_assoc($result1)){
$classname = $row["classname"];
if ($classname == "English"){
$newName = $classname;
$sql = "SELECT Book FROM classes WHERE Book='$newName';";
$result1 = mysqli_query($conn, $sql);
$resultCheck = mysqli_num_rows($result1);
if ($resultCheck > 0){
while ($row = mysqli_fetch_assoc($result1)){
$materials = $row["Book"];
echo "<div class='subname'>$newName:";
echo "<div class='wow'>$materials</div>";
echo "</div><br>";
}
}
}
}
}
Well, given the crappy design of your database the first step you need to do is itemize your lists such as "textbook, folder, laptop" [such that you get three rows with one item instead of one row with three items]. The semantics of that operation is a bit like SQL UNNEST but sadly, the "structure" (insofar as using that word is appropriate for what you have) of your database is unfit for using that. I'm not sure it can be done without some form of procedural coding, so the answer most likely to be correct is "just forget doing that in one SQL statement".
What you call DISTINCT can only be applied [to the results you get] after the itemization.
After applying the DISTINCT, you then need to re-group. Maybe it can be done in client-side languages, but those are not my cup of tea.
As a general statement, this isn't a very robust database design. You may be better served to normalize your table and have a single classname-book combination in every row.
If that is not a possibility, I'd group_concat all the books per class and then explode them to an array on the PHP side, make the result unique, and join it back to a string:
$sql = "SELECT classname, GROUP_CONCAT(book SEPARATOR ', ') AS materials FROM classes GROUP BY classname";
$result = mysqli_query($conn, $sql);
while ($row = mysqli_fetch_assoc($result)) {
$classname = $row["classname"];
$materials = $row["materials"];
$materials = implode(',', array_unique(explode(', ', $materials)));
echo "<div class='subname'>${classname}:";
echo "<div class='wow'>$materials</div>";
echo "</div><br/>";
}

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%' ";
}
}

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 ;

error on query , trying to make a search by keywords

i have a variable and an user_name i want to search on a string(function_description) of the user_name for it
whats wrong with this :
$function_keywords = mysql_real_escape_string($_POST['function_keywords']);
if($function_keywords=="" || empty($function_keywords)){
redirect("show.php?functions=PHP");
}
//trim whitespace from the stored variable
$trimmed = trim($function_keywords);
//separate key-phrases into keywords
$trimmed_keywords = explode(" ",$trimmed);
// Build SQL Query for each keyword entered
foreach ($trimmed_keywords as $trimm){
// MySQL "MATCH" is used for full-text searching.
//this code is ebv weird , should check out soon!
$query = "SELECT *
FROM functions
WHERE isEnabled=1 AND isPrivate=0
AND function_description LIKE '{$trimm}'
AND user_name='{$user_name}'
";
// Execute the query to get number of rows that contain search kewords
$results=mysql_query ($query,$connection);
as far as "like" syntax goes you have to use the '%' symbol. if you query for
select * from table where column like '%yourkeyword%'
then it returns any rows with 'yourkeyword' inside the table column.
your statement will be true only if the column = 'yourkeyword'
That's highly inefficient. If someone puts in 5 keywords, you'd be running the search 5 times and getting 5 sets of results. Try something more along these lines:
$words = $_POST['function_keywords'];
if ($words == '') {
... abort ...
}
$parts = trim(explode(' ', $words));
$clauses = array();
foreach($parts as $part) {
$clauses[] = "function_description LIKE '%" . mysql_real_escape_string($part) . "%'";
}
$clause = implode(' OR ' , $clauses);
$sql = "SELECT .... WHERE (isEnabled=1) AND (isPrivate=1) AND (user_name='$user_name') AND ($clause)";
$result = mysql_query($sql) or die(mysql_error());
This'll build up a long series of or statements for each keyword specified, and run the whole thing as a single query.
To see if the function_description contains the keyword you need to use '%' which stands for anything much the way '*' does in unix. Try function_description LIKE '%{$trimm}%'

How to order mysql search results by relevence of word priority [duplicate]

This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
PHP MySQL Search And Order By Relevancy
Hi,
I have a table with several columns containing fields like name, address, company etc. Lets say someone search for "microsoft john". I want the results containing "microsoft" should appear first, then results containing john. vice versa if query is "john microsoft"
My php code is:
$searchitems=explode(" ", $trimmed);
//print_r($searchitems);
$so = $_GET['so']=='2'?"2":"1";
$clause = $so=='2'?"AND":"OR";
include("dbconnect.php");
// Build SQL Query
$query = "select FirstName,LastName,course,Department,batch,City,companyjob,companylocation,
companyposition,coursename,institutename,coursename2,institutename2,coursename3,
institutename3 from alumni WHERE ";
for($i=0;$i<count($searchitems);$i++)
{
$queryappend .= "(FirstName LIKE '".$searchitems[$i]."%' OR LastName LIKE '".$searchitems[$i]."%'
OR City LIKE '".$searchitems[$i]."%' OR CountryorRegion LIKE '".$searchitems[$i]."%'
OR companyjob LIKE '".$searchitems[$i]."%' OR companylocation LIKE '".$searchitems[$i]."%'
OR coursename LIKE '".$searchitems[$i]."%' OR institutename LIKE '".$searchitems[$i]."%'
OR coursename2 LIKE '".$searchitems[$i]."%' OR institutename2 LIKE '".$searchitems[$i]."%')";
if($i<count($searchitems)-1) $queryappend .= $clause;
}
$query .=$queryappend;
The problem is MYSQL is ordering the results by id... This makes it funny, because some higher valued results may be stuck deep in the stack. btw, phpmyadmin search has the same flaw.
Please suggest.
As an example:
SELECT
FirstName,
LastName,
IF (FirstName LIKE '%Microsoft%' || LastName LIKE '%Microsoft%', 1, 0) AS One,
IF (FirstName LIKE '%John%' || LastName LIKE '%John%', 1, 0) AS Two
FROM alumni
ORDER BY One DESC, Two DESC
In your code, this will make the query pretty complicated. The advantage is, that items with both search term appear before items that match only a single search term.
An alternative is sorting the records into buckets while retrieving them using PHP. Assuming you have the search terms in an array $search (ordered by descending priority):
while ($record = mysql_fetch_array($result))
{
$total = join(' ', $record);
$found = false;
foreach ($search as $term)
{
if (strpos($total, $term) !== false)
{
$buckets[$term][] = $record;
$found = true;
break;
}
}
if (!$found)
{
$results[] = $record;
}
}
foreach (array_reverse($search) as $term)
{
if (isset($buckets[$term]))
{
$result = array_merge($buckets[$term], $result);
}
}
Now you have the results in array $results. Note that this demonstrates the algorithm, it it not tuned for performance.
I would think the simplest way to solve it would be sorting the results by the levenstein distance.
Something like....
$queryappend="ORDER BY
length(firstname) - levenshtein(FirstName, '".$searchitems[$i]."') +
length(lastname) - levenstein(LastName, '".$searchitems[$i]."') +
length(City) - levenstein(City, '".$searchitems[$i]."') +
...
Although it might be a good idea to use a schema MORE SUITED to this kind of searching

Categories