Is there a way to do WHERE NOT CONTAINS?

Hi,

My Jekyll posts have a category_list which contains a list of post categories.

I’m trying to filter posts which CONTAIN category A but NOT category B.

For example, I want to get posts in the tech category which do not contain games category.

{% assign posts = site.posts | where_exp:"item","item.category_list contains 'category A' and item.category_list not contains 'category B'" | sort: 'users' | reverse %}

This is obviously not working. I know I can do this using unless, but that means I have to change the filter to an if condition.

Is there a way to do this using filters only?

Can you negate in front instead of in the middle?

and not item.category_list contains ...

You can add brackets for readability

and not (item.category_list contains ...)

Welp, that didn’t work :frowning:

{% assign posts = site.posts | where_exp:"item","item.languages contains lang and not (item.category_list contains 'adult')" | sort: 'users' | reverse %}
Liquid Exception: Liquid syntax error (line 55): Expected end_of_string but found id in index.md
             Error: Liquid syntax error (line 55): Expected end_of_string but found id
             Error: Run jekyll build --trace for more information.

Tried with and without brackets. No joy.

It recommended running a trace on the issue. Did you do that? What did the trace say?

1 Like

I’m not in front of the computer to test this, but given you are still trying to make it work, I believe you can just add another where_exp. If I’m not mistaken, Liquid will process your filters from left to right. So instead of doing the AND, just do your first where_exp and then do the second.

Thanks for the tip, Bill, but it didn’t work. Adding the not gives an error:

Filter:

{% assign posts = site.posts | where_exp:"item","item.category_list not contains 'adult'" | where_exp:"item","item.category_list contains cat" | sort: 'users' | reverse %}
       Jekyll Feed: Generating feed for posts
  Liquid Exception: Liquid syntax error (line 113): Expected end_of_string but found id in index.md
             Error: Liquid syntax error (line 113): Expected end_of_string but found id
             Error: Run jekyll build --trace for more information.

Removing the not parses correctly, so I’m assuming the not keyword is causing the problem.

I’m currently just using an if to filter out the results, like this:

{% if post.category_list contains 'adult' %}
        {% continue %}
{% endif %}

… but I wish there was a neater solution.

I am back and did some research on this. There is no easy way to do what you are asking for. There is no not contains feature in Jekyll. You could do a != for exact matches, but that would mean you have to match a particular string, not an array of strings. Would it be possible for you to move the category_list elements to additional YAML data elements? Maybe you could more clearly articulate why you are doing this we can come up with a different solution?

So, I have posts which contain a set of categories under category_list.

For every category, I iterate over that list and display posts for that category. The code for this is here.

Additionally, when displaying posts for a category, I want to avoid posts which contain a particular category, for example “adult”. This is to avoid displaying 18+ posts. To achieve this, I’m currently using an if check as on Line 125.

The above code combining for and if is technically correct and working, but it would be a lot neater if I could combine both in a single expression.

Okay, I see the code and understand where you are coming from. Since adult content is a unique element to your site and [could potentially?] spread across any type of category, my best suggestion is to create a new YAML front matter element.

For example, your new YAML might look like this:

---
layout: instance
page: /
title: "0q0-xyz"
name: "0q0.xyz"
instance_url: "https://0q0.xyz"
short_description: "Pleroma: An efficient and flexible fediverse server"
description: "Pleroma: An efficient and flexible fediverse server"
admin: "blac_baqu@tuta.xyz"
admin_email: "blac_baqu@tuta.xyz"
statuses: 0
active_users: 0
users: 1
statuses_display: "0"
active_users_display: "0"
users_display: "1"
languages: en
category_list: 
prohibited_content: ""
thumbnail: "https://fed.0q0.xyz/instance/thumbnail.jpeg"
registrations: True
approval_required: False
version: "2.7.2 (compatible; Pleroma 2.4.1)"
created_at: 1639739176.0
adult: true
---

Notice the new adult: true at the bottom of the post elements.

Now your new code will look something like this:

{% assign filtered_posts = site.posts | where_exp: "item", "item.category_list contains 'Category A'" | where: "adult", "false" %}

or

{% assign filtered_posts = site.posts | where_exp: "item", "item.category_list contains 'Category A'" | where: "adult", "true" %}

I hope this helps.