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%'
Related
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.
I want a search query to return results that match all keywords, but I can only get it to either match any or match string.
I.E. "cake chocolate" either returns all records with "cake" OR "chocolate" in a tag field, or results with the exact tag "cake chocolate", as opposed to what I want, which is to get any record with "cake" AND "chocolate". Here is what I've got:
$key = $this->input->post('searchTerm');
if($key != ''){
// if I comment out the next 10 lines, it becomes MATCH-ANY
$this->db->or_like('product_name',$key);
$this->db->or_like('product_code',$key);
$this->db->or_like('description',$key);
$this->db->or_like('season',$key);
$this->db->or_like('year',$key);
$this->db->or_like('photo_style',$key);
$this->db->or_like('photo_status',$key);
$this->db->or_like('extra_field1',$key);
$this->db->or_like('extra_field2',$key);
$this->db->or_like('additional_notes',$key);
// But if I comment out the next 7 lines instead, I match only the entire string.
$Singlequry = "select * from (select *, concat_ws(' ',product_name,product_code,description,season,year,photo_style,photo_status,extra_field1,extra_field2,additional_notes) merged from records )temp where ";
$keywordsMany = explode(' ',$key);
$tempQuery = array();
foreach($keywordsMany as $each){
$tempQuery[] = " temp.merged like '%".mysql_real_escape_string($each)."%'";
}
$Singlequry = $Singlequry.implode(' or ',$tempQuery); // end of search type comment-out
$makeQuery = true;
}
In case it's not obvious, I really have no idea what I'm doing. I've thrown where_in and other stuff in place of the or_like but with no luck. All this to say, I will need a little hand-holding:)
Unfortunately you cannot produce this query with codeigniter or_like;
Your current code produce your query like this
SELECT * from tablename WhHERE product_name like '%key%' or product_code like '%key%' OR..............
which will return only any match with the key.
BUT I guess you want all match which should be like this
SELECT * from tablename WhHERE product_name like '%key%' AND product_code like '%key%' AND..............
if so you can replace your or_like with where like followings
$this->db->where("product_name LIKE '%$key%'");
$this->db->where("product_code LIKE '%$key%'");
$this->db->where("description LIKE '%$key%'");
It will produce your expected query.It will return results which has all match with key.
You can do it many other way too.
Hope it will will help you.
I am trying to select a record using the LIKE function. However, it does not show results if there are characters in between. I want it to select data that contains those strings. For example:
$value = "Mark Anthony";
$qry ="SELECT * FROM students WHERE name LIKE '%$value%'";
returns these results:
John Mark Anthony
Mark Anthony Philipps
but I also want to have results like these
Mark James Anthony
Mark Gabriel Anthony Fernandez
Marko Julian Anthonyo
Any ideas?
UPDATE: 'Mark' must be before 'Anthony'
I think Full TEXT works
But Full-text searches are supported for MyISAM tables only
SELECT * FROM students
WHERE MATCH (name)
AGAINST ('Mark Anthony' IN BOOLEAN MODE)
UPDATE:- As per question update OP need to search Marko as well, then you can get like this:-
SELECT * FROM students
WHERE
(
name LIKE '%Mark%'
OR name LIKE '%Anthony%'
)
You could split the value string into two parts. Like so:
WHERE name LIKE '%Mark%' AND name LIKE '%Anthony%'
last try:
$value = "Mark Anthony";
$value = str_replace(" ","%",$value);
$qry ="SELECT * FROM students WHERE name LIKE '%$value%'";
not tested!
Try this solution -
SELECT * FROM table WHERE column REGEXP '(Mark).+(Anthony)';
The proplem is, you say "bring all results which contains Mark Anthony" so it does. You should set it like
$value = "Mark%Anthony";
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!
I've been trying to come up with something for a while now to no avail. My MySQL knowledge is rudimentary at best so I could use some guidance on what I should use for the following:
I have 2 tables ('bible' and 'books') that I need to search from. Right now I am just searching 'bible' with the following query:
SELECT *
FROM bible
WHERE text LIKE '%" . $query . "%'
ORDER BY likes DESC
LIMIT $start, 10
Now, I need to add another part that searches for some pretty advanced stuff. Here is what I want to do in pseudocode which I am aware doesn't work:
SELECT *
FROM bible
WHERE books.book+' '+bible.chapter+':'+bible.verse = '$query'
$query would equal something like Genesis 1:2, Genesis coming from books.book, 1 coming from bible.chapter and 2 coming from bible.verse
Any help/guidance on this is much appreciated =)
I would recommend designing a way in your application code to break up that query so that you search for the book, chapter, and verse separately from the keyword.
That means you need columns for book, chapter, and verse that are separate from the verse text.
You should also use a FULLTEXT index because the LIKE wildcard searches are extremely inefficient.
Here's how I'd run the query in PHP using PDO:
$quoted_query = $pdo->quote($query);
$sql = "SELECT * FROM bible
WHERE book = ? AND chapter = ? AND verse = ?
AND MATCH(text) AGAINST ({$quoted_query})"
$stmt = $pdo->prepare($sql);
$stmt->execute(array($book, $chapter, $verse));
I'd rather use a parameter for the fulltext query too, but MySQL doesn't support that.
You're close. To concatenate the fields use the CONCAT() function:
SELECT * FROM bible WHERE CONCAT(books.book, ' ', bible.chapter, ':', bible.verse) = '$query'
You can use MySQL concatenation:
SELECT *
FROM bible JOIN books
WHERE CONCAT(books.book, ' ', bible.chapter, ':', bible.verse) = '$query'
I'm not sure what your foreign key is linking books to bible, but that may need specification as well.
You need to parse the query into book, chapter and verse in php first.
A regular expression should work:
preg_match("/(.+)([0-9]+):([0-9]+)/",$query,$matches);
$book = trim(matches[1]); // use trim to remove extra spaces
$chapter = matches[2];
$verse = matches[3];
Then your sql query becomes:
SELECT *
FROM bible
WHERE books.book = '$book' AND bible.chapter= '$chapter' AND bible.verse ='$verse'
-- watch out for sql injection here! use prepared statements!