Auto-tag products by age, with Mechanic.

Mechanic is a development and ecommerce automation platform for Shopify. :)

Auto-tag products by age

This task scans every product in your store, and adds or removes the tags you configure, according to how many days have passed since the product was created. (Or published, if you prefer!)

Runs Occurs when a user manually triggers the task and Occurs when a bulk operation is completed. Configuration includes product tags and age threshold in days, match product tags by minimum age, match product tags by maximum age, use product publish times instead of creation times, run daily, and run hourly.

15-day free trial – unlimited tasks

Documentation

This task scans every product in your store, and adds or removes the tags you configure, according to how many days have passed since the product was created. (Or published, if you prefer!)

Enable "Use product publish times instead of creation times" to have this task look at when the product was published to your online storefront, instead of when the product was created. When this option is enabled, unpublished products will have any of this task's tags removed.

Configure "Product tags and age threshold in days" with product tags on the left, and the product age (in days) to qualify on the right. Combine this setting with a choice between "Match product tags by minimum age" and "Match product tags by maximum age", to achieve tagging based on either minimum or maximum ages.

For example, a product tag of "new-5" with an age threshold of "5", combined with "Match product tags by maximum age" will be added to all products that were created/published within the last 5 days. Once a product ages beyond that threshold, running this task will remove that tag.

Run this task manually to scan your store's products on demand, or configure the task to run daily or hourly.

Developer details

Mechanic is designed to benefit everybody: merchants, customers, developers, agencies, Shopifolks, everybody.

That’s why we make it easy to configure automation without code, why we make it easy to tweak the underlying code once tasks are installed, and why we publish it all here for everyone to learn from.

(By the way, have you seen our documentation? Have you joined the Slack community?)

Open source
View on GitHub to contribute to this task
Subscriptions
mechanic/user/trigger
{% if options.run_daily__boolean %}mechanic/scheduler/daily{% endif %}
{% if options.run_hourly__boolean %}mechanic/scheduler/hourly{% endif %}
mechanic/shopify/bulk_operation
Tasks use subscriptions to sign up for specific kinds of events. Learn more
Options
product tags and age threshold in days (keyval, number, required), match product tags by minimum age (boolean), match product tags by maximum age (boolean), use product publish times instead of creation times (boolean), run daily (boolean), run hourly (boolean)
Code
{% comment %}
  Preferred option order:

  {{ options.product_tags_and_age_threshold_in_days__keyval_number_required }}
  {{ options.match_product_tags_by_minimum_age__boolean }}
  {{ options.match_product_tags_by_maximum_age__boolean }}
  {{ options.use_product_publish_times_instead_of_creation_times__boolean }}
{% endcomment %}

{% if options.match_product_tags_by_minimum_age__boolean and options.match_product_tags_by_maximum_age__boolean %}
  {% error "Please choose either 'Match product tags by minimum age' or 'Match product tags by maximum age', but not both." %}
{% elsif options.match_product_tags_by_minimum_age__boolean == false and options.match_product_tags_by_maximum_age__boolean == false %}
  {% error "Please choose either 'Match product tags by minimum age' or 'Match product tags by maximum age', but not both." %}
{% endif %}

{% if event.topic == "mechanic/user/trigger" or event.topic contains "mechanic/scheduler/" %}
  {% capture bulk_operation_query %}
    query {
      products {
        edges {
          node {
            id
            createdAt
            publishedAt
            tags
          }
        }
      }
    }
  {% endcapture %}

  {% action "shopify" %}
    mutation {
      bulkOperationRunQuery(
        query: {{ bulk_operation_query | json }}
      ) {
        bulkOperation {
          id
          status
        }
        userErrors {
          field
          message
        }
      }
    }
  {% endaction %}

{% elsif event.topic == "mechanic/shopify/bulk_operation" %}
  {% assign now_s = "now" | date: "%s" | times: 1 %}

  {% if event.preview %}
    {% assign preview_interval_s = options.product_tags_and_age_threshold_in_days__keyval_number_required.first.last | times: 24 | times: 60 | times: 60 %}

    {% if options.match_product_tags_by_minimum_age__boolean %}
      {% assign preview_time = now_s | minus: preview_interval_s | minus: 86400 | date: "%Y-%m-%d" %}
    {% elsif options.match_product_tags_by_maximum_age__boolean %}
      {% assign preview_time = now_s | minus: preview_interval_s | plus: 86400 | date: "%Y-%m-%d" %}
    {% endif %}

    {% capture bulkOperation_objects_jsonl %}
      {"id":"gid:\/\/shopify\/Product\/1234567890","createdAt":{{ preview_time | json }},"publishedAt":{{ preview_time | json }},"tags":[]}
    {% endcapture %}

    {% assign bulkOperation = hash %}
    {% assign bulkOperation["objects"] = bulkOperation_objects_jsonl | parse_jsonl %}
  {% endif %}

  {% for product in bulkOperation.objects %}
    {% assign product_timestamp_s = product.createdAt | date: "%s" | times: 1 %}
    {% if options.use_product_publish_times_instead_of_creation_times__boolean %}
      {% assign product_timestamp_s = product.publishedAt | date: "%s" | times: 1 %}
    {% endif %}

    {% assign product_age_d = now_s | minus: product_timestamp_s | divided_by: 86400 %}

    {% assign tags_to_add = array %}
    {% assign tags_to_remove = array %}

    {% for keyval in options.product_tags_and_age_threshold_in_days__keyval_number_required %}
      {% assign tag = keyval[0] %}
      {% assign tag_age_threshold_d = keyval[1] %}

      {% assign tag_age_threshold_met = false %}

      {% comment %}
        0 happens when we didn't have a valid datetime string to begin with. Happens
        when there's no published_at value.
      {% endcomment %}

      {% if product_timestamp_s != 0 %}
        {% if options.match_product_tags_by_minimum_age__boolean and product_age_d > tag_age_threshold_d %}
          {% assign tag_age_threshold_met = true %}
        {% elsif options.match_product_tags_by_maximum_age__boolean and product_age_d < tag_age_threshold_d %}
          {% assign tag_age_threshold_met = true %}
        {% endif %}
      {% endif %}

      {% comment %}
        {% log product_id: product.id, product_age_d: product_age_d, tag: tag, tag_age_threshold_d: tag_age_threshold_d, tag_age_threshold_met: tag_age_threshold_met %}
      {% endcomment %}

      {% if tag_age_threshold_met %}
        {% unless product.tags contains tag %}
          {% assign tags_to_add[tags_to_add.size] = tag %}
        {% endunless %}
      {% else %}
        {% if product.tags contains tag %}
          {% assign tags_to_remove[tags_to_remove.size] = tag %}
        {% endif %}
      {% endif %}
    {% endfor %}

    {% if tags_to_add != empty or tags_to_remove != empty %}
      {% action "shopify" %}
        mutation {
          {% if tags_to_add != empty %}
            tagsAdd(
              id: {{ product.id | json }}
              tags: {{ tags_to_add | json }}
            ) {
              userErrors {
                field
                message
              }
            }
          {% endif %}

          {% if tags_to_remove != empty %}
            tagsRemove(
              id: {{ product.id | json }}
              tags: {{ tags_to_remove | json }}
            ) {
              userErrors {
                field
                message
              }
            }
          {% endif %}
        }
      {% endaction %}
    {% endif %}
  {% endfor %}
{% endif %}
Task code is written in Mechanic Liquid, an extension of open-source Liquid enhanced for automation. Learn more
Defaults
Product tags and age threshold in days
{"new-this-week"=>"7", "new-this-month"=>"30"}
Match product tags by maximum age
true
Use product publish times instead of creation times
true