Use REGEX in user input variable in SQL Query. - php

I have to write a sql query with a where clause which looks for user input values in database table.
Say a user input a job title "Head of science and maths"
The query should look for all the words in a job title individually, and return department list accordingly.
$keyword = mysql_real_escape_string($keyword);
SELECT distinct departments.name FROM departments, jobtitle_keywords
WHERE jobtitle_keywords.keywords REGEXP '$keyword'
AND departments.id = jobtitle_keywords.dept_id;
This query runs perfectly in ideal situations( no special characters , spaces etc ) but fails otherwise
can anyone guide me to a proper way to escape all kinds of special characters and anomalies a user can input so my query does not fail.
Any help is greatly appreciated.
Thanks

So taken your comments i assume you want to take the single words from your users search input (i.e "head of science / math") and use only the words (head, of, science and math) and use those to search for in your jobtitle_keywords.keywords field.
Assuming this is what you want/need:
Use a simple regex to make sure you get all words out of your query like this:
$re = '/(\w+)/i';
$str = 'head of science / math';
preg_match_all($re, $str, $matches);
now in $matches[0] you have your results: array( "head", "of", "science", "math"). Awfully simple.
Now use this to create an SQL Statement using either AND or OR. You can do so easily using implode.
$queryAddon = "jobtitle_keywords.keywords LIKE '%"
. implode ("%' AND jobtitle_keywords.keywords LIKE '%", $matches[0])
. "%'";
Gives you jobtitle_keywords.keywords LIKE '%head%' AND jobtitle_keywords.keywords LIKE '%of%' AND jobtitle_keywords.keywords LIKE '%science%' AND jobtitle_keywords.keywords LIKE '%math%'.
Like stealing a Childs Candy right?
So now just put that in your Query and fire it up.
$query = "SELECT distinct departments.name FROM departments, jobtitle_keywords " ."WHERE departments.id = jobtitle_keywords.dept_id " ." AND (" . $queryAddon . ")";
This should do the trick for you - if i got you right.

Related

How to search something in the Database independent of the sequence of words?

I am working on a search filter that connects to a MySQL database. It accepts a keyword parameter; however, it only searches for the keyword in the order it is typed in. For example: If I type in "house rental", it looks for the term in xyz column in the order it is typed it.
However, I would like to change it so that it searches for both those terms are independent of the order they are typed in. Example, if typed in "house rental", the result should contain listings that have either, "house rental" or "rental house" mentioned somewhere in the xyz columns.
I have tried to break the keywords, put it in an array and do a foreach loop on the array to get the result but it generates correct but undesired results. The results that are generated are not the ones that I required.
$samp_text = 'House Rental';
$split_string_array = preg_split('/[\s,]+/', $samp_text);
foreach ($split_string_array as $each_sql_query) {
print_r('SELECT * FROM XYZ WHERE $keyword LINK = %' . $each_sql_query . '% '. "\r");
}
I would like a suggestion on how to tackle this problem.
While the method Sloan Thrasher suggests works, it does not scale.
The solution is to create a table contains each keyword from a searchable document as a single row with a foreign key to the original document with an index on the search word then the foreign key. Split your search term into an equivalent table, join the 2 and count the matches:
Select doc.txt, count(*)
From doc
Inner join keywords
On doc.id=keywords.doc_id
Inner join search
On keywords.word=search.word
Where search.query_id=?
Group by doc.txt
Order by count(*) desc
Alternatively just use the built-in fulltext capability of mysql.
You can build a query that will check for all keywords at once and give you list of rows that contain all of the keywords.
If you want all rows that contain one or more keywords, change the AND in the implode function to OR.
$samp_text = 'House Rental';
$split_string_array = preg_split('/[\s,]+/', $samp_text);
$qstr = "SELECT * FROM XYZ WHERE ";
$keywords = array();
foreach ($split_string_array as $each_sql_query) {
$keywords[] = " LINK = '%" . $each_sql_query . "%'\r")
}
$qstr .= implode(" AND ",$keywords);
// Code to execute query and use results.

Find text with select in 2 columns mySQL/PHP

I am making an autocomplete form and requesting names from my database. This is the table in my database
user_id | firstname | lastname
I can get the data for a "searchterm" using this
$query = "SELECT user_id, firstname, lastname FROM users WHERE firstname like :searchterm ";
But I want to expand it so I could also search both firstname and lastname at the same time, for example, to search for "John D...". I think there is a way to join the two columns and then make a search. Can someone explain it?. I am just starting with this mysql thing.
When you've got spaces in your search term, you'll often need to split the string into sections before you put it into your query. Use AND if you want to match all inputted search terms, or OR if you want to match any. Example:
Search: john james smith
<?php
$search = "john james smith";
$bits = explode(" ", $search);
foreach($bits as $key => $bit) {
$bits[$key] = '(`field1` LIKE "' . $bit . '" AND `field2` LIKE "' . $bit . '")';
}
$sql = implode(' OR ', $bits);
// (`field1` LIKE "john" AND `field2` LIKE "john") OR (`field1` LIKE "james" AND `field2` LIKE "james") OR (`field1` LIKE "smith" AND `field2` LIKE "smith")
?>
Demo: https://eval.in/66808
Looks like you're using PDO (which is good), so you'll just need to put your binding parameters into that string instead, then concatenate it on at the end of your current SQL query into the WHERE clause.

Mysql LIKE clause and separate words in a field

I currently use a mysql statement like the one below to search post titles.
select * from table where title like %search_term%
But problem is, if the title were like: Acme launches 5 pound burger and a user searched for Acme, it'll return a result. But if a user searched for Acme burger or Acme 5 pound, it'll return nothing.
Is there a way to get it to return results when a users searches for more than one word? Is LIKE the correct thing to use here or is there something else that can be used?
You could use a REGEXP to match any of the words in your search string:
select *
from tbl
where
title REGEXP CONCAT('[[:<:]](', REPLACE('Acme burger', ' ', '|'), ')[[:>:]]')
Please notice that this will not be very efficient. See fiddle here.
If you need to match every word in your string, you could use a query like this:
select *
from tbl
where
title REGEXP CONCAT('[[:<:]]', REPLACE('Acme burger', ' ', '[[:>:]].*[[:<:]]'), '[[:>:]]')
Fiddle here. But words have to be in the correct order (es. 'Acme burger' will match, 'burger Acme' won't). There's a REGEXP to match every word in any order, but it is not supported by MySql, unless you install an UDF that supports Perl regexp.
To search for a string against a text collection use MATCH() and AGAINST()
SELECT * FROM table WHERE MATCH(title) AGAINST('+Acme burger*')
or why not RLIKE
SELECT * FROM table WHERE TITLE RLIKE 'Acme|burger'
or LIKE searching an array, to have a compilation of $keys
$keys=array('Acme','burger','pound');
$mysql = array('0');
foreach($keys as $key){
$mysql[] = 'title LIKE %'.$key.'%'
}
SELECT * FROM table WHERE '.implode(" OR ", $mysql)
What you need to do is construct a SQL such that, for example:
select * from table where title like "%Acme%" and title like "%burger%"
In short: split the string and create one like for each part.
It might also work with replacing spaces with %, but I'm not sure about that.
The best thing is thing use perform union operation by splitting your search string based on whitespaces,
FOR Acme 5 pound,
SELECT * FROM TABLE WHERE TITLE LIKE '%ACME 5 POUND%'
UNION
SELECT * FROM TABLE WHERE TITLE LIKE '%ACME%'
UNION
SELECT * FROM TABLE WHERE TITLE LIKE '%5%'
UNION
SELECT * FROM TABLE WHERE TITLE LIKE '%POUND%'
Find out a way to give the first query a priority. Or pass the above one as four separate queries with some priority. I think you are using front end tp pass query to data bases, so it should be easy for you.
<?php
$search_term = 'test1 test2 test3';
$keywords = explode(" ", preg_replace("/\s+/", " ", $search_term));
foreach($keywords as $keyword){
$wherelike[] = "title LIKE '%$keyword%' ";
}
$where = implode(" and ", $wherelike);
$query = "select * from table where $where";
echo $query;
//select * from table where title LIKE '%test1%' and title LIKE '%test2%' and title LIKE '%test3%'

Not searching what I want with LIKE in Codeigniter, MySQL

I have the followings examples in be_user_profiles.subject. These are subject ids which each teacher teaches.
1// English
1,2 // English and Math etc
1,2,14
2,4,114
12,24,34
15, 23
I want to select where be_user_profiles.subject has 1. When I use the following, it outputs all which has 1 in it. So it will outputs all. I tried HAVING but it picks up only exact matches. So it shows only the first one. How can I pick up data which has the be_user_profiles.subject?
$this->db->select('*');
$this->db->from('be_user_profiles');
$this->db->join('be_users', 'be_users.id = be_user_profiles.user_id');
$this->db->where('be_users.group', $teachergrp);
$this->db->like('be_user_profiles.subject', $subjectid);
//$this->db->having("be_user_profiles.subject = $subjectid");// this picks up only exact match
$query = $this->db->get();
Thank you in advance.
be_user_profiles table
row1: 1,2,14
row2: 2,4,114
row3: 12,24,34
row4: 15, 23
To get data with exact match use this query
$this->db->query("
SELECT * FROM `be_user_profiles`
WHERE subject LIKE '1'
UNION
SELECT * FROM `be_user_profiles`
WHERE subject LIKE '1,%'
UNION
SELECT * FROM `be_user_profiles`
WHERE subject LIKE '%,1,%'
UNION
SELECT * FROM `be_user_profiles`
WHERE subject LIKE '%,1'
");
The both clause that you put into the like query means to add % widcard in front and after of the string to search, so it returns 1 as long as 12, 21, 121 etc. If you remove it it will search only for exact match.
You could add this like clause and add commas to it and i think that it will work. Try to add this instead of the like you have now:
$this->db->like("," . "be_users.group" . "," , "," . $subjectid. "," , both);
I think you can use a regex pattern here.
$pattern = "(^|.*,)1(,.*|$)";
...
...
$this->db->select('*');
....etc
$this->db->where("be_user_profiles.subject REGEXP $pattern");
This regex pattern assumes that there are no spaces in the comma string.
However, as #halfer said in the comments you really, really should split this out into a "teachersubject" table with teacherid and subjectid columns otherwise it will bite you in the backside very, very soon. [Been there, done that :-) ]
eg Imagine trying to expand the above into searching for a teacher that teaches ((maths or physics) and English). Nightmare!

filter mySQL database with multiple words in any order in Concatenated field

I have Concatenated in mySQL to produce a field that I can search with. This contains animal as well as owner names and addresses for a client database. I need to be able to search by animal name, owner names and postcode in any order.
Thus if if my name is john smith and i own a dog called Fido and live in postcode AB1 2CD, I want the search to be yield results if any of the following are entered:
"john fido AB1"
"john AB1"
"AB1 john"
"AB1 fido"
"fido 2CD"
"2CD"
"fido"
etc... i.e any of the fields in any order, and also not complete words either so "john fido AB1" should yield the same result as "jo fi AB1"
I currently have this PHP code, taking a single text field on my search page, exploding then imploding it to add % between the search terms:
$list = explode(' ',$_GET["q"]);
$q = implode('%%', $list);
if (!$q) return;
$sql = "SELECT DISTINCT owner.AddressPrim, owner.PostcodePrim,
owner.OwnerSurnamePrim,owner.OwnerForenamesPrim,owner.OwnerID
FROM owner
Inner Join patient ON owner.OwnerID = patient.OwnerID
WHERE CONCAT_WS(' ',owner.AddressPrim, owner.PostcodePrim,
owner.OwnerForenamesPrim,owner.OwnerSurnamePrim,patient.AnimalName) LIKE '%$q%'";
This works for "AB1 john" and "john fido" but not "fido john" as it is out of order in the concatenated field.
Any help greatly appreciated
I think you're going to have to split the keywords and add a query for each keyword in the string of keywords.
So first (in PHP), split the query string and dynamically generate your SQL query, then send it to the database. Here's some pseudocode to show you what I mean:
$keywords = explode(' ', $q);
$sql = "SELECT DISTINCT owner.AddressPrim, owner.PostcodePrim,
owner.OwnerSurnamePrim,owner.OwnerForenamesPrim,
owner.OwnerID
FROM owner
Inner Join patient ON owner.OwnerID = patient.OwnerID";
$first = true;
foreach($keyword in $keywords):
if($first):
$sql += " WHERE ";
$first = false;
else:
$sql += " AND ";
$escaped = mysql_real_escape_string($keyword);
$sql += " CONCAT_WS(' ',owner.AddressPrim, owner.PostcodePrim,
owner.OwnerForenamesPrim,owner.OwnerSurnamePrim,patient.AnimalName)
LIKE '%$escaped%'";
But do beware, this is not going to be anywhere near fast for the size of tables you'll probably encounter in daily operation. You may want to look into a better way of doing fulltext search, whether it means using a library or making a cross-reference table of keywords maintained by triggers.
MySQL's fulltext search (MyISAM tables only!) could be useful to you.
http://dev.mysql.com/doc/refman/5.0/en/fulltext-search.html
You can try this http://www.sphinxsearch.com/
You need dedicated server to run this thing, but if you have one, sphinx will easily solve your problem and your queries won't load database.

Categories