Liquid `where` and `reject` Filters Not Working as Expected
The where and reject filters in Shopify Liquid are not functioning correctly. When used to filter collections based on properties like tags, they return incorrect results—often an empty array ([]) or null, even when matching products exist.
This issue directly impacted a Scheduled Drop, preventing products from displaying as expected. This led to a loss of visibility, sales, and customer engagement, which is critical for merchants who rely on Shopify for product launches.
There were no changes to the shop theme code in the past 4 weeks, yet due to this bug, the products did not appear as intended, revealing the issue.
Steps to Reproduce
Test Case 1: where Not Filtering Properly
{% assign test_array_filter = section.settings.collection.products | where: "tags", "contains", "foo" %}
<script>
document.addEventListener("DOMContentLoaded", () => {
console.log("Test WHERE filter:", {{ test_array_filter | json }});
});
</script>
Expected: Returns an array of products with "foo" tag. Actual: Returns null or an empty array ([]).
Test Case 2: reject Not Filtering Properly
{% assign products_to_display = section.settings.collection.products | reject: "tags", "contains", "bar" %}
<script>
document.addEventListener("DOMContentLoaded", () => {
console.log("Test REJECT filter:", {{ products_to_display | json }});
});
</script>
Video: https://www.loom.com/share/135d72b5c61d4441a86cd2da1aafa4c2
Expected: Returns all products excluding those tagged "bar". Actual: Returns null or an empty array ([]).
The syntax you are using is not valid. Both reject and where only supports two parameters (the field to filter and the value) while you are using an extra one.
You won’t be able to use those filters for filtering tags here, you will need to do a for loop to be able to use the contains operator.
Ran into a valid case for this today in a Shopify context. I think reject and where should consider supporting contains.
A plus client needs to filter out specific product media by country because of legal requirements in their industry. Markets don't support separate product media per market unless the products are duplicated. That won't work with their business requirements.
Images do no have arbitrary properties like metafields, so alt text and src are the only text properties that can be controlled by the merchant. Either one could contain a country code but neither can equal the country code.
Product media is an array of objects so I cant loop it and reconstruct it the way I could build an array of strings. Assuming I don't want to get creative with objects and arrays I am out of luck.
The best version of this code would be this: {% assign hide_me = request.country | downcase | prepend: hidden- %} {% assign product_media = product_media | reject: 'src', contains: hide_me %}
Instead, we end up with a great deal of logic repeated anywhere product media is looped or the product media array size is used as an index.