пересечение и нумерация страниц в Eloquent ORM

Привет всем, у меня есть 3 таблицы:

таблица с именем content со следующими атрибутами:

id
name
table_type_id
release_date
popularity

другая таблица с именем content_genres со следующими атрибутами:

content_id
genres_id

другая таблица под названием genres со следующими атрибутами:

id
name

у каждого контента может быть несколько жанров, а у жанра может быть несколько контента (многие ко многим).


хорошо, пока вот определение разных таблиц, теперь я пытаюсь сделать запрос для поиска контента, который имеет, например, genre_id=1 и в то же время genre_id= 2

в postgresql это было бы легко:

 SELECT content.id
 FROM content INNER JOIN content_genres ON content.id =content_genres.content_id
 WHERE content_genres.`genres_id`= 1

 INTERSECT

 SELECT content.id
 FROM content INNER JOIN content_genres ON content.id =content_genres.content_id
 WHERE content_genres.`genres_id`= 2
 ;

Я делаю один запрос, я делаю другой запрос, а затем я делаю пересечение, получая контент с genre_id 1 и 2.


но когда я пытаюсь написать этот же запрос в красноречии, у меня возникают некоторые проблемы:

запрос 1:

$content1=$this->content::join('content_genres','content_genres.content_id','=','content.id')
        ->with('genres')
        ->where('content_genres.genres_id',1)
        ->where('content.table_type_id',1)
        //->whereYear('release_date',2017)
        ->select('content.id','content.name','content.popularity')
        ->orderBy('popularity','desc')->get();

запрос 2:

$content2=$this->content::join('content_genres','content_genres.content_id','=','content.id')
        ->with('genres')
        ->where('content_genres.genres_id',2)
        ->where('content.table_type_id',1)
        //->whereYear('release_date',2017)
        ->select('content.id','content.name','content.popularity')
        ->orderBy('popularity','desc')->get();

пересечение:

 $final_result=$content1->intersect($content2);

Хорошо, как мы видели, таким образом мы можем сделать пересечение, но у меня есть некоторые проблемы:

когда я хочу выполнить нумерацию страниц вручную, я не знаю, как мне подсчитать элементы, которые будут иметь пересечение, и после этого ограничить результаты пересечения.

пример:

количество результатов запроса1:

18950

количество результатов запроса2:

22650

количество результатов пересечения

3457

это очень медленно, потому что я не могу сказать, чтобы ограничить запрос 1 до 100 результатов, ограничить запрос 2 до 100 результатов, а затем сделать пересечение, я не могу этого сделать, потому что количество результатов от пересечения не будет всегда то же самое, по этой причине как я могу вручную разбить пересечение на страницы без загрузки всех результатов из query1 и query2, говоря, что я хочу разбить на страницы пересечения на страницах из 20 результатов?

Последняя вещь - большая проблема, с которой я столкнулся всю неделю.


реальный пример

заходишь на эту страницу, потом год ставишь ни одного, а в жанрах выбираешь два случайных жанра. как вы можете видеть, что нумерация страниц этого пересечения всегда 20, не зависит от того, есть ли на пересечении больше результатов или нет, всегда 20. И я почти уверен, что они не загружаются из БД все результаты, достижения.


Хороший результат:

Благодаря ответу правильный способ сделать это ниже:

 $this->content::join('content_genres as g1','g1.content_id','=','content.id')
->join('content_genres as g2','g2.content_id','=','content.id')
->where('g1.genres_id', 1)
->where('g2.genres_id', 2)

это работает для меня, я мог бы выбрать другой вариант, но у меня есть отношение многие ко многим, потому что мой content_genres является сводной таблицей, но я думаю, что я также был бы действителен.


person Ratchet    schedule 06.03.2018    source источник


Ответы (1)


Вы должны объединить оба запроса. Я вижу два способа сделать это.

1) Присоединитесь к content_genres дважды:

$this->content::join('content_genres as g1','g1.content_id','=','content.id')
    ->join('content_genres as g2','g2.content_id','=','content.id')
    ->where('g1.genres_id', 1)
    ->where('g2.genres_id', 2)

2) Используйте whereHas():

$this->content::whereHas('content_genres', function($query) {
    $query->where('genres_id', 1)
})->whereHas('content_genres', function($query) {
    $query->where('genres_id', 2)
})

Для этого требуется отношение: контент → HasMany → контент_жанры.

person Jonas Staudenmeir    schedule 06.03.2018
comment
это работает, спасибо, чувак, но у меня есть один вопрос, эффективно ли делать два соединения? потому что, возможно, два соединения допустимы, но что произойдет с точки зрения производительности, если пользователь решит отфильтровать около 3 или 4 жанров? Я могу сделать цикл и присоединиться все время, которое потребуется, но я не знаю, будет ли это допустимым вариантом? - person Ratchet; 07.03.2018
comment
Соединения должны быть более эффективными, чем подзапросы (stackoverflow.com/q/13063772/4848587). Я думаю, что несколько объединений не должны быть проблемой производительности, если вы добавите необходимые индексы. Обязательно проанализируйте свой запрос с помощью EXPLAIN. - person Jonas Staudenmeir; 07.03.2018