0
votes

I've successfully implemented related posts in my site by using site.related_posts with the following Liquid templating:

The code bellow returns 4 posts related by tags on every post page, so far so good, but what's the best approach to achieve something like this for Jekyll collections?

{% assign hasSimilar = '' %}

{% for post in site.related_posts %}
 {% assign postHasSimilar = false %}
   {% for tag in post.tags %}
     {% for thisTag in page.tags %}
       {% if postHasSimilar == false and hasSimilar.size < 4 and post != page and tag == thisTag %}
       {% if hasSimilar.size == 0 %}
       {% endif %}

     <a href="{{ site.baseurl }}{{ post.url }}" class="entry">
      <div class="entry-media">
      <img src="{{ post.thumbnail | prepend: site.baseurl }}" alt="">
      <p class="post-cat">{% for category in post.categories.last %}{{ category }}{% endfor %}</p>
      </div>
      <h6 class="entry-title">{{ post.title | truncatewords: 70, '...' }}</h6>
      <p class="post-date">{{ post.date | date: "%d/%m/%Y" }}</p>{% if post.series %}(Series: {{ post.series }}){% endif %}
     </a>
{% capture hasSimilar %}{{ hasSimilar }}*{% endcapture %}
 {% assign postHasSimilar = true %}{% endif %}
{% endfor %}
   {% endfor %}
     {% endfor %}
       {% if hasSimilar.size > 0 %}

{% endif %}

As per Jekyll's documentation, when you create a collection it becomes available via the site Liquid variable, much like site.pages and site.posts, so in order to create a related collection items for my portfolio collection I replicated the Liquid logic above and assigned site.portfolio as posts, see:

{% assign posts = site.portfolio | sort: "tags" %}

So I set tags: [tag1, tag2, tag3] on every "project.md" document's front-matter within my _portfolio collectionas, as I'd normally do with posts and the liquid logic above returns the related portfolio collection items.

Anyway, although I'm not sure if this is the right path to achieve such functionality, it works as intended. I couldn't find any references/ usage for site.related_posts when it come to collections.

I'd really appreciate any thoughts regarding this issue. Thanks in advance.

1

1 Answers

2
votes

site.related_posts are the ten most recent posts.

So, your code is only looking for related in ten posts not all posts.

The following code will looks for tags related items for site.posts or any site.collection

{% assign pageTagsNumber = page.tags | size %}
{% assign related        = "" | split: "/" %}<!-- create empty array -->
{% assign relatedCount   = 0 %}
{% assign maxRelated     = 4 %}<!-- maximum number of related items -->
{% assign minTagMatch    = 4 %}<!-- minimum number of tag match to be a related -->
{% if minTagMatch > pageTagsNumber %}
    {% assign minTagMatch = pageTagsNumber %}
{% endif %}
{% assign matchedComplete = false %}<!-- flag -->

<!--
numberOfTag to match to be in the related list
Will try to match all page tags, then page tags size - 1, -2, until reaching minTagMatch
-->
{% for numberOfTag in (minTagMatch...pageTagsNumber) reversed %}

  <!-- Looping over site.posts or any site.mycollection -->
  <!-- here you can do {% for item in site.mycollection %} -->
  {% for item in site.posts %}
    <!-- !!! ITEM SPECIFIC -->
    <!-- for a collection change item.title == page.title if needed-->
    {% if related contains item or item.title == page.title %}
        <!--
        Don't scan an item that is already in related
        Don't scan an item if we are on his page
        -->
        {% continue %}<!-- go to next item -->
    {% endif %}
    {% assign itemTagsNumber = item.tags | size %}
    {% if itemTagsNumber < numberOfTag %}
      <!-- Not enough tags {{ itemTagsNumber }} -->
      {% continue %}<!-- go to next item -->
    {% endif %}
    {% assign matchingTags = 0 %}<!-- tag match counter -->
    {% for tag in page.tags %}
      <!-- Comparing page tag "{{ tag }}" to {{ item.tags | join: ", " }} -->
      {% if item.tags contains tag %}
        <!-- one matching tag increment matchingTags counter-->
        {% assign matchingTags = matchingTags | plus: 1 %}

        <!-- We have a match and {{ matchingTags }} matching tags total -->
        {% if matchingTags >= numberOfTag %}
            <!-- we have enough matching tag, this is a related item -->
            {% capture html %}
            <li><a href="{{ site.baseurl }}{{ item.url }}">
            {{ item.title }}
            {% assign pageTagsNumber = item.tags | size %}
             - has {{ pageTagsNumber }} tags
             - match on {{ matchingTags }} tags
            </a></li>
            {% endcapture %}
            {% assign related = related | push: html %}
            {% assign relatedCount = related | size %}
            {% if relatedCount >= maxRelated %}
                <!-- if we have all our related set the matchedComplete flag to true -->
                {% assign matchedComplete = true %}
            {% endif %}
            {% break %}
        {% endif %}
      {% endif %}
    {% endfor %}
    {% if matchedComplete == true %}
        <!-- matchedComplete breaking item loop! -->
        {% break %}
    {% endif %}
  {% endfor %}
  {% if matchedComplete == true %}
      <!-- matchedComplete breaking numberOfTag loop! -->
      {% break %}
  {% endif %}
{% endfor %}
{% if relatedCount > 0 %}

<!-- print related items -->
<ul>
  {% for item in related %}
    {{ item }}
  {% endfor %}
</ul>
{% endif %}