How to write this doctrine query with multiple joins? - doctrine-orm

I have an entity Person (p) which is related to an entity Notifications related to an entity NotificationType.
Some persons have received a notification of type 1 (notification_type_id = 1).
Some have received notifications but not of the type 1
some haven't received messages at all.
Globally, I want to retrieve those who haven't received the messages of type 1.
I have written this query :
$qb = $this->createQueryBuilder('p')
->leftJoin('p.notifications', 'n')
->leftJoin('p.notification_type', 'nt')
->addSelect(array('p','n','nt'))
->where('nt.id NOT IN (1)')
->orderBy('p.name', 'ASC');
return $qb->getQuery()->getResult();
But with this query I only get those who have received notifications but not of type 1, I don't get those who haven't received notifications at all.
How can I rectify my query to have those ones ?
Thanks a lot for your help

NOT IN with null values (for your joins where there are no messages) will not parse as you expect. Similarly for eq or ne.
Try:
$qb = $this->createQueryBuilder('p');
$results = $qb->leftJoin('p.notifications', 'n')
->leftJoin('p.notification_type', 'nt')
->addSelect(array('p','n','nt'))
->where('nt.id !=1')
->orWhere('nt.id IS NULL')
->orderBy('p.name', 'ASC');
return $qb->getQuery()->getResult();
You can also put these as conditions on your join, refer to 13.2.5. The Expr class of the doctrine code
As an aside, you probably don't need the select since you can access those values programmatically via getters in the entity.

Related

Getting the list of approved items from status column in dynamodb

I am trying to query to my dynamodb using paginator.paginate.
Here is my code:
for page_1 in paginator.paginate(TableName=chroma_organization_data_table,
FilterExpression='#s = Approved',
ProjectionExpression="#s, organizationId",
ExpressionAttributeNames={'#s': 'status'}
):
print page_1
However, I get nothing returned back. I know there are several entries that are in the 'approved' states.
This is how my dynamodb returns data if there are no conditions on it (no FilterExpression)(example)
[{u'organizationId': {u'S': u'323454354525'}, u'status': {u'S': u'Approved'}}]
So clearly there is an entry where status is approved just when I use paginator, it doesn't work.
What can I do about this?
You cannot embed string literals in filter/condition expressions. You need to set an ExpressionAttributeValues map equal to { ":approved": "Approved" } and then update your filter expression to be #s = :approved.

Siebel NextRecord method is not moving to the next record

I have found a very weird behaviour in our Siebel 7.8 application. This is part of a business service:
var bo:BusObject;
var bc:BusComp;
try {
bo = TheApplication().GetBusObject("Service Request");
bc = bo.GetBusComp("Action");
bc.InvokeMethod("SetAdminMode", "TRUE");
bc.SetViewMode(AllView);
bc.ClearToQuery();
bc.SetSearchSpec("Status", "='Unscheduled' OR ='Scheduled' OR ='02'");
bc.ExecuteQuery(ForwardOnly);
var isRecord = bc.FirstRecord();
while (isRecord) {
log("Processing activity '" + bc.GetFieldValue("Id") + "'");
bc.SetFieldValue("Status", "03");
bc.WriteRecord();
isRecord = bc.NextRecord();
}
} catch (e) {
log("Exception: " + e.message);
} finally {
bc = null;
bo = null;
}
In the log file, we get something like this:
Processing activity '1-23456'
Processing activity '1-56789'
Processing activity '1-ABCDE'
Processing activity '1-ABCDE'
Exception: The selected record has been modified by another user since it was retrieved.
Please continue. (SBL-DAT-00523)
So, basically, it processes a few records from the BC and then, apparently at random, it "gets stuck". It's like the NextRecord call isn't executed, and instead it processes the same record again.
If I remove the SetFieldValue and WriteRecord to avoid the SBL-DAT-00523 error, it still shows some activities twice (only twice) in the log file.
What could be causing this behaviour?
It looks like in business component "Action" you have join(s) that can return multiple records for one base record and you use ForwardOnly mode to query BC.
Assume, for example, in table S_EVT_ACT you have one record with a custom column X_PHONE_NUMBER = '12345678' and you have two records in table S_CONTACT with column 'MAIN_PH_NUM' equal to the same value '12345678'. So when you will join these two tables using SQL like this:
SELECT T1.* FROM SIEBEL.S_EVT_ACT T1, SIEBELS_CONTACT T2
WHERE T1.X_PHONE_NUMBER = T2.MAIN_PH_NUM
as a result you will get two records, with the same T1.ROW_ID.
Exactly the same situation happens when you use ForwardOnly cursor mode in eScript, in this case Siebel just fetches everything what database has returned. And that why it's a big mistake to iterate over business component while it's queried in a ForwardOnly mode. You should use ForwardBackward mode instead, because in this case Siebel will exclude duplicates records (it also true for normal UI queries, because it also executed in ForwardBackward mode).
Actually this is the most important and less known difference between ForwardOnly and ForwardBackward cursor modes.
Try changing that query mode
bc.ExecuteQuery(ForwardOnly);
to ForwardBackward.

CRM 2015 Microsoft.Xrm.Sdk: Unexpected results in second CreateQuery call

Microsoft.Xrm.Sdk, Version=7.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
var ctx = new ServiceContext(...);
var result1 = (from f in ctx.CreateQuery<aEntity>()
where f.field1 == "x"
select new { f.Id, f.field2 }).ToList();
var result2 = (from f in ctx.CreateQuery<aEntity>()
where f.field1 == "x"
select f.field1).First();
result2 returns null! After adding f.field1 to the select clause in the first query result2 returns "x". It looks like a internal columnset is created and used in the context of the second call. Looking at the SQL Server trace of both calls we see the expected select-from queries based on the C# code. The returned second result is not expected. Can someone explain this behaviour?
As for me it looks like a caching functionality and it's on the side of CRM because as you mentioned SQL queries were correct. I had the same issue in my applications when tried to make two consecutive queries for the same entity record but selected two different fields, the second request always returned NULL. Here are workarounds that I use when work with the ServiceContext:
Simple one: always retrieve an entity with all fields (without select statement) (even if I want it or not)
or create a service context with disabled caching
Right now I try to use the ServiceContext as less as possible replacing it with QueryBase expressions (even if I love to use LINQ).
Keep in mind LINQ CRM driver implementation is a subset of SQL only.
Could you try something like this?
var result1 = (from f in ctx.CreateQuery<aEntity>()
where f.field1 == "x"
select new CustomClass {
Id = f.aEntityId,
Field2 = f.field2
}).ToList();
You can have complex queries if you want, but you need to know what can be done and what can't be done.
Id property is not always returned by the driver, but the entity's primary key is, which is normally the entity logical name + "Id".

Doctrine2 simple where clause issue

Short and Simple: From time to time we send gifts to some of our users. I have a user table and a gift table with a many-to-many relationship. I want to fetch all users which did NOT receive a particular gift.
The following query however returns me all users and the gifts they've received, with the particular gift excluded.
$qb = $this->_em->createQueryBuilder();
$qb->select('u, g')
->from('Application\Entity\User', 'u')
->leftJoin('u.gifts', 'g')
->where('g.id != = :giftId')
->setParameter('giftId', 2);
If a user received a particular gift, I want to exclude that user from the result set. Is this possible with Doctrine2?
You first need to select all users, then exclude those who had your gift already:
SELECT
u
FROM
Application\Entity\User u
WHERE
u.id NOT IN(
SELECT
u2.id
FROM
Application\Entity\User u2
JOIN
u2.gifts g
WHERE
g.id = :giftId
)
In QueryBuilder API, it looks like following:
$qb1 = $em->createQueryBuilder();
$qb2 = $em->createQueryBuilder();
$qb2
->select('u2')
->from('Application\Entity\User', 'u2')
->join('u2.gifts', 'g')
->andWhere($qb2->expr()->eq('g.id', ':giftId');
$users = $qb1
->select('u')
->from('Application\Entity\User', 'u')
->andWhere($qb1->expr->in($qb2->getDQL())
->setParameter('giftId', $giftId)
->getQuery()
->getResult();
Also, I don't personally think the QueryBuilder is suited for this use case unless you have dynamic DQL. As you can see, the query gets quite complex, and at some point you're even falling back to QueryBuilder#getDQL, which builds the DQL string and makes recycling of $qb2 impossible.
Plain DQL works just fine here.

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');