JPA JPQL: select items when attribute of item (list/set) contains another item - list

public class Document extends Model {
...
#ManyToMany
public Set<User> accessors;
...
}
I want to select all Documents which accessors contain a certain user.
I have just minimal experiences with SQL and no experiences with JPQL.
So how to do that?
thanks in advance

SELECT d FROM Document AS d WHERE :user MEMBER OF d.accessors
Should be what you need, and it is simpler than joining tables.
Just dont forget to use the user as a parameter instead of using its id:
query.setParameter("user", user);

select distinct d from Document d inner join d.accessors a where a.id = :id
You should learn how SQL joins work, and then learn how to use joins in JPQL. That's essential. You'll find plenty of tutorials online. Google is your friend.

Related

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

Get a list of entities that match ALL the supplied ids for a related entity field in a many-to-many association

I am trying to get a list of users from the database that have ALL the tags in a criteria.
The User entity has a many-to-many association to a Tag entity.
The or version where just one of the tags have to match is working using the following code
$tagIds = array(29,30);
$this->createQueryBuilder('u')
->select('u','t')
->leftJoin('u.tags','t')
->where("t IN(:tagIds)")
->setParameter("tagIds",$tagIds)
;
Can anybody help me with getting it to work so ALL tag ids must match ?
Keep in mind this is a query to get a list of users, not just one user , so i guess every user must be checked to see if they match all the supplied tag ids.
I have tried a bunch of queries but not having any luck so far...
simple bruteforce query:
$tagIds = array(29,30);
$qb = $this->createQueryBuilder('u');
$qb
->select('u')
;
foreach($tagIds as $idx => $tagId)
{
$joinAlias = "t{$idx}";
$qb->leftJoin('u.tags', $joinAlias)
->addSelect($joinAlias)
->andWhere("{$joinAlias}.id = $tagId AND $joinAlias IS NOT NULL")
;
}
this is really bruteforce and costly query, you join each tag as a separate join, if you have lots of users and tags, this will take ages to execute.
since database is the bottleneck of your application, you should make a simple query to the database and then parse the data in your application, so you should use your query and then check which users have those 2 tags in their collections.
Ok... after a lot of searching this seems to work for me :
$tagIds = array(29,30);
$this->createQueryBuilder('u')
->select('u','t')
->leftJoin('u.tags','t')
->where("t IN(:tagIds)")
->groupBy('u.id')
->having('COUNT(DISTINCT t.id) = ' . count($tagIds))
->setParameter("tagIds",$tagIds)
;

DQL join between unrelated entities?

Can I have a DQL join between unrelated entities using WITH DQL operator? OR is definign relationship absolutely mandatory?
I have a unidirectional relationship with Category and CategorySubsription. Where CategorySubscription has a many to one unidirectional relationship with Category. I want to grab a list of Categories c and left join CategorySubscription cs WITH cs.category_id = c.id AND cs.user_id = value.
Can I do this somehow?
Starting with Doctrine version 2.3 you can, as mentioned in a blog.
It's also mentioned in the docs if you know where to look. Scroll down to the last example under "15.2.4. DQL SELECT Examples":
Joins between entities without associations were not possible until version 2.4, where you can generate an arbitrary join with the following syntax:
<?php
$query = $em->createQuery('SELECT u FROM User u JOIN Blacklist b WITH u.email = b.email');
I know it says "not possible until version 2.4", but it definitely works starting with 2.3!
You can try using multiple from() methods and join conditions put in where() or andWhere methods()

Filter for elements using exists through a reverse foreign key relationship

A relevant image of my model is here: http://i.stack.imgur.com/xzsVU.png
I need to make a queryset that contains all cats who have an associated person with a role of "owner" and a name of "bob".
The sql for this would be shown below.
select * from cat where exists
(select 1 from person inner join role where
person.name="bob" and role.name="owner");
This problem can be solved in two sql queries with the following django filters.
people = Person.objects.filter(name="bob", role__name="owner")
ids = [p.id for p in people]
cats = Cat.objects.filter(id__in=ids)
My actual setup is more complex than this and is dealing with a large dataset. Is there a way to do this with one query? If it is impossible, what is the efficient alternative?
I'm pretty sure this is your query:
cats = Cat.objects.filter(person__name='bob', person__role__name='owner')
read here about look ups spanning relationships

Doctrine join filter

I know conditional filters aren't yet available for queries (as per "26.1.4. Applying Filter Rules to any Query" in the Known Limitations section of the Doctrine2 manual) , so I wanted to ask the experts what their preferred solution to the following problem is:
My site has product objects that each have many reviews. The reviews have a status field. I don't want to unnecessarily pull in reviews that haven't been approved during the automatic association that Doctrine2 does so wonderfully.
My current solution/hack is to use single table inheritance (STI) with a discriminator for "status" and have an ApprovedProductReview extending the ProductReview class based on a status of "APPROVED"
I should add that I am currently simply calling
$em->find('Entities\Product', $pid);
to get my product, and Doctrine2 does all the associations automatically. Should I instead be instantiating a product by providing a DQL query?
What I'd really like is a way to override the magic that Doctrine2 provides based on the annotations, and simply be able to use DQL to lazily get the correct subset of reviews.
Suggestions?
You can use the WITH statement in DQL:
$dql = "SELECT p, r
FROM Entities\Product p
LEFT JOIN p.reviews r WITH r.status = :status
WHERE p.id = :id"
$q = $em->createQuery($dql);
$q->setParameter('id', $id);
$q->setParameter('status', $status);
$product = $q->getSingleResult();