Regexp inside of 'where' method - mysql

I need a method that will go through database and return appropriate results. in this case its searching for books by author, title, publishing date or ISBN code. I decided to use where() method but i encountered two problems:
1) i have trouble searching by multiple fields. its easy looking for a title:
def self.browse(query)
if query.nil?
nil
else
self.where("title REGEXP :query", query: query)
end
end
but i dont know how to set it to look for title OR author OR isbn etc. tried
self.where("(title OR author OR publishing_date OR isbn) REGEXP :query", query: query)
but it doesnt work
and second, i want my query to match only a beginning or the end of the word. in mysql Workbench its pretty easy but i have a hard time doing it in Rails. here's what i've tried so far (and failed):
self.where("title REGEXP :query", query: /^(query)*$/)
self.where("title REGEXP /^:query/", query: query)
self.where("title REGEXP :query", query: $"query"^)
Needless to say, on the internet i found many different docs or tutorials, one saying "^" should be at the end, the other it should be at the beginning...

1) You will want to use parentheses and both AND and OR clauses in your where sql:
(title IS NOT NULL AND title REGEXP :id_query) OR (name IS NOT NULL AND name REGEXP :name_query)
2) You will want to use both ^ (beginning of line) and $ (end of line), like this.
(^something|something$)
Here is an example of the whole thing that I matched against my own code. Replace id and name with your own columns, and put extra OR's in there to match against more columns
Charity.where("(id IS NOT NULL AND id REGEXP :id_query) OR (name IS NOT NULL AND name REGEXP :name_query)", id_query:'1', name_query:'(^a|a$)')
Here is the to_sql output of the above:
Charity.where("(id IS NOT NULL AND id REGEXP :id_query) OR (name IS NOT NULL AND name REGEXP :name_query)", id_query:'1', name_query:'(^a|a$)').to_sql
=> "SELECT `charities`.* FROM `charities` WHERE ((id IS NOT NULL AND id REGEXP '1') OR (name IS NOT NULL AND name REGEXP '(^a|a$)'))"

This should do it:
self.where("title REGEXP ? OR author REGEXP ? OR publishing_date REGEXP ? OR isbn REGEXP ?", query, query, query, query)
The "?" will be subbed in order by the included variables. If you want to use the same regexp for each column, then just plug the code in as-is
As for the second part, you may want to check out the LIKE operator.
To match a column which starts with a given string you'd do:
self.where("title LIKE ?", (query + "%"))
And to match a column that ends in a particular string:
self.where("title LIKE ?", ("%" + query))

create your sql query and pass into ActiveRecord execute method,it will excute sql query and do not need to change in ActiveRecord query
sql query = "your sql query"
ActiveRecord::Base.connection.execute(sql query)

You can use or:
class MyARModel < ActiveRecord::BAse
scope :search, ->(rgx) do
where('title REGEXP ?', rgx)
.or('author REGEXP ?' rgx)
.or('publishing_date REGEXP ?' rgx)
.or('isbn REGEXP ?' rgx)
end
#...

Related

MySQL- Output an "other" column based on multiple columns

I am using MySQL REGEXP to assign reviews into different topics and output them into separate columns. The problem is- some reviews may not get assigned to any topic, which is why I need an "Other" column. How do I modify the query below to achieve that?
SELECT
text,
text REGEXP 'keywords' AND text REGEXP 'other keywords' AND .... AS Cleanliness,
text REGEXP 'keywords' AND text REGEXP 'other keywords' AND .... AS Restaurant,
text REGEXP 'keywords' AND text REGEXP 'other keywords' AND .... AS Wifi,
FROM review_table;
Note that a review can belong to multiple topics.
The end result should look like this:
One solution would be create anoter REGEXP expression that represents the negation of all other expressions. But that can quickly become tedious to maintain.
Another option is to just wrap the query and analyze the results in the outer query to generate the additional column. This should be as simple as:
SELECT x.*, (Cleanliness + Food + Wifi = 0) AS Other
FROM (
--- original query
) x
Tip: in MySQL, the return value of a condition expression is 1 on success and 0 on failure. This means that this expression:
CASE
WHEN review REGEXP 'relevant keywords'
AND review REGEXP 'additional keywords if necessary'
THEN 1
ELSE 0
END AS 'Cleanliness'
Can also be written:
(
review REGEXP 'relevant keywords'
AND review REGEXP 'additional keywords if necessary'
) AS 'Cleanliness'
I think we can use the NOT(expression) command
CASE
WHEN review NOT (REGEXP 'relevant keywords'
AND review REGEXP 'additional keywords if necessary' )
THEN 1
ELSE 0
END AS 'Irrelevant'
Reference: https://dev.mysql.com/doc/refman/5.7/en/regexp.html
Related: negate regex pattern in mysql

sql regexp filter

I have data in column 'sub' in mysql database :
1=0|2=0|3=0, 3=0, 1=0, 2=0
How to filter '2=0' from 'sub' with regexp mysql ?
i was try
SELECT * FROM `men` WHERE `sub` REGEXP '2{0,1)=0'
but the result is more than 1. I want only '2=0' for my result.
please, is that any solution for me ?
You could use LIKE operator
For all rows containing 2=0
SELECT * FROM men WHERE sub LIKE '%2=0%'
For containing just 2=0
SELECT * FROM men WHERE sub = '2=0'
I want only '2=0' for my result
Use the beginning of the string ^ and the end of the string $ anchors.
REGEXP, in this case, would be good to find dynamic numbers:
SELECT * FROM men WHERE sub REGEXP '^[0-9]=0$'
for such simple case (I want only '2=0' for my result) it's achievable without regexp :
SELECT * FROM men WHERE sub = '2=0'

regular expression search a number from string

this question maybe very simple for you. i'm using mysql regexp statment.
myQuery is
select * from contents where categories regexp '{myPattern}';
categories field area 54,25,99,4,2... etc string.
my question how can i find only number of '4' from categories field.
sorry for my english.
help me please.
… WHERE FIND_IN_SET('4', categories) > 0
better yet to normalize your db scheme with categories in their own table and then join these tables together m:n
The way to match "any cvs string containing '4' as a value in the string" is:
mycolumn regexp '[[:<:]]4[[:>:]]'
In mysql regex, [[:<:]] means "start of word" and [[:>:]] means "end of word".
You can use this:
where categories RLIKE '(^|,)4($|,)';
This will match when categories contains 4, followed by a , or nothing, and precedeed by , or nothing.

MySQL regex query case insensitive

In my table I have firstname and last name. Few names are upper case ( ABRAHAM ), few names are lower case (abraham), few names are character starting with ucword (Abraham).
So when i am doing the where condition using REGEXP '^[abc]', I am not getting proper records. How to change the names to lower case and use SELECT QUERY.
SELECT * FROM `test_tbl` WHERE cus_name REGEXP '^[abc]';
This is my query, works fine if the records are lower case, but my records are intermediate ,my all cus name are not lower case , all the names are like ucword.
So for this above query am not getting proper records display.
I think you should query your database making sure that the names are lowered, suppose that name is the name you whish to find out, and in your application you've lowered it like 'abraham', now your query should be like this:
SELECT * FROM `test_tbl` WHERE LOWER(cus_name) = name
Since i dont know what language you use, I've just placed name, but make sure that this is lowered and you should retrieve Abraham, ABRAHAM or any variation of the name!
Hepe it helps!
Have you tried:
SELECT * FROM `test_tbl` WHERE LOWER(cus_name) REGEXP '^[abc]';
I don't know since when, but nowadays MySql REGEXP is case insensitive.
https://dev.mysql.com/doc/refman/5.7/en/pattern-matching.html
You don't need regexp to search for names starting with a specific string or character.
SELECT * FROM `test_tbl` WHERE cus_name LIKE 'abc%' ;
% is wildcard char. The search is case insensitive unless you set the binary attribute for column cus_name or you use the binary operator
SELECT * FROM `test_tbl` WHERE BINARY cus_name LIKE 'abc%' ;
A few valid options already presented, but here's one more with just regex:
SELECT * FROM `test_tbl` WHERE cus_name REGEXP '^[abcABC]';

MySQL REGEXP: matching blank entries

I have this SQL condition that is supposed to retrieve all rows that satisfy the given regexp condition:
country REGEXP ('^(USA|Italy|France)$')
However, I need to add a pattern for retrieving all blank country values. Currently I am using this condition
country REGEXP ('^(USA|Italy|France)$') OR country = ""
How can achieve the same effect without having to include the OR clause?
Thanks,
Erwin
This should work:
country REGEXP ('^(USA|Italy|France|)$')
However from a performance point of view, you may want to use the IN syntax
country IN ('USA','Italy','France', '')
The later should be faster as REGEXP can be quite slow.
There's no reason you can't use the $ (match end of string) to fill in your "empty subexpression" issue...
It looks a little weird but country REGEXP ('^(USA|Italy|France|$)$') will actually work
You could try:
country REGEXP ('^(USA|Italy|France|)$')
I just added another | after France, which should would basically tell it to also match ^$ which is the same as country = ''.
Update: since this method doesn't work, I would recommend you use this regex:
country REGEXP ('^(USA|Italy|France)$|^$')
Note that you can't use the regex: ^(USA|Italy|France|.{0})$ because it will complain that there is an empty sub expression. Although ^(USA|Italy|France)$|^.{0}$ would work.
Here are some examples of the return value of this regex:
select '' regexp '^(USA|Italy|France)$|^$'
> 1
select 'abc' regexp '^(USA|Italy|France)$|^$'
> 0
select 'France' regexp '^(USA|Italy|France)$|^$'
> 1
select ' ' regexp '^(USA|Italy|France)$|^$'
> 0
As you can see, it returns exactly what you want.
If you want to treat blank values the same (e.g. 0 spaces and 5 spaces both count as blank), you should use the regex:
country REGEXP ('^(USA|Italy|France|\s*)$')
This will cause the last row in the previous example to behave differently, i.e.:
select ' ' regexp '^(USA|Italy|France|\s*)$'
> 1