Отвечая на мой вопрос ...
Я на самом деле удивлен, насколько это сложно. Все решения, которые включают запросы к Книгам и каждой Книге, имеющей свой список верхних и нижних Обзоров, приводят к N запросам на Книгу в некоторых базах данных, и для моего в частности (MySql) требуется 3N. Конечно, кто-то может найти умный способ обойти это, но моя игра с кодом, сырым SQL, исследование того, что сделали другие, ограничения базы данных и т. Д., Кажется, всегда возвращается к этому.
Итак ... В итоге я решил проблему. Вместо того, чтобы вычислять верхние и нижние Обзоры всякий раз, когда я запрашиваю Книги (что является очень частым запросом - для справки. Справочные книги и обзоры приведены в качестве примера - фактический домен отличается), я вычисляю верхнее / нижнее при каждой отправке нового Обзора. Это изменяет отправку рецензии из одного «запроса» в три запроса, но отправка рецензии происходит довольно редко по сравнению с запросами для книг.
Для этого я добавил два новых свойства в Book, topReviews и bottomReviews и отобразил их как списки один-ко-многим в Review. Затем я обновил код, который сохраняет новые Обзоры, чтобы запросить верхний и нижний Обзоры для рассматриваемой Книги (2 запроса), задает свойства верхнего / нижнего Обзоров для Книги (перезаписывая предыдущий) и сохраняет Книгу. Любые запросы Book теперь возвращают эти «кэшированные» верхние и нижние отзывы. У меня все еще есть ленивое свойство "обзоры", в котором будут все отзывы (не только сверху / снизу), если нужно - еще одно сопоставление один ко многим.
Я открыт для других рекомендаций. Большинство ответов здесь находятся в Ball Park, но пропускают проблему или не будут работать из-за ограничений базы данных или требуют слишком много запросов.