Any ideas on how to do sort posts by the date it was published, but use a front matter property if that’s populated instead?
Some of our blog posts get updated occasionally and become relevant again. These posts should be appear on top with the newest posts. We don’t want to pretend that the post was only recently published, so I added a updated front matter field for the date on which it was last updated.
Now I’d like to sort the posts chronologically by that updated value, but use the publish date instead if the post hasn’t been updated after publishing. Simply sorting by published and then by updated doesn’t work—that would put all updated posts before all other posts that haven’t been changed, even if they haven been published after all the updated posts have had a change.
Of course I could populate the updated field with the published value for all posts, but that would require editors to change both fields if they don’t want to use the default value (‘now’), which is prone to mistakes.
Ah I have an idea: instead of adding an updated field, I could add an originally_published field. Then I can simply change the publish date upon updates, sort by that and use the originally_published date if available.
I am trying to understand your question here. Jekyll will always sort your list of posts descending, by the date field, so the latest post shows first.
Each post has front matter, like this:
---
title: My post
date: 12-01-01
layout: post
---
This is my blog post...
The following code will always sort the list of posts descending using that date field, so the last post is the latest:
{% for post in site.posts %}
<p><a href="{{ post.url }}">{{ post.title }}</a>
{% endfor %}
If you want to sort by some other field, then absolutely, you can use the front matter as you propose. Here is what that might look like using your example:
---
title: My post
date: 12-01-21
last_updated: 12-08-21
layout: post
---
This is my blog post...
And you would sort this way:
{% assign posts = site.posts | sort: 'last_updated' | reverse %}
{% for post in posts %}
<p><a href="{{ post.url }}">{{ post.title }}</a>
{% endfor %}
Of course if you can remove the reverse option to sort ascending.
Ah sorry, I see I did a poor job explaining the challenge! Here’s what I had in mind:
All posts have a date, indicating when they were first published
The archive is sorted chronologically, with the most relevant posts at the top. Right now relevance is defined by recency: the newest posts appear first.
Some posts get changes. Only these posts have a updated front matter property with the date of the last update as a value.
Posts should be sorted by their updated value, but since that value isn’t available for all posts, the date value should be used as a fallback.
This should result in posts updated today appearing at the top of the archive and posts updated last month appearing among the posts published last month.
That said, I realized that updating a post doesn’t always make them relevant again (f.e. when we correct a mistake). So instead I added another front matter field: pin. Posts with pin: true appear before all other posts. The code for that is simple:
{% assign groups = site[page.collection] | where_exp: "item", "item.path contains '/posts/'" | reverse | group_by: 'pin' | reverse %}
{% for group in groups %}
{% for post in group.items %}
{% include post_preview.liquid post = post %}
{% endfor %}
{% endfor %}
(We use collections to manage content in different languages, so site[page.collection] | where_exp: "item", "item.path contains '/posts/'" just means all posts on the site in the archive page’s language.)
I am thinking you are getting pretty advanced with this. What are your thoughts on performing multiple queries? You can create them as part of the business logic in an include page so you do not have to write it out all the time. I think it will also help reduce complexity.