Advice for filtering collection with routes

Hey there!

I’m not exactly new to Jekyll, but never had to work on this sort of desired structure. Apologies if I’m missing some obvious functionality.

If there is a way to achieve the following structure, how would you go about it in the cleanest, best practices kind of way.

Let’s say I have a large collection of places. Each of the place has two categories assigned, type and location.

For example:
title: Cool Brewery
type: Bar
location: West

I’d like to filter using routes, so:
/places/bars - page outputs all of the bars
/places/bars/west - page outputs all of the bars within west

and so on
/places/restaurants/east
/places/bars/south

Any advice or example is much appreciated.
Thanks!

If you don’t need to actually have an page for each bar, then you could store all bar data in frontmatter of the bar/index.md, or a collection or of a YAML data file, then iterate over that. No need to iterate over paths because you could get only bars.

e.g. with frontmatter for bars/index.md

---
bar_info:
  - title: A
    description: ABC
  - title: B
    description: DEF
---

Add a for loop here over this array
{{ page.bar_info }}

But in case you want a page for each bar, then see my next answer.

1 Like

Let’s say you have directory setup like this

places/bars/
  index.md  # menu listing of all bars
  abc.md
  def.md

Each page in Jekyll has the following attributes - you can use either for route.

{{ item.url }}
{{ item.dir }}

The url is browser path.

e.g. /places/bars/ and /places/bars/abc/
assuming you have pretty permalink.

Or /places/bars/index.html which is less useful for you and you’d have to replace the filename.

And dir is directory of the markdown file.

e.g. places/bars/ - the same for both the index.md page and the other pages. This is a good one for you. More on that below.


Since the code will be for places/bars/index.md then using

{{ page.dir }}

Will evaluate to this (maybe a trailing slash?)

places/bars

So then that index.md must have

So then you could list all bars in the bar directory.

---
title: Bars
--- 

<ul>
{% for p in site.pages %}
  {% if p.dir == page.dir and p.name != 'index.md' %}
    <li>
      {{ p.title }} - {{ p.type }} - {{ p.location }}
    </li>
{% endif %}
{% endfor %}
</ul>

You could also add a link to click to the page with a href="{{ p.url | relative_url }}:

There I check equality that {{ item.dir == page.dor }} exactlyn- only at the exact same level and not in west directory. Which means you might get zero results if the bar pages are not in bars directly but in bars/west etc.

In that case you can do a contains match.

{% if p.dir contains page.dir and p.name != 'index.md' %}

Note the space before and after `contains.

You might also find a builtin Ruby method something like

{% if p.dir.startswith(page.dir) and p != page %}

Either approach will match both bars/abc.md and bars/west/xyz.md

And then repeat that for loop on places/bars/west/index.md to match all the pages in the west directory only.

When you have that working, you can turn it into an includes snippet or layout so it is reusable on your bar pages and location listing.

Good luck.

1 Like

The two most popular ways to accomplish this are with Jekyll Collections or Jekyll Data. I will cover both in this response.

The Jekyll Collections approach

Here, you create individual files for each of the breweries, and then you can show them one at a time (like a page or a post), or you can iterate through them to create a list.

In your _config.yml file, add the line (anywhere):

collections:
  breweries:

Note: If Jekyll is running, even with livereload, you will need to stop and start again for the change to take effect.

Next, create a folder at the root level and name it _breweries. That folder corresponds to the collection you added to the _config.yml file.

Inside that folder, create as many breweries as you like. Every markdown file represents a brewery. You can add whatever YAML front matter you like.

file: anchor-steam.md

---
title: Anchor Steam Brewery
type: brewery
sub-type: na
location: San Francisco
---
## Famous brewery with a tasting room
Learn the traditions of this famous brewery...

File: magnolia-brewery

---
title: Magnolia Brewery
type: brewery
sub-type: restaurant
location: San Francisco
---
## Come for the beer stay for the fries
Tucked away in a corner in the historic Haight Ashbury district there lies a gem of a brewery and restaurant....

All the files you create can have whatever content you like, including pictures, links, videos, etc. It is like creating a single web page or podcast post for each one.

Now create a folder from the root and title it breweries (or whatever you want to call it).

Create two files in the breweries folder:

all.html
restaurants

File: all.html

---
title: All Breweries
layout: default
---

{% for brewery in site.breweries %}
    <h1>Name: {{ brewery.title }}</h1>
{% endfor %}

File: restaurants.html

---
title: Restaurants
layout: default
---

{%- assign breweries = site.breweries| where: "sub-type", "restaurant" -%}
{% for brewery in breweries %}
    <h1>Name: {{ brewery.title }}</h1>
{% endfor %}

Now, you can go to your Jekyll site and go to mysite/breweries/all or mysite/breweries/restaurants and the breweries are filtered as you like.

Of course, I only display the title of the brewery, but you can display whatever metadata or content you want to display.

If you want to learn more about Collections, check out the official documentation:

The Jekyll Data approach

Here, you do not have to modify the _config.yml file and you create a file containing a dataset of breweries. Jekyll can work with CSV, JSON, YML, and XML. I’m going to use YAML in this example.

Create a new folder at the root of your site and call it _data (this is a built-in folder name Jekyll uses so you can add data files).

In that new folder, create a file and call it breweries.yml.

Edit that file and add the following data:

items:
    - title: Anchor Steam Brewery
      type: brewery
      sub-type: na
      location: San Francisco
      Description: "Learn the traditions of this famous brewery..."
    - title: Magnolia Brewery
      type: restaurant
      sub-type: restaurant
      location: San Francisco
      description: "Tucked away in a corner in the historic Haight Ashbury district there lies a gem of a brewery and restaurant...."

Now, in the root of your site, create a new folder and call it brewery-data (or whatever name you want).

In the brewery-data folder, create two files:

all.html
restaurants.html

File: all.html

---
title: All Breweries (from data)
layout: default
---

{% for brewery in site.data.breweries.items %}
    <h1>Name: {{ brewery.title }}</h1>
{% endfor %}

File: restaurants.html

---
title: Restaurants (from data)
layout: default
---

{%- assign breweries = site.data.breweries.items| where: "sub-type", "restaurant" -%}
{% for brewery in breweries %}
    <h1>Name: {{ brewery.title }}</h1>
{% endfor %}

To test this, you can go open your Jekyll site in the browser and go to mysite/brewery-data/all or mysite/brewery-data/restaurants.

Once again, my example just displays the name of the brewery, but you can add any other data elements you want.

Conclusion

Both methods work great, but it will depend on how you want to manage things. If each place has its own unique approach to how you want to describe it, then the collections approach is probably better.

The data approach is really easy and accessible, but you have to know how to work with data. Also, what happens when you want to add images and manage the content for the site? Working with data becomes unwieldy once you think of every data element you want to put in there. You may also find it is harder to do grammar and spell-checking when using data.

If you want to go the data route, I suggest you watch the following video and the following additional videos that explain Jekyll data and teaches you how to use it:

3 Likes

Yes collections and data both work for storing and displaying data.

For collections, if you set up the config with output: true then each time in the collection will get its own page. The data approach doesn’t do this, unless you use a generator plugin which is not worth it here.

For collections, having subtype is an interesting approach because then all bars can be in the same directory and they are easy to iterate over. You even prefix names as west-abc.md and east-def.md to sort them easily for editing and you could extract the location from item.name even.

A downside of the collection approach compared to my routing answer is that collections let you group by folder. If you make a collection called bars, then items in bars/west and bars/east are all seen as in a single bars collection.
However, you could make a collection for east, west, etc. The hassle is remembering to add the name to config file when creating it (if you have more than 4 locations and these grow). Plus you lose the nesting approach. In order to get all bars, you have to iterate over west collection and then east collection etc.

So if you if you do go for my suggested routing approach, you could use pages or collections even. I don’t see an advantage with using collections as pages and collection items can both store frontmatter.

Maybe collections would be useful if you want to treat bars as a collection and using url routing within that and then restaurants as another collection. But it would need more specific code for each collection, while my code generalizes well to use the same layout for any page at any level (even if you go 4 levels deep as places, bars, west, irish). without having to hardcode using site.bars in the for loop.

To demonstrate, I have easy path-based routing without collections and using multiple levels of nesting down. If I add another new-dir/index.md page at any level, it automatically becomes part of the visible menu. No need to add a new collection in the config.


Another thing to note on collections is that the bars listing page should not exist in the bars collection. Your structure would be

_bars/
    abc.md
    def.md
bars.md # permalink: /bars/

And the weakness here is that you need to tell the bars.html page that it belongs to the bars collection structure. While my approach puts bars.md as index.md inside bars directory.

1 Like

All I’m saying is we have some great breweries and brewery bars here in San Francisco :slight_smile:

We have cool names here in Cape Town.

Darling Brewery. Hoghouse Brewing Company. Jack Black Brewing Company. Devils Peak Brewery. Woodstock Brewery, Hey Joe Brewery.

Much more interesting!

I really appreciate such detailed responses.

What @MichaelCurrin suggested is something I thought about. Going the manual way and not using collections at all, just nesting pages. Just like on your Dev Cheatsheets project.

But I can’t help myself, it doesn’t seem like the right way, does it? :slight_smile:
I’m trying to think of potential downsides, for ex. working with some kind of CMS, all those pages would not render too nicely in those, most likely…

Anyhow, appreciate the input once again!