Doctrine, QueryBuilder : use particular "where" with parenthesis - postgresql

I got a problem to make a "simple" query with the Doctrine QueryBuilder.
I try to get some "persons" which are on 10 km max.
My query :
$QB = $this->createQueryBuilder('p');
$QB->add('select', 'p')
->add('from', 'MyProject\Bundle\FrontBundle\Entity\Pro p')
->where('p.job = :job')
->andWhere('(3956 * 2 * ASIN(SQRT( POWER(SIN((:latitude - abs(pro.latitude)) * pi()/180 / 2),2) + COS(:latitude * pi()/180 ) * COS(abs(pro.latitude) * pi()/180) * POWER(SIN((:longitude - pro.longitude) * pi()/180 / 2), 2) ))) <= 10')
->addOrderBy('p.dateCreation', 'DESC')
->addOrderBy('p.id', 'DESC')
->setParameter('latitude', $latitude)
->setParameter('longitude', $longitude)
->setParameter('job', $jobId);
The problem is on the second 'where' statement, Doctrine fails on "ASIN" because of the parenthesis that follows. It tries to execute the function...
Is there a way to escape it ? Or another way to construct this condition ?
Thanks for Doctrine professional ;)

If I understand correctly, you can either write a raw SQL query for that, or implement the function ASIN(or any other) in doctrine.
Here you have a bundle that might help:
https://github.com/wiredmedia/doctrine-extensions
You can use its implementation of ASIN function:
https://github.com/wiredmedia/doctrine-extensions/blob/master/lib/DoctrineExtensions/Query/Mysql/Asin.php
And and article about custom DQL functions:
http://symfony.com/doc/current/cookbook/doctrine/custom_dql_functions.html

Related

NULLIF how to set it properly in this query

I am using the following query to get some tablespaces usage at a glance:
db2 "select substr(tbsp_name,1,30) as Tablespace_Name, tbsp_type as Type, substr(tbsp_state,1,20) as Status, (tbsp_total_size_kb / 1024 ) as Size_Meg, smallint((float(tbsp_free_size_kb)/ float(tbsp_total_size_kb))*100) as Percent_Free_Space, int((tbsp_free_size_kb) / 1024 )as Meg_Free_Space from sysibmadm.tbsp_utilization where smallint((float(tbsp_free_size_kb)/ float(tbsp_total_size_kb))*100) < 20 order by Percent_Free_Space"
however, I'm stuck with the following error:
SQL0801N Division by zero was attempted. SQLSTATE=22012
I understand you can fix this error with a NULLIF however I can't find the correct way to set in in the query, thanks for the help.
( using "DB2 v9.7.0.11", "s150922", "IP23937", and Fix Pack
"11")
Maybe you should use case:
CASE WHEN tbsp_total_size_kb=0 THEN NULL ELSE (tbsp_total_size_kb / 1024 ) END as Size_Meg
if tbsp_total_size_kb can be null IFNULL like this:
CASE WHEN IFNULL(tbsp_total_size_kb,0)=0 THEN NULL ELSE (tbsp_total_size_kb / 1024 ) END as Size_Meg
Try this:
smallint(float(tbsp_free_size_kb) / float(nullif(tbsp_total_size_kb,
0)) * 100)

Using "BETEEN" operator on a LONG-EMBEDDED-LIST property declared in OrientDB 2.2.17

I am using OrientDB 2.2.17 (enterprise edition) and I have a problem searching on the data:
I have a vertex called: EN70 with a property: EP70_4 from type 'embedded list = LONG'.
I added a record to this vertex with the value of [2,3,4] in this 'EP70_4' property.
The index on this property: Not_Unique (sbtree). [must support range queries]
SELECT * FROM EN70 WHERE (EP70_4 >= 1) is working and returns my record.
SELECT * FROM EN70 WHERE (EP70_4 <= 3) is working and returns my record.
The operator "Between" is not woking and throw exception:
SELECT * FROM EN70 WHERE (EP70_4 BETWEEN 1 AND 3 )
I tried to simulate a "between" operator:
SELECT * FROM EN70 WHERE ((EP70_4 >= 1) AND (EP70_4 <= 3)) IS NOT WORKING
but it is not working.
both sections 6 and 7 throws an exception:
Error on using index EN70.EP70_4 in query 'SELECT * FROM EN70 WHERE (EP70_4 BETWEEN 1 AND 3 )'. Probably you need to rebuild indexes. Now executing query using cluster scan
java.lang.ClassCastException: java.util.ArrayList cannot be cast to java.lang.Long
at com.orientechnologies.common.serialization.types.OLongSerializer.preprocess(OLongSerializer.java:36)
at com.orientechnologies.orient.core.index.sbtree.local.OSBTree.iterateEntriesBetweenAscOrder(OSBTree.java:1474)
at com.orientechnologies.orient.core.index.sbtree.local.OSBTree.iterateEntriesBetween(OSBTree.java:771)
at com.orientechnologies.orient.core.index.engine.OSBTreeIndexEngine.iterateEntriesBetween(OSBTreeIndexEngine.java:185)
at com.orientechnologies.orient.core.storage.impl.local.OAbstractPaginatedStorage.doIterateIndexEntriesBetween(OAbstractPaginatedStorage.java:2065)
at com.orientechnologies.orient.core.storage.impl.local.OAbstractPaginatedStorage.iterateIndexEntriesBetween(OAbstractPaginatedStorage.java:2053)
at com.orientechnologies.orient.core.index.OIndexMultiValues.iterateEntriesBetween(OIndexMultiValues.java:275)
at com.orientechnologies.orient.core.index.OIndexAbstractDelegate.iterateEntriesBetween(OIndexAbstractDelegate.java:104)
at com.orientechnologies.orient.core.index.OIndexTxAwareMultiValue.iterateEntriesBetween(OIndexTxAwareMultiValue.java:339)
at com.orientechnologies.orient.core.sql.operator.OQueryOperatorBetween.executeIndexQuery(OQueryOperatorBetween.java:131)
at com.orientechnologies.orient.core.sql.OCommandExecutorSQLSelect.searchForIndexes(OCommandExecutorSQLSelect.java:2184)
at com.orientechnologies.orient.core.sql.OCommandExecutorSQLSelect.searchInClasses(OCommandExecutorSQLSelect.java:1001)
at com.orientechnologies.orient.core.sql.OCommandExecutorSQLResultsetAbstract.assignTarget(OCommandExecutorSQLResultsetAbstract.java:209)
at com.orientechnologies.orient.core.sql.OCommandExecutorSQLSelect.assignTarget(OCommandExecutorSQLSelect.java:530)
at com.orientechnologies.orient.core.sql.OCommandExecutorSQLSelect.executeSearch(OCommandExecutorSQLSelect.java:512)
at com.orientechnologies.orient.core.sql.OCommandExecutorSQLSelect.execute(OCommandExecutorSQLSelect.java:488)
at com.orientechnologies.orient.core.sql.OCommandExecutorSQLDelegate.execute(OCommandExecutorSQLDelegate.java:74)
at com.orientechnologies.orient.core.storage.impl.local.OAbstractPaginatedStorage.executeCommand(OAbstractPaginatedStorage.java:2624)
at com.orientechnologies.orient.core.storage.impl.local.OAbstractPaginatedStorage.command(OAbstractPaginatedStorage.java:2570)
at com.orientechnologies.orient.core.command.OCommandRequestTextAbstract.execute(OCommandRequestTextAbstract.java:69)
at com.orientechnologies.orient.server.network.protocol.http.command.post.OServerCommandPostCommand.execute(OServerCommandPostCommand.java:106)
at com.orientechnologies.orient.graph.server.command.OServerCommandPostCommandGraph.execute(OServerCommandPostCommandGraph.java:37)
at com.orientechnologies.orient.server.network.protocol.http.ONetworkProtocolHttpAbstract.service(ONetworkProtocolHttpAbstract.java:169)
at com.orientechnologies.orient.server.network.protocol.http.ONetworkProtocolHttpAbstract.execute(ONetworkProtocolHttpAbstract.java:622)
at com.orientechnologies.common.thread.OSoftThread.run(OSoftThread.java:77)
What can it be??? Please help me...
I'd say it's a bug, I strongly suggest you to report it here: https://github.com/orientechnologies/orientdb/issues

Zend Framework 1 SQL query Where

Is there any way to do something like this:
SELECT * FROM table WHERE p1=1 AND p2=2 AND ( p3 like %string1% OR p3 like %string3% )
In Zend Framework 1 by Zend_Db_Select or something else ?
Not sure about how you would go about doing such complex nested query using Zend_Db_Select but you can write can consider writing query manually as follows -
$sql = 'SELECT * FROM table WHERE p1 = ? AND p2 = ? AND (p3 LIKE ? OR p3 LIKE ?)';
$db->fetchAll($sql, [$p1, $p2, "%{$p3}%", "%{$p4}%"]);
You should rather use Zend_Db_Statement, which gives flexible options for fetching result sets. You can use:
$stmt = $db->query(
'SELECT * FROM table WHERE p1 = ? AND p2 = ? AND (p3 LIKE ? OR p3 LIKE ?',
array('1', '2', '%string1%', '%string3%')
);

Symfony2 Doctrine - ILIKE clause for PostgreSQL?

I'm currently using symfony2, doctrine 2.3 and PostgreSQL 9. I've been searching for a couple of hours now to see HOW on earth do I do a ILIKE select with QueryBuilder.
It seems they only have LIKE. In my situation, though, I'm searching case-insensitive.
How on earth is it done?
// -- this is the "like";
$search = 'user';
$query = $this->createQueryBuilder('users');
$query->where($query->expr()->like('users.username', $query->expr()->literal('%:username%')))->setParameter(':username', $search);
// -- this is where I get "[Syntax Error] line 0, col 86: Error: Expected =, <, <=, <>, >, >=, !=, got 'ILIKE'
$search = 'user';
$query = $this->createQueryBuilder('users');
$query->where('users.username ILIKE :username')->setParameter(':username', $search);
I don't know about Symfony, but you can substitute
a ILIKE b
with
lower(a) LIKE lower(b)
You could also try the operator ~~*, which is a synonym for ILIKE
It has slightly lower operator precedence, so you might need parenthesis for concatenated strings where you wouldn't with ILIKE
a ILIKE b || c
becomes
a ~~* (b || c)
The manual about pattern matching, starting with LIKE / ILIKE.
I think this guy had the same problem and got an answer:
http://forum.symfony-project.org/viewtopic.php?f=23&t=40424
Obviously, you can extend Symfony2 with SQL vendor specific functions:
http://docs.doctrine-project.org/projects/doctrine-orm/en/2.1/cookbook/dql-user-defined-functions.html
I am not a fan of ORMs and frameworks butchering the rich functionality of Postgres just to stay "portable" (which hardly ever works).
This works for me (Symfony2 + Doctrine 2)
$qq = 'SELECT x FROM MyBundle:X x WHERE LOWER(x.y) LIKE :y';
$q = $em->createQuery($qq)->setParameter(':y', strtolower('%' . $filter . '%'));
$result = $q->getResult();
Unfortunately, we still do not have the ability to create custom comparison operators...
But we can create a custom function in which we implement the comparison.
Our DQL query well look like this:
SELECT q FROM App\Entity\Customer q WHERE ILIKE(q.name, :name) = true
The class describing the ILIKE function will look like this:
class ILike extends FunctionNode
{
/** #var Node */
protected $field;
/** #var Node */
protected $query;
/**
* #param Parser $parser
*
* #throws \Doctrine\ORM\Query\QueryException
*/
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->field = $parser->StringExpression();
$parser->match(Lexer::T_COMMA);
$this->query = $parser->StringExpression();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
/**
* #param SqlWalker $sqlWalker
*
* #return string
* #throws \Doctrine\ORM\Query\AST\ASTException
*/
public function getSql(SqlWalker $sqlWalker)
{
return '(' . $this->field->dispatch($sqlWalker) . ' ILIKE ' . $this->query->dispatch($sqlWalker) . ')';
}
}
The resulting SQL will look like this:
SELECT c0_.id AS id_0,
c0_.name AS name_1,
FROM customer c0_
WHERE (c0_.name ILIKE 'paramvalue') = true
From what I am aware, search queries in Symfony2 (at least when using Doctrine) are case in-sensitive. As noted in the Doctrine QL docs:
DQL is case in-sensitive, except for namespace, class and field names, which are case sensitive.

Re-Write complex query (COS, SIN, RADIANS, ACOS) into Entity-to-SQL

The following store procedure retrives nearest 500 addresses for the given latitude and longitude. Many applications use it, and it is one of the useful query.
Is it possible to rewrite with Entity-to-SQL? If so, could you please point me to the right direction (I am not new to Entity-to-SQL)? Thanks in advance.
DECLARE #CntXAxis FLOAT
DECLARE #CntYAxis FLOAT
DECLARE #CntZAxis FLOAT
SET #CntXAxis = COS(RADIANS(-118.4104684)) * COS(RADIANS(34.1030032))
SET #CntYAxis = COS(RADIANS(-118.4104684)) * SIN(RADIANS(34.1030032))
SET #CntZAxis = SIN(RADIANS(-118.4104684))
SELECT
500 *,
ProxDistance = 3961 * ACOS( dbo.XAxis(LAT, LONG)*#CntXAxis + dbo.YAxis(LAT, LONG)*#CntYAxis + dbo.ZAxis(LAT)*#CntZAxis)
FROM
tbl_ProviderLocation
WHERE
(3961 * ACOS( dbo.XAxis(LAT, LONG)*#CntXAxis + dbo.YAxis(LAT, LONG)*#CntYAxis + dbo.ZAxis(LAT)*#CntZAxis) <= 10)
ORDER BY
ProxDistance ASC
If you are using Ms Sql Server, you can use SqlClient functions with Entity SQL
http://msdn.microsoft.com/en-us/library/bb399586.aspx
According to this those functions are available for LINQ queries aswell. I couldn't find an example but it seems straightforward.
var qry = from r in mytable
select new {Acos = SqlFunctions.ACos(r.mycloumn)};