No lazy loading of references which has the inversedBy and mappedBy option #180

Closed
tecbot opened this Issue Nov 1, 2011 · 9 comments

Projects

None yet

6 participants

Contributor
tecbot commented Nov 1, 2011

Currently all references which was configured with the inversedBy and mappedBy option is not lazy loading. The generated hydrator class loads all references on hydration.

Owner
jwage commented Feb 7, 2012

I think it is only @ReferenceOne with mappedBy is the reference eagerly loaded in hydration. It is not possible to put a proxy in place or do it lazily since on the inverse side we don't have a foreign key id value to store and use in a proxy/reference.

@jwage jwage closed this Feb 7, 2012
madarco commented Dec 17, 2012

Sorry if I comment on this closed issue,
but it should be clearly stated in the documentation if it isn't going to be fixed, because it can easily generate the infamous n+1 problem.
Also because we can't ->prime(true) that references.

Owner
jmikola commented Jan 17, 2013

Re-opening for future discussion on documentation changes.

@jmikola jmikola reopened this Jan 17, 2013
madarco commented Jan 17, 2013

If it could help, this is my use case:

I've a Document User that is referenced in multiple other Documents: SalesInfo, Profile, Billing, ecc

-- Simplicity in the templates:
Often in the code, given a User instance, we need to access those documents with the getters, either for simplicity in the templates, but also because it is difficult to get those in the Symfony Sonata Admin:

If we create an Admin class for the User doc, we will have only a collection of User, and it is difficult to get the other documents in the twig templates.

-- Normalized schema:
Also, I don't want to put the reference ids of each of those documents in my User Document, since doing this would require to do an update on the User Collection every time we add/delete a Profile or Billing document.

So I've used extensively the mappedBy attribute, but I've found too late that in this way, every time I get an User document, doctrine fetch all the associated Documents. In some pages it do 15* 4 queries.

It would be really usefull to lazily load the mappedBy documents:

-- Possible solution:
In the hydrator we should put a Proxy in the mappedBy fields,
the only difference is that this proxy should not raise an exception when the _load fails, since we don't know afterward if exists a document in the referred collection.

I think that if the Proxy implements the Countable interface, we can check for existence of the referred Document like usually:
if($user->getProfile()) { ... }

(as with the PersistentCollection)

Owner
jwage commented Jan 18, 2013

I think this feature is impossible to implement. We can't implement proxy and lazy loading for something if we do not know the ID ahead of time, since all objects hydrated must be registered in the identity map. If we allowed this then it would be possible to have > 1 objects with the same ID floating around in PHP. Those types of inverse references must be eagerly loaded.

madarco commented Jan 18, 2013

You are right jwage,
I tried to implement this by myself but I've encountered an error right when I tried to register the proxy (the Proxy support an array for identity out of the box, but obviously then it can't be registered in the identity map).

The only practical solution that I see for my problem is to replace every ReferenceOne(mappedBy...) with a RefrenceMany

In this way in the hydrator of my User doc, Doctrine put a PersistentCollection that actually do the lazy loading. However this is not so nice, since now I have an array instead of the referred doc.

What I was suggesting was like a PersistentCollection of only 1 document... kinda ugly I know.

I think this drawback should be stated in the documentation, because the mappedBy seems good at first (better, normalized schema without references to maintain), but the non-lazy loading render it impractical.

Owner
jwage commented Jan 18, 2013

You can map a ReferenceMany then just have a getter that returns the first element. I've actually done this myself and it works well.

Owner
jwage commented Jan 18, 2013

Agreed, the documentation should be updated. That is a common mistake people coming from relational databases will make. In a RDBMS that mindset makes sense but not in mongodb.

madarco commented Jan 18, 2013

Yes I've tried this with the ReferenceMany, although it seems an hack.

Each method has his own disadvantages:

ReferenceOne(mappedBy)

  • requires an index on the invertedBy field
  • no lazy loading

RefrenceOne

  • requires a cascade update to the parent doc when the reference is created/deleted

Embedded

  • the data is always loaded, but without the n+1 problem

but the mappedBy hardly make sense without the lazy loading.

Thanks.

@alcaeus alcaeus added the has PR label Dec 18, 2015
@malarzm malarzm closed this in 7b5d4e8 Feb 20, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment