Regex with Doctrine 2 query builder? - regex

As per the title, how would one match on a regular expression with the Doctrine 2 query builder? Basically I'm trying to generate unique slugs.
Here is my current implementation. I generate the slug. I then check to see if there are any slugs in use like this slug. If there are, I will append a -{number} to the end of the slug where {number} is the lowest number not already in use.
$qb->select(array('partial o.{id, slug}'))
->from('Foo\Bar\Entity\Object', 'o')
->where($qb->expr()->like('o.slug', ':slug'));
$slug = new SlugNormalizer($text);
$qb->setParameter('slug', $slug->__toString().'-%');
The problem here is LIKE slug% could match foo-bar-1, foo-bar-2, AND foo-bar-not-the-same-slug. What would be cleaner is a regex looking for REGEX slug-(\d+) or something similar.
Any way to do this with the Doctrine 2 query builder?

install the DoctrineExtensionsBundle:
composer require beberlei/doctrineextensions
add the REGEXP configuration - update your app/config.yml
doctrine:
orm:
dql:
string_functions:
regexp: DoctrineExtensions\Query\Mysql\Regexp
where ever your QueryBuilder is do this:
$qb = $this->createQueryBuilder('x');
return $qb->andWhere('REGEXP(x.your_property, :regexp) = true')
->setParameter('regexp', '[[:digit:]]{3}') // insert your own regex here
->getQuery()->getResult();
and don't forget to use SQL compatible regexes

REGEXP is a vendor specific function so Doctrine itself doesn't support it. Plus it's not a function so much as a comparison operator (see this answer). But you can use the function on the field to compare with another value. DoctrineExtensions (written by a contributor to doctrine) has code to enable regular expression in MySQL.
Example from the File:
$query = $this->getEntityManager()->createQuery('SELECT A FROM Entity A WHERE REGEXP(A.stringField, :regexp) = 1');
$query->setParameter('regexp', '^[ABC]');
$results = $query->getArrayResult();
If you don't want to use DoctrineExtensions, you can write your own by following this blog post, or you can look at the code for this Doctrine extension and write your own custom DQL function.
I have confirmed that REGEXP using DoctrineExtensions works great for my needs!

Not tested (for MySQL):
$qb->where(new Doctrine\ORM\Query\Expr\Comparison(
'o.slug', 'REGEXP', ':slug')
);
$qb->setParameter('slug', '^'.$slug->__toString().'-[[:digit:]]+$');

I did like this
$query->andWhere('REGEXP(r.status, :text) = 1')
->orWhere('REGEXP(r.comment, :text) = 1')
->setParameter('text',MY REGULAR EXP);

Related

Django advanced LIKE filtering

So I'm trying to find a nice way to execute an advanced filter using the LIKE statement in Django.
Let's say I have the following records in a table called elements:
id = 1, name = 'group[1].car[8]'
id = 2, name = 'group[1].car[9]'
id = 3, name = 'group[1].truck[1]'
id = 4, name = 'group[1].car[10]'
id = 4, name = 'group[1].carVendor[1]'
I would like to select all elements that look like group[x].car[y].
To query this in SQL I would do:
SELECT * FROM elements WHERE name LIKE 'group[%].car[%]'
Now, by reading the Django documentation here, I see that the only pre-built LIKE statements are the following:
contains: name LIKE '%something%'
startswith: name LIKE 'something%'
endswith: name LIKE '%something'
So the one I need is missing:
plain like: name LIKE 'group[%].car[%]'
I'm also using Django Rest Framework to write up my API endpoints and also here we find the possibility to use:
contains: name__contains = something
startswith: name__startswith = something
endswith: name__endswith = something
So also here, the one I need is missing:
plain like: name__like 'group[%].car[%]'
Of course I know I can write a raw sql query through Django using the raw() method, but I would like to use this option if no better solution comes up, because:
I need to make sure my customization is safe
I need to extends the customization to DRF
Can anybody think of a way to help me out with this in a way to go with the flow with both Django and Django Rest Framework?
You can use a regular expression (regex) [wiki] for this, with the __iregex lookup [Django-doc]:
Elements.objects.filter(name__iregex=r'^group\[.*\].car\[.*\]$')
if between the square brackets, only digits are allowed, we can make it more specific with:
# only digits between the square brackets
Elements.objects.filter(name__iregex=r'^group\[\d*\].car\[\d*\]$')
Since some the specifications are a bit "complex" it is better to first test your regex, for example with regex101 you can look what names will be matched, and which will not.

Include wildcard (%) in Django 'contains' query

According to Django docs
Entry.objects.get(headline__contains='Lennon')
Roughly translates to this SQL:
SELECT ... WHERE headline LIKE '%Lennon%';
But if I want to do somethng like this (removing a wildcard):
SELECT ... WHERE headline LIKE '%Lennon';
What would the Django query be?
The keywords for partial field lookups you are looking for are startswith and endswith:
Entry.objects.filter(headline__startswith='Lennon')
Entry.objects.filter(headline__endswith='Lennon')
You can also use the case insensitive variants, istartswith and iendswith:
Entry.objects.filter(headline__istartswith='lennon')
Entry.objects.filter(headline__iendswith='lennon')

Django istartswith and contains in same query?

I need to build a simple username search. I want to have it ordered by usernames that start with the query and then after usernames that contain the string.
if I have a list of usernames like this
john
matt
atom
atyler
mrmat
I want it to order like so:
atom
atyler
matt
mrmat
How would I achieve this in a single query? Right now I have
users = User.objects.filter(Q(name__istartswith='at') | Q(name__icontains='at'))
But this returns:
matt
mrmat
atyler
atom
class Z(Func):
function = 'ilike'
template = "%(expressions)s %(function)s 'in%%%%'"
for u in User.objects.filter(username__icontains('in'))\
.annotate(q=Z(F('username')))\
.order_by('-q', 'username'):
print u.username, u.q
ilike is a postgres extension, for mysql you'll have to either sacrifice case insensitivity or write the corresponding workaround for that.
The "old" way:
User.objects.filter(username__icontains='at')\
.extra(select={'q': "username ilike 'at%%%%'"},
order_by=['-q', 'username'])
This code block filters the queryset and orders it as required by OP.
Also the note about ilike from the second answer applies here.

In querying a collection in a Mongo database with Mongoose, how can I find where a value is LIKE a query term?

I've search hard for an answer to this but haven't found anything that works. I have a NodeJS app, with the Mongoose ORM. I'm trying to query my Mongo database where a result is LIKE the query.
I have tried using a new RegExp to find the results, but it hasn't worked for me. The only time I get a result is when the query is exactly the same as the collection property's value.
Here's what I'm using right now:
var query = "Some Query String.";
var q = new RegExp('^/.*'+ query +'.*/i$');
Quote.find({author: q}, function(err, doc){
cb(doc);
});
If the value of an author property contains something LIKE the query (for instance: 'some. query String'), I need to return the results. Perhaps stripping case, and excluding special characters is all I can do? What is the best way to do this? My RegEx in this example is obviously not working. Thanks!
You likely want to create your RegExp as follows instead as you don't include the / chars when using new RegExp:
var q = new RegExp(query, 'i');
I don't know of a way to ignore periods in the author properties of your docs with a RegExp though. You may want to look at $text queries for more flexible searching like that.

django filter icontains match whole words only

I am using the filter icontains to search for words but I only want it to match whole words. e.g. if I searched for liver I wouldn't want it returning delivery.
my query looks like this
MyModel.objects.filter(title__icontains=search_word)
I have seen the filter __search but this does not bring back results with 3 characters or less and the site I am building contains a lot of these which could be searched for, e.g. 'bbc'
I do not have access to the db but if anyone knows how I can disable this in the code then I would be happy to switch to using this as an alternative.
Regexp are usually enough :
http://docs.djangoproject.com/en/dev/ref/models/querysets/#regex
Please note that the regular expression syntax is that of the database backend in use.
In python (sqlite) the regexp would be :
\b(word)\b
In Mysql you have :
mysql> SELECT 'a word a' REGEXP '[[:<:]]word[[:>:]]'; -> 1
mysql> SELECT 'a xword a' REGEXP '[[:<:]]word[[:>:]]'; -> 0
In case you have angularjs and REST service implemented with tastypie or DRF, you can filter by whole words as $http.get(uri, {'params': {'display_name__iregex': '[[:<:]]word[[:>:]]'})
of course, display_name should be enabled to filtering in Tastypie resource's Meta class as filtering = {'display_name': ALL,}
It sounds like you want a Case-insensitive exact match.
MyModel.objects.filter(title__iexact=search_word)
http://docs.djangoproject.com/en/dev/ref/models/querysets/#lookup-iexact