Doctrine query + LIKE expression - doctrine-orm

I have problems to create a Doctrine Query with LIKE Expression:
QUERY:
$dql = "SELECT u FROM Users u JOIN u.group g WHERE g.name LIKE lower('ADMIN')";
$query = $em->createQuery($dql);
$result = $query->getResult();
ERROR:
QueryException: [Syntax Error] line 0, col 147: Error: Expected Doctrine\ORM\Query\Lexer::T_STRING, got 'lower'
LOWER was just an example, I need to use other functions in LIKE
EXPRESSION, for example, unnacent...
How can I change Like Expression to support function on both sides?
Example: LOWER(unaccent(u.login)) LIKE LOWER(unaccent('ADMIN'))

The like string needs to have % signs. If you want something that starts with ADMIN then you would write ADMIN% if you want something that ends with ADMIN you would write %ADMIN and finally if you want it to contain ADMIN then it would be %ADMIN%.
Maybe to return a string you can use the CONCAT function of doctrine or you can do it via PHP.

I tested this with QueryBuilder and there doesn't seem to be a solution. The second parameter will not take a function, so I would suggest switching the parameters around:
$dql = "SELECT u FROM Users u JOIN u.group g WHERE UPPER(g.name) LIKE 'ADMIN'";

Related

Ignite SqlFieldsQuery specific keys

Using the ignite C++ API, I'm trying to find a way to perform an SqlFieldsQuery to select a specific field, but would like to do this for a set of keys.
One way to do this, is to do the SqlFieldsQuery like this,
SqlFieldsQuery("select field from Table where _key in (" + keys_string + ")")
where the keys_string is the list of the keys as a comma separated string.
Unfortunately, this takes a very long time compared to just doing cache.GetAll(keys) for the set of keys, keys.
Is there an alternative, faster way of getting a specific field for a set of keys from an ignite cache?
EDIT:
After reading the answers, I tried changing the query to:
auto query = SqlFieldsQuery("select field from Table t join table(_key bigint = ?) i on t._key = i._key")
I then add the arguments from my set of keys like this:
for(const auto& key: keys) query.AddArgument(key);
but when running the query, I get the error:
Failed to bind parameter [idx=2, obj=159957, stmt=prep0: select field from Table t join table(_key bigint = ?) i on t._key = i._key {1: 159956}]
Clearly, this doesn't work because there is only one '?'.
So I then tried to pass a vector<int64_t> of the keys, but I got an error which basically says that std::vector<int64_t> did not specialize the ignite BinaryType. So I did this as defined here. When calling e.g.
writer.WriteInt64Array("data", data.data(), data.size())
I gave the field a arbitrary name "data". This then results in the error:
Failed to run map query remotely.
Unfortunately, the C++ API is neither well documented, nor complete, so I'm wondering if I'm missing something or that the API does not allow for passing an array as argument to the SqlFieldsQuery.
Query that uses IN clause doesn't always use indexes properly. The workaround for this is described here: https://apacheignite.readme.io/docs/sql-performance-and-debugging#sql-performance-and-usability-considerations
Also if you have an option to to GetAll instead and lookup by key directly, then you should use it. It will likely be more effective anyway.
Query with operator "IN" will not always use indexes. As a workaround, you can rewrite the query in the following way:
select field from Table t join table(id bigint = ?) i on t.id = i.id
and then invoke it like:
new SqlFieldsQuery(
"select field from Table t join table(id bigint = ?) i on t.id = i.id")
.setArgs(new Object[]{ new Integer[] {2, 3, 4} }))

Doctrine createQuery() conditional params

I am trying to put a conditional parameter in my query. if orgID is null i do not want the query to use it as a filter.
AND (:orgID IS NULL OR o.id = :orgID)
I am struggling trying to do this. What am i doing wrong?
$em = $this->getEntityManager();
$query = $em->createQuery('
select count(d.id)
from \***\OrganizationBundle\Entity\Organization o
inner join \***\OrganizationBundle\Entity\Facility f
inner join \***\OrganizationBundle\Entity\Department d
where d.active = true
AND f.active = true
AND o.active = true
AND d.name LIKE :name
AND (:orgID IS NULL OR o.id = :orgID)
');
$query->setParameter(':name', '%' . $name . '%');
$query->setParameter(':orgID', $orgID);
$count = $query->getSingleScalarResult();
Both #Cerad and #xabbuh from comment section are right. Either skip appending the predicate or use QueryBuilder to conditionally make your query string.
In any case, remember that prepared statements placeholders cannot be used for table names and columns.
Also, from performance standpoint, you need not to run a query if you know for a fact that it will return previously known results. That's just waste of resources.
I agree with what others have said - use QueryBuilder as this is the Symfony / Doctrine way.
But if you want to solve it on the MySQL query level, then you will need something like "conditional WHERE clause", for example using CASE expression.
Replace
AND (:orgID IS NULL OR o.id = :orgID)
with
AND
CASE
WHEN :orgID IS NULL THEN TRUE
ELSE o.id = :orgID
END
The first condition explicitly evaluates to TRUE, which is essentially equal to your requirement: "I do not want the query to use it as a filter". (Although technically this is still a filter).

How to order by a computed value in DQL

I'm trying to order the results of my query by whether or not they match my original entity on a property. I could do this easily in mySQL with the following query:
SELECT * FROM table
ORDER BY prop = 'value' DESC;
However, in Doctrine, when I attempt the following:
// $qb is an instance of query builder
$qb->select('e')
->from('Entity', 'e')
->orderBy('e.prop = :value', 'DESC')
->setParameter('value', 'value');
// grab values
I get a Doctrine syntax error, 'end of string'. I looked into creating a custom function, but that seems like overkill. I'm fairly new to Doctrine, is there a better way to do this?
Since Doctrine ORM 2.2, you can use the HIDDEN keyword and select additional fields, in this case with a CASE expression:
SELECT
e,
CASE WHEN e.prop = :value THEN 1 ELSE 0 END AS HIDDEN sortCondition
FROM
Entity e
ORDER BY
sortCondition DESC
As I struggeled a while to figure out how to create that query using php syntax here's what I came up with:
$value = 'my-value';
$qb->select('e')
->from('Entity', 'e')
->addSelect('CASE WHEN e.prop = :value THEN 1 ELSE 0 END AS HIDDEN sortCondition')
->setParameter('value', $value)
->addOrderBy('sortCondition', 'DESC');

How to write a DQL select statement to search some, but not all the entities in a single table inheritance table

So I have 3 entities within one table. I need to be able to search 2 out of the 3 entities in one select statement, but I'm not sure how to do this.
Use the INSTANCE OF operator in your dql query like this (where User is your base class):
$em->createQuery('
SELECT u
FROM Entity\User u
WHERE (u INSTANCE OF Entity\Manager OR u INSTANCE OF Entity\Customer)
');
Doctrine translates this in the sql query in a WHERE user.type = '...' condition.
See here for more details on the dql query syntax.
The answer for multiple instances actually doesn't work. You would have to do something like this to check for multiple instances.
$classes = ['Entity\Manager', 'Entity\Customer'];
$qb = $this->createQueryBuilder('u');
->where('u.id > 10') //an arbitrary condition, to show it can be combined with multiple instances tests
->andWhere("u INSTANCE OF ('" . implode("','", $classes) . "')");
As commented by flu, if you want to retrieve some entities from different instances with a QueryBuilder instead of a DQL query, you can use an array as parameter:
$qb = $this->createQueryBuilder('u');
->where('u.id > 10') //an arbitrary condition, to show it can be combined with multiple instances tests
->andWhere('u INSTANCE OF :classes')
->setParameter('classes', ['Entity\Manager', 'Entity\Customer'])
;

Django: RawQuerySet problem with passing multiple params

I've used answer from this question:
Django: making raw SQL query, passing multiple/repeated params?
but have some problems.
I have params:
params = {'film_id_string': 'core_film.parent_id', 'tags_params': 'comedy', 'order_by': 'core_film.title', 'content_type': '18', 'language_code': 'en'}
for SQL query:
query = 'SELECT DISTINCT "core_object".*, "core_film".* FROM "core_film" INNER JOIN "core_object" ON ("core_film"."parent_id" = "core_object"."id") LEFT OUTER JOIN "core_objectlocalized" ON ("core_objectlocalized"."parent_id" = %(film_id_string)s) LEFT OUTER JOIN "tagging_taggeditem" ON ("tagging_taggeditem"."object_id" = "core_objectlocalized"."id") LEFT OUTER JOIN "tagging_tag" ON ("tagging_tag"."id" = "tagging_taggeditem"."tag_id") WHERE "tagging_tag"."name" IN (%(tags_params)s) AND "core_objectlocalized"."LANG"=%(language_code)s AND content_type_id=%(content_type)s ORDER BY %(order_by)s'
When I tried to use RawQuerySet
films = Film.objects.raw(query, params)
I get:
SELECT DISTINCT "core_object".*, "core_film".*
FROM "core_film"
INNER JOIN "core_object" ON ("core_film"."parent_id" = "core_object"."id")
LEFT OUTER JOIN "core_objectlocalized" ON ("core_objectlocalized"."parent_id" = E\'core_film.parent_id\')
LEFT OUTER JOIN "tagging_taggeditem" ON ("tagging_taggeditem"."object_id" = "core_objectlocalized"."id")
LEFT OUTER JOIN "tagging_tag" ON ("tagging_tag"."id" = "tagging_taggeditem"."tag_id")
WHERE "tagging_tag"."name" IN (E\'comedy\')
AND "core_objectlocalized"."LANG"=E\'en\'
AND content_type_id=E\'18\'
ORDER BY E\'core_film.title\'
Problem is, that every place with 'E\' generate error similar to this:
DatabaseError: invalid input syntax for integer: "core_film.parent_id"
LINE 1: ...calized" ON ("core_objectlocalized"."parent_id" = E'core_fil...
How can I fix this?
Django version 1.2.3.
edit
I can't remove quotes, because I work with string:
result = self.function(result, tag, "core_film.parent_id")
def function(self, objects, tags, film_id_string):
My params for RawQuerySet look like this:
params = {'film_id_string': film_id_string}
When I try to parse this I get:
LEFT OUTER JOIN "core_objectlocalized" ON ("core_objectlocalized"."parent_id" = E\'core_film.parent_id\')
and then I have problems with
DatabaseError: invalid input syntax for integer: "core_film.parent_id"
LINE 1: ...calized" ON ("core_objectlocalized"."parent_id" = E'core_fil...
but, when I use string formatting
LEFT OUTER JOIN "core_objectlocalized" ON ("core_objectlocalized"."parent_id" = %s)' % film_id_string
it works:
LEFT OUTER JOIN "core_objectlocalized" ON ("core_objectlocalized"."parent_id" = core_film.parent_id)
I want to ommit posibility of SQL injection, so basing on Django docs I don't want to pass params with string formatting.
What can I else do?
Django prevents SQL interjection by escaping parameters. This is an SQL statement, therefore the fact that it does not work is a good thing - Django is doing it's job. Unless the users of the system will be setting the value for "film_id_string" what you did to get it to work should be ok. If not, then you would have to still used the "...%s)' % film_id_string..." method but create your own custom filter to validate that it is one of the correct allowed values.