Say I have some records in my database with data like so:
John Murdoch
I am John Murdoch
My first name is john and my second name is murdoch
I have a search form and I type in "john murdoch" which will run this query:
$search //contain the search string in this case is john murdoch
$sql = mysqli_query($sql, "SELECT * FROM table WHERE column LIKE '%$search%'");
while($row = mysql_fetch_assoc($sql)){
echo $row['first']."<br>";
}
This will return the first two rows only because it is only those rows that have the both words beside each other. How can I return the other row even though the words are split up? I know I could explode the string and check each piece, but I was looking for a more stable and efficient way of doing this.
Just replace punctuation and spaces with the wildcard % before your query.
$search = str_replace ( array( '.', ' ', "'", '-' ), '%', $search );
This does still require the first word to appear in the text before the second word. So if your search was for "Murdoch John", you would not return any results.
The better answer is to employ FULLTEXT searching on the column (must do this in MySQL), and do a MATCH() query against the column, like so:
Add a plus sign before each word (to indicate that word is required)
$words = '+' . str_replace( ' ', ' +', $search );
And your query:
SELECT * FROM table MATCH ( column ) AGAINST ( '$words' IN BOOLEAN MODE)
More info here: http://dev.mysql.com/doc/refman/5.5/en/fulltext-boolean.html
SELECT * FROM table WHERE `column` LIKE '%john%' AND `column` LIKE '%murdoch%'
I would construct that query like this:
$search_terms = array('john','murdoch');
$field_to_search = 'some_column';
$sql = 'SELECT * FROM table WHERE' . "\n";
$sql .= '`' . $field_to_search . '` LIKE \'%'
. implode('%\' AND `' . $field_to_search . '` LIKE \'%',$search_terms)
. '%\'';
That PHP can be used with any number of search terms. It requires matches for all because it's connected with AND.
Here's a link to a php fiddle: http://phpfiddle.org/main/code/igv-3qc
Related
We have made a search field where you can search for ingredients and find recipes.
For now you can only type in 1 ingredient:
if (isset($_POST['search'])) {
$searchquery = $_POST['search'];
$query = mysql_query("SELECT * FROM opskrifter WHERE id IN
(SELECT opskrifterid FROM ingredienser WHERE ing_name IN ('$searchquery'))") or die("search failed");
We want to be able to search for multiple ingredients in the same search field by seperating the ingredients with a "," or something like this.
Is there a simple way to make that happen ?
EDIT:
We tried to use explode like this without succes.
$searchTerms = explode(' ', $searchquery);
$searchTermBits = array();
foreach ($searchTerms as $term) {
if (!empty($term)) {
$searchTermBits[] = "ing_name '$term'";
}}
...
$result = mysql_query("SELECT * FROM opskrifter WHERE id IN (SELECT * FROM WHERE ".implode(' AND ', $searchTermBits)));
Thanks! :)
You could simply get the user to type in his values comma-separated, the the input would be almost in the right syntax for the query. You just have to add semicolons around the values because you search for a string in your table.
You can use PHP's str_replace()-Function:
$vals = $_POST['search'];
$valsFormatted = "'" . str_replace(",", "','", $vals) . "'";
In this code, you replace all the commas of the input with the comma plus semicolons before and behind them in orderto wrap all values of the input with semicolons. You also have to add one at the beginning and at the end of the string. Replace the first comma in the function above with the char you want your users to separate the values with.
After that, you can simply change your query to the following:
$query = "SELECT * FROM opskrifter WHERE id IN
(SELECT opskrifterid FROM ingredienser WHERE ing_name IN ('$valsFormatted'))";
Please also be informed, that your code like this is vulnerable for SQL Injections! Check out this link to learn how to prevent this.
A simple statement like this would work:
$array = implode("','",explode($_POST['search'], ","));
$query = mysql_query("SELECT * FROM opskrifter WHERE id IN (SELECT opskrifterid FROM ingredienser WHERE ing_name IN ({$array}))") or die("search failed");
First explode your search, then implode it (might not even need to do so). After that make sure the array gets used as the 'in' operator as a string/array.
For more information about this, you could read this question: PHP/MySQL using an array in WHERE clause
The working copy from my local machine was this;
$_POST['search'] = "0, 1, 2";
$array = implode ( "','", explode ( ",", $_POST['search'] ) );
$query = mysql_query("SELECT * FROM users WHERE id IN ('$array')") or die(mysql_error());
var_dump ( $array );
var_dump ( $query );
var_dump ( "SELECT * FROM users WHERE id IN ('$array')" );
var_dump ( mysql_fetch_array ( $query ) );
which actually did return users, so if we would take this example and change it to your code, it would be (the query, at least):
$query = mysql_query("SELECT * FROM opskrifter WHERE id IN (SELECT opskrifterid FROM ingredienser WHERE ing_name IN ('$array'))") or die(mysql_error());
Do take note of the changed $array variable too.
First you need to convert the text coming from the search field to array with:
$string = $_POST['search'];
$array = explode( '"' , $string);
So if you put in the search: test"hello"hi
the array will be:
1 => test,
2 => hello,
3 => hi
After that, you need to use the SQL format:
WHERE column_name IN (value1,value2,...)
So you need to change the array we have created to a string with this format:
$string = implode(',',$array);
So the echo of $string will be:
test,hello,hi
and SQL will be :
WHERE column_name IN ($string)
In my database I have a number: 00 48 76 848
The string to match against is: 004876848
I have:
...
WHERE tel REGEXP '\s($q)'
...
But that doesn't work.
What regexp do I need to achieve this?
$q = preg_replace("/[\\D]+/", "", $q);
return DB::select('SELECT contacts_phones.id, CONCAT(firstName, " ", lastName) AS name, tel, "phoneNumber" AS type, entities.name AS company,
entities.id AS companyId
FROM contacts_phones, contacts, entities
WHERE tel REGEXP "[[:space:]]"
AND contacts_phones.contactId = contacts.id
AND entities.id = contacts.ownerId
AND contacts.ownerTypeId = 1
AND contacts.archived = 0
LIMIT ' . $limit, array('%' . $q . '%'));
You can't tell regex just to "ignore" whitespace. You can only match it, which is next to impossible with a value like yours.
An easier approach, without regex, would be to remove the spaces with REPLACE() before you test it against your value:
WHERE REPLACE(`tel`, ' ', '') = '004876848'
Good practice would be to normalize your values before you store them in the database. That makes everything else a lot easier.
WHERE tel REGEXP '[[:space:]]';
I´m currently working on a query that must show a list of all articles from a specific table, but it must sort the list according to a search form, so that the articles that contain most/best matches are shown first and those that do not have any matches at all will be shown last sorted alphabetically.
I have made this code which is working fine, though I cannot find a way to sort the matches by most hits / relevance.
Here is my code:
$search = $_POST["searhwords"];
$search = preg_replace('/\s+/', ' ',$search);
$SearchQueryArray = str_replace(",", "", $search);
$SearchQueryArray = str_replace(" ", ",", $SearchQueryArray);
$SearchQueryArray = explode(',', $SearchQueryArray);
$outputtt1 = '';
$outputtt2 = '';
foreach ( $SearchQueryArray as $queryword )
{
$outputtt1 .= "title LIKE '%".$queryword."%' OR ";
$outputtt2 .= "title NOT LIKE '%".$queryword."%' AND ";
}
$outputtt1 = rtrim($outputtt1, ' OR ');
$outputtt2 = rtrim($outputtt2, ' AND ');
$query_for_result = mysql_query("SELECT * from mytable
WHERE ".$outputtt1."
union all
SELECT * from mytable
WHERE ".$outputtt2."
");
So I need to find a way to sort the article that contain matches so that those that contain most matches are sorted first.
You can see the script i Have made live here:
http://www.genius-webdesign.com/test/querytest.php
Here is the SQL that does this:
select t.*
from mytable
order by ((title like '%keyword1%') +
(title like '%keyword2%') +
(title like '%keyword3%') +
. . .
(title like '%keywordn%')
) desc;
MySQL treats boolean expressions as numbers, with true being 1. So, this counts the number of matches.
By the way, if your data has any size, you might find full text search is more efficient than using like.
EDIT:
Counting the number of keywords is a bit more challenging, but you can do it as:
order by ((length(replace(title, 'keyword1', 'x')) -
length(replace(title, 'keyword1', '')
) +
(length(replace(title, 'keyword2', 'x')) -
length(replace(title, 'keyword2', '')
) +
. . .
(length(replace(title, 'keywordn', 'x')) -
length(replace(title, 'keywordn', '')
)
);
Counting the number of appearance of a keyword is more cumbersome than merely looking for where or not it is present.
Another way to do it using full-text search
SELECT *,
MATCH('title') AGAINST($_GET['query']) * 10 as score1,
MATCH('content') AGAINST($_GET['query']) * 5 AS score2
FROM articles
WHERE MATCH (title, content) AGAINST($_GET['query'])
ORDER BY (score1) + (score2) DESC;
Alter your table like this if needed
ALTER TABLE articles ENGINE = MYISAM;
ALTER TABLE articles ADD FULLTEXT(title, content);
I am trying to select stories for my mysql database Where the author variable is equal to a list of names.
I basically have a list of names that I want use in determining what stories to pull.
$names = "name1 name2 name3 name4"
$get_stories = mysql_query("SELECT * FROM glnce_stories WHERE author = '$names' ORDER BY id DESC");
I know that this isn't the right way to do but I was looking for some solutions on how I might be able to break that list up so I can have it pull from the authors in that $names variable.
Thanks!
If you aren't able to change the format of the $names variable, this should work:
$names = 'name1 name2 name3 name4';
$names = '"' . implode('","', explode(' ', $names)) . '"';
$get_stories = mysql_query('SELECT * FROM glnce_stories WHERE author IN(' . $names . ') ORDER BY id DESC');
comma separate the names and then
use "WHERE author in (" . $names . ")";
You should use WHERE field in (list) like this:
$names = "'name1', 'name2', 'name3', 'name4'";
$get_stories = mysql_query("SELECT * FROM glnce_stories WHERE author in ($names) ORDER BY id DESC");
So, your query should look something like this: SELECT * FROM glnce_stories WHERE author in ('Bob', 'Steve', 'Andrey', 'Mike', 'Jenna')
Rewrite your code as follows:
$names = "name1 name2 name3 name4";
$names = "'".implode ("','", array_map('mysql_real_escape_string', explode(' ', $names))."'";
$get_stories = mysql_query("SELECT * FROM glnce_stories WHERE author in ($names)");
You can use FIND_IN_SET() for that if you want to be lazy. You will just have to turn your names list into a comma-separated string:
$names = "name1,name2,name3,name4";
mysql_query("SELECT * FROM stories WHERE FIND_IN_SET(author, '$names')"
That's maybe easier than a list of OR conditions or preparing a proper string list for an IN clause. (You can _real_escape_string the whole comma separated list, instead of each name.)
I need to implement the following functionality:
I have a name field which contains both name and surname. This is stored in a database and is in the order 'surname name'.
I am implementing a script which searches through these records. Currently, I managed to check if a string contains a space, if it contains a space it means it is a name and not an ID Card Number for instance. Here is the code:
$query = "John Doe";
$checkIfSpaceExists = strpos($query, " ");
if ($checkIfSpaceExists == "")
{
//No Space therefore it is not a name
}
else
{
//Contains space
$queryExploded = explode(" ", $query);
foreach ($queryExploded as $q)
{
//Here I need the functionality so that if someone entered John Doe
//2 different strings are saved, which are
//$string1 = John Doe
//$string2 = Doe Johns
//If the name consists of 3 parts, strings for every combination is saved
}
Then I will insert these strings in an SQL statement with the LIKE attribute and there will be a LIKE for both JOHN DOE and DOE JOHN. Hence, if the user can either enter John Doe or Doe John in order to find the result.
Any suggestions on how to do this?
Many thanks
chris
Ok, from the start - be sure to read the manual carefully. strpos doesn't do exactly what you think it's doing. Here's how you should check for a space:
if (strpos($query, ' ') === false) // the triple-equals is important!
After that, it's simply a matter of permutations and combinations. Here's another answer on Stack Overflow which shows you how to do it: algorithm that will take number or words and find all possible combinations
What about using these exploded 3 strings in separate AND-combined LIKE-constraints?
Something like
"... WHERE name LIKE '%$name[0]%' AND name LIKE '%$name[1]%' AND name LIKE '%$name[2]%'"
You could build this String in a foreach loop.
echo preg_replace('/^(\w+) (\w+)$/', '$2 $1', "John Doe");
Doe John
I guess you cannot split this field into name and surname? I suggest creating new table, tags in database. Tags will be any word - might be surname, may be name, may be ID number... Then make a connection record_tags with record_id and tag_id. Then the query will look like
SELECT record.* FROM record
INNER JOIN record_tags rt1 ON rt1.record_id = record.id
INNER JOIN tag t1 ON t1.id = rt1.tag_id
INNER JOIN record_tags rt2 ON rt2.record_id = record.id
INNER JOIN tag t2 ON t2.id = rt2.tag_id
WHERE
t1.name = "John"
AND t2.name = "Doe"
This will be better approach to searching, as you can then use any amount of words/tags. I think the SQL can be even easier. the multiple-like approach is I think much slower, especially as your database grows.
With some looping and string manipulation, I managed to implement this script.
Here is what I did:
I checked if the query contains a space using strpos. If it contains a space, then it means its a name with both name and surname so I enter a loop in order to output one string with 'name surname' and the other string with 'surname name'
Here is the code:
$like = ""; //This is the LIKE sql command.
foreach ($selectedFields as $field)
{
$checkIfSpaceExists = strpos($query," ");
if ($checkIfSpaceExists != "")
{
$query1 = $query; //No space, so query1 is equal ta original query
$queryExploded = explode(" ", $query);
for ($i=0; $i<count($queryExploded); $i++) //First loop (name surname)
{
$tmp1 = $tmp1 . " " . $queryExploded[$i];
}
for ($i=count($queryExploded); $i>=0; $i--) //Second loop (surname name)
{
$tmp2 = $tmp2 . " " . $queryExploded[$i];
}
$query2 = $tmp2;
$query2 = trim($query2);
$like = $like . $field . " LIKE '%" . $query1 . "%' or " . $field . " LIKE '%" . $query2 . "%' or ";
I hope this helps someone else in need :)