Doctrine2 - Specific NULL sorting - doctrine-orm

When you sort on column a descending, it will put all records having a NULL at the bottom, which makes sense. What I want is the following ordering when I'm sorting on attribute a descending:
a
----
NULL
NULL
5
4
3
2
1
I know Oracle has NULLS_FIRST and in MySQL you could use ORDER BY ISNULL(a), but I'm wondering whether there is an elegant way to deal with this using Doctrine2 functionality only? So not using native queries, etc.
Answer with the help of S0pa: I fixed it using the following new function node for Doctrine2:
<?php
namespace MyBundle\General;
use \Doctrine\ORM\Query\Lexer;
class IsnullFunctionNode extends \Doctrine\ORM\Query\AST\Functions\FunctionNode
{
private $isnull;
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->isnull = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
return 'ISNULL('.$this->isnull->dispatch($sqlWalker).')';
}
}
And connected it in Symfony2 to Doctrine2 like this (app.yml):
doctrine:
orm:
entity_managers:
default:
...
dql:
numeric_functions:
isnull: Diagenda\CommonBundle\General\IsnullFunctionNode
I can't use these functions in my ORDER BY clause, but by doing a SELECT ISNULL(a) AS n_a I can order on the n_a attribute.

I think the best way is to define your own DQL function, using this tutorial. You will still need to handle different case depending on your DB management system.

Related

Is there a way how to address nested properties in AWS DynamoDB for purpose of documentClient.query() call?

I am currently testing how to design a query from AWS.DynamoDB.DocumentClient query() call that takes params: DocumentClient.QueryInput, which is used for retrieving data collection from a table in DynamoDB.
Query seems to be simple and working fine while working with indexes of type String or Number only. What I am not able to make is an query, that will use a valid index and filter upon an attribute that is nested (see my data structure please).
I am using FilterExpression, where can be defined logic for filtering - and that seems to be working fine in all cases except cases when trying to do filtering on nested attribute.
Current parameters, I am feeding query with
parameters {
TableName: 'myTable',
ProjectionExpression: 'HashKey, RangeKey, Artist ,#SpecialStatus, Message, Track, Statistics'
ExpressionAttributeNames: { '#SpecialStatus': 'Status' },
IndexName: 'Artist-index',
KeyConditionExpression: 'Artist = :ArtistName',
ExpressionAttributeValues: {
':ArtistName': 'BlindGuadian',
':Track': 'Mirror Mirror'
},
FilterExpression: 'Track = :Track'
}
Data structure in DynamoDB's table:
{
'Artist' : 'Blind Guardian',
..
'Track': 'Mirror Mirror',
'Statistics' : [
{
'Sales': 42,
'WrittenBy' : 'Kursch'
}
]
}
Lets assume we want to filter out all entries from DB, by using Artist in KeyConditionExpression. We can achieve this by feeding Artist with :ArtistName. Now the question, how to retrieve records that I can filter upon WritenBy, which is nested in Statistics?
To best of my knowledge, we are not able to use any other type but String, Number or Binary for purpose of making secondary indexes. I've been experimenting with Secondary Indexes and Sorting Keys as well but without luck.
I've tried documentClient.scan(), same story. Still no luck with accessing nested attributes in List (FilterExpression just won't accept it).
I am aware of possibility to filter result on "application" side, once the records are retrieved (by Artists for instance) but I am interested to filter it out in FilterExpression
If I understand your problem correctly, you'd like to create a query that filters on the value of a complex attribute (in this case, a list of objects).
You can filter on the contents of a list by indexing into the list:
var params = {
TableName: "myTable",
FilterExpression: "Statistics[0].WrittenBy = :writtenBy",
ExpressionAttributeValues: {
":writtenBy": 'Kursch'
}
};
Of course, if you don't know the specific index, this wont really help you.
Alternatively, you could use the CONTAINS function to test if the object exists in your list. The CONTAINS function will require all the attributes in the object to match the condition. In this case, you'd need to provide Sales and WrittenBy, which probably doesn't solve your problem here.
The shape of your data is making your access pattern difficult to implement, but that is often the case with DDB. You are asking DDB to support a query of a list of objects, where the object has a specific attribute with a specific value. As you've seen, this is quote tricky to do. As you know, getting the data model to correctly support your access patterns is critical to your success with DDB. It can also be difficult to get right!
A couple of ideas that would make your access pattern easier to implement:
Move WrittenBy out of the complex attribute and put it alongside the other top-level attributes. This would allow you to use a simple FilterExpression on the WrittenBy attribute.
If the WrittenBy attribute must stay within the Statistics list, make it stand alone (e.g. [{writtenBy: Kursch}, {Sales: 42},...]). This way, you'd be able to use the CONTAINS keyword in your search.
Create a secondary index with the WrittenBy field in either the PK or SK (whichever makes sense for your data model and access patterns).

Will Doctrine2 select all fields on all associations (JOINS from a query) to populate the full aggregate object?

I'm researching whether to try Doctrine2 or not. One thing that scares me is the over SELECTing of columns I don't need (ie. consider lots of varchars being selected unnecessarily).
You might ask: but don't you want your full entity object filled? Yes, unless I'm looking for an array hydration. However, many times I don't need the full aggregation filled. Take the association shown below. If I query the Users table with a JOIN on Address, will all the columns from the address table be SELECTed as well (and therefore populated into an address object inside of users object)? Now imagine we have more JOINs. This could get really bad. What if I only want the fields from User populated in just a users only object? I guess I'm a little confused at what Doctrine is doing behind the scenes with associations and query JOINs.
/** #Entity **/
class User
{
// ...
/**
* #ManyToOne(targetEntity="Address")
* #JoinColumn(name="address_id", referencedColumnName="id")
**/
private $address;
}
/** #Entity **/
class Address
{
// ...
}
So does Doctrine2 populate all the fields of all the objects within the aggregate after a query (unless I specifiy partial)?
It depends on your query, but generally it is not implicit.
Using the query builder, you can fetch the associated record like this:
<?php
$qb = $em->createQueryBuilder();
$query = $qb->select(array("u", "a"))
->from("User", "u")
->innerJoin("u.address", "a")
->getQuery();
In the select() statement you specify what to fetch, in this case you get both.
If you only fetch the User records, then when you get the associated record with $user->getAddress(), Doctrine will make the query on the fly and hydrate the Address record for you.
That said, performance wise it is better to select both entities so Doctrine will make only one query and not 1+N queries

Preventing lazy-loading gives me 1 "subentity"

I have a problem with Doctrine (Symfony2.1). I want to prevent lazy-loading by join fetching subentities (OneToMany relation) but I got only one result for those subentities.
For example:
public function getSingleProjectQuery($project){
$query = $this->createQueryBuilder('p')
->select(array("p", "fb"))
->where('p.id = :project_id')->setParameter('project_id', $project)
->leftJoin('p.feedbacks', 'fb')
->groupBy('p.id')
->getQuery();
return $query;
}
In this example Doctrine returns me the "Project"-object and one single "feedback" object (but there are more than one feedbacks...).
When I replace the select to this: ->select(array("p"))
I got all the "Feedback" objects but there are then lazy-loaded (many queries).
see http://docs.doctrine-project.org/en/latest/reference/dql-doctrine-query-language.html#joins
You should remove the groupBy clause.

Restrict query results based on attributes of 1 item in 1-n association

I have an entity 'Order', and a one-to-many associated entity 'OrderStatus' (so 1 order can have many statuses). The current status of an order is defined by the last status that was added to that order.
Now I want to create a DQL query which selects all orders with a certain status. However, because this is a one-to-many relation, I have no idea how to accomplish this in DQL. I only know of querying the collection of statuses as a whole.
Does anyone have an idea if this is even possible, or do I have to use a workaround?
We had this very same problem in a project we're working on. The query you're trying to perform is very similar to this question, except that you're trying to do this in DQL, which makes it even harder. I think (but I might be wrong) that DQL does not allow this kind of query, and you may achieve the result you're expecting with a native SQL query, with all the caveats this implies.
What we ended up with, and I strongly suggest to you, is to make the current status a property of your Order. This allows easy & fast querying, with no joins required. The change is really painless:
class Order
{
/**
* #ManyToOne(targetEntity="OrderStatus")
*/
protected $status;
/**
* #OneToMany(targetEntity="OrderStatus")
*/
protected $statuses;
public function setStatus($status)
{
$orderStatus = new OrderStatus($this, $status);
$this->statuses->add($orderStatus);
$this->status = $orderStatus;
}
}
$status can also be a simple string property, if your OrderStatus is basically composed of a status string and a date; the code would then become:
class Order
{
/**
* #Column(type="string")
*/
protected $status;
// ...
public function setStatus($status)
{
// ...
$this->status = $status;
}
}

How to convert entity leaving it id

There are some entities (Region, Country, City) which used STI (or even CTI). How it possible convert Country to City leaving old id?
This is not supported in Doctrine 2 because type casting user objects is not supported by PHP.
With that said, Doctrine uses the discriminator column to determine what type of object to hydrate. If you change the value of the discriminator column in the database with a SQL UPDATE, the object type will be changed the next time the object is loaded. This works for STI, but CTI would be more complicated.
It may not be possible by standard using Doctrine, but you can work around it.
If you use the Class Metadata you can select your discriminator column.
Take a look at the Trait that I've created to solve the problem within my app:
namespace App\Doctrine\Repository;
trait DiscriminatorTrait
{
abstract public function getClassMetadata();
abstract public function getEntityManager();
private function updateDiscriminatorColumn($id, $class)
{
$classMetadata = $this->getClassMetadata();
if (!in_array($class, $classMetadata->discriminatorMap)) {
throw new \Exception("invalid discriminator class: " . $class);
}
$identifier = $classMetadata->fieldMappings[$classMetadata->identifier[0]]["columnName"];
$column = $classMetadata->discriminatorColumn["fieldName"];
$value = array_search($class, $classMetadata->discriminatorMap);
$connection = $this->getEntityManager()->getConnection();
$connection->update(
$classMetadata->table["name"],
[$column => $value],
[$identifier => $id]
);
}
}
I do have to warn you though, when your sub-classes have (a lot of) extra fields you will need to fill or clear them manually afterwards.