Podobne artykuły na bazie wspólnych tagów w CakePHP 5

Jeśli chcesz pokazywać w blogu podobne artykuły, samo filtrowanie po tagach nie wystarczy. Trzeba jeszcze policzyć, ile tagów artykuły mają wspólnych, a potem posortować wyniki od najlepiej dopasowanych.

Problem z prostym matching()

Intuicyjnie jest zacząć od takiego zapytania:
$similarArticles = $this->Articles->find()
->matching('Tags', function ($q) use ($tagIds) {
return $q->where(['Tags.id IN' => $tagIds]);
})
->select(['id', 'title', 'slug'])
->where(['Articles.id !=' => $result->id])
->distinct()
->limit(3);
To działa tylko częściowo. Zapytanie zwraca artykuły, które mają przynajmniej jeden wspólny tag, ale nie rozróżnia, czy artykuł ma 1 wspólny tag, czy 5 wspólnych tagów.

Lepsza logika

Jeżeli chcesz najlepsze dopasowania, musisz:
  • znaleźć artykuły z przynajmniej jednym wspólnym tagiem,
  • policzyć liczbę wspólnych tagów dla każdego artykułu,
  • posortować wyniki malejąco po tej liczbie,
  • dopiero potem wziąć kilka pierwszych rekordów.
W praktyce oznacza to użycie groupBy() i COUNT().

Przykładowe zapytanie

Poniżej masz zapytanie, które możesz dopasować do swojej aplikacji:
$similarArticles = $this->Articles->find()
->select([
'Articles.id',
'Articles.title',
'Articles.slug',
'shared_tags' => $this->Articles->find()->func()->count('Tags.id')
])
->matching('Articles', function ($q) use ($tagIds) {
return $q->where(['Articles.id IN' => $tagIds]);
})
->where(['Articles.id !=' => $result->id])
->groupBy(['Articles.id', 'Articles.title', 'Articles.slug'])
->orderBy([
'shared_tags' => 'DESC',
'Articles.id' => 'DESC',
])
->limit(3);

Dlaczego to działa

matching('Articles') zawęża artykuły tylko do tych, które mają tagi z podanej listy. COUNT(Articles.id) liczy, ile takich dopasowań wystąpiło dla danego artykułu. Dzięki groupBy() każdy artykuł pojawia się tylko raz, a orderBy() ustawia ranking od najbardziej podobnych. Najważniejsza różnica jest taka: nie szukasz już tylko artykułów, które mają jakiś wspólny tag, ale artykułów, które mają najwięcej wspólnych tagów. To właśnie daje sensowną sekcję "podobne wpisy" i sprawia, że rekomendacje są dużo lepsze niż losowe wyniki.