Как можно использовать Doctrine ODM для создания двунаправленной ссылки «один-к-одному», которая лениво загружается при использовании для ссылки поля, отличного от первичного ключа?
У меня есть две коллекции в MongoDB с документами Article и ArticleMetaData. Для каждого документа Article есть ArticleMetaData и наоборот. (Двунаправленное отношение OneToOne.) По унаследованным причинам два типа документов должны находиться в отдельных коллекциях. Обе коллекции обновляются внешними системами, которые не знают идентификаторов Mongo. Однако они содержат общее поле «групповой код», которое можно использовать для сопоставления нужной статьи с ее метаданными.
Я пытаюсь настроить Doctrine таким образом, чтобы я мог получить метаданные для объекта статьи и статьи из его объекта метаданных, но я хочу, чтобы они загружались лениво. (Нет необходимости запрашивать другой конец, когда он мне не нужен.)
Отображения выглядят следующим образом:
Foo\BarBundle\Document\Article:
repositoryClass: Foo\BarBundle\Repository\ArticleRepository
changeTrackingPolicy: DEFERRED_EXPLICIT
collection: article
type: document
fields:
id:
id: true
groupcode:
type: int
index: true
unique:
order: asc
...
referenceOne:
metaData:
targetDocument: Foo\BarBundle\Document\ArticleMetaData
mappedBy: groupcode
repositoryMethod: findOneByArticle
Foo\BarBundle\Document\ArticleMetaData:
repositoryClass: Foo\BarBundle\Repository\ArticleMetaDataRepository
changeTrackingPolicy: DEFERRED_EXPLICIT
collection: article_meta
fields:
id:
id: true
groupcode:
type: int
index: true
unique:
order: asc
...
referenceOne:
article:
targetDocument: Foo\BarBundle\Document\Article
mappedBy: groupcode
repositoryMethod: findOneByMetaData
И методы хранилища, упомянутые выше:
// In the ArticleRepository
public function findOneByMetaData(ArticleMetaData $metadata)
{
$article = $this
->createQueryBuilder()
->field('groupcode')->equals($metadata->getGroupcode())
->getQuery()
->getSingleResult();
$article->setMetaData($metadata);
return $article;
}
// In the ArticleMetaDataRepository
public function findOneByArticle(Article $article)
{
$metaData = $this
->createQueryBuilder()
->field('groupcode')->equals($article->getGroupcode())
->getQuery()
->getSingleResult();
$metaData->setArticle($article);
return $metaData;
}
Кажется, все работает довольно хорошо. Я могу запросить Article или ArticleMetaData и получить другую сторону, только проблема в том, что это не похоже на ленивую нагрузку. Когда я запрашиваю статью:
$article = $documentManager
->getRepository('FooBarBundle:Article')
->findOneBy(['groupcode' => 123]);
Много запросов выполняется:
doctrine.INFO: MongoDB query: {"find":true,"query":{"groupcode":123},"fields":[],"db":"development","collection":"article"}
doctrine.INFO: MongoDB query: {"limit":true,"limitNum":1,"query":{"groupcode":123},"fields":[]}
doctrine.INFO: MongoDB query: {"limit":true,"limitNum":null,"query":{"groupcode":123},"fields":[]}
doctrine.INFO: MongoDB query: {"find":true,"query":{"groupcode":123},"fields":[],"db":"development","collection":"article_meta"}
doctrine.INFO: MongoDB query: {"limit":true,"limitNum":1,"query":{"groupcode":123},"fields":[]}
doctrine.INFO: MongoDB query: {"find":true,"query":{"groupcode":123},"fields":[],"db":"development","collection":"article"}
doctrine.INFO: MongoDB query: {"limit":true,"limitNum":1,"query":{"groupcode":123},"fields":[]}
Что я делаю неправильно? Есть ли способ, которым я могу выполнить двунаправленную ссылку «один на один» с отложенной загрузкой, имеющую вышеуказанные ограничения?
Редактировать:
Прочитав ответ Роба Холмса, я удалил тест методов репозитория, который мог вызвать проблему. К сожалению, проблема все еще остается, и до сих пор выполняется 3 запроса, где достаточно одного (или не более двух).
Doctrine ODM уже будет лениво загружать ссылочный документ, а не предварительно выбирать его для вас.
Я считаю, что ваша проблема на самом деле заключается в ваших методах хранилища … Например, в findOneByMetaData
функционировать первое, что вы делаете, звонит $metadata->getArticle()
при этом вы просите доктрину загрузить статью из базы данных, которую из-за вашего хранилища будет вызывать метод findOneByMetaData
снова. Вот почему вы видите несколько запросов.
Ваш findOneByMetaData
функция должна выглядеть примерно так:
// In the ArticleRepository
public function findOneByMetaData(ArticleMetaData $metadata)
{
$article = $this->createQueryBuilder()
->field('groupcode')->equals($metadata->getGroupcode())
->getQuery()
->getSingleResult();
$article->setMetaData($metadata);
return $article;
}
Doctrine позаботится о том, была ли статья загружена, поэтому нет необходимости пытаться проверить нулевое значение. То же самое относится и к вашему findOneByArticle
функция тоже.
Надеюсь, что это имеет смысл, и поможет вам решить вашу проблему.
Это из-за Logger (loggableCursor), он дублирует запросы в файле журнала.
Например, вы вызываете … find () -> limit (1) -> getQuery (), он регистрирует каждый вызов, но на самом деле существует один запрос.
Больше информации: https://github.com/doctrine/mongodb-odm/issues/471#issuecomment-63999514
Проблема ODM: https://github.com/doctrine/mongodb/issues/151