Product search

Search and filter Experience products, returning one result per product.

The product_search tag searches a creator's Experience products and returns one result per product.

This is useful for building search and listing pages where each product appears once, regardless of how many departure dates it has. To return a separate result for each departure date, use the experience_slot_search tag instead.

The search only returns public, published products. Products are excluded if:

  • The Experience is unpublished.
  • The Experience has been marked as private in Manage product availability.

Only one product_search tag should be used per page to ensure expected results.

Basic usage

{% product_search
  name: "Beyond",
  duration: { greater_than: 3, less_than: 8 },
  page_size: 12,
  sort: "departure_date_asc"
%}
  {% for item in result.items %}
    <p>{{ item.name }}</p>
  {% endfor %}

  <div>
    {{ paginate.collection_size }} results
    {{ paginate | default_pagination }}
  </div>
{% endproduct_search %}

The tag makes the following objects available:

ObjectDescription
result.itemsArray of matching Products.
paginatePaginate object for paginated results.
available_monthsArray of Date objects representing months that contain available departures.

The Search object is also available when the page URL contains ?search[...] query parameters. It reflects the applied URL parameters and is useful for preserving filter state in forms.

Using query parameters

Search parameters can be passed in the URL using the search query parameter:

/search?search[name]=beyond&search[departure_date][greater_than]=2024-11-22&search[sort]=departure_date_asc

Query parameters override any matching attributes passed directly to the tag. This allows you to set defaults in the tag and let URL parameters refine the search.

page_size cannot be set via URL — it is a tag-only attribute.

Parameters

ParameterTypeDescription
nameStringPartial text search on product name (case-insensitive).
categoryString or arrayFilter by product category (case-sensitive).
subcategoryString or arrayFilter by product subcategory (case-sensitive).
cityString or arrayFilter by location city (case-insensitive).
countryString or arrayFilter by country code or name.
departure_dateRange hashFilter by departure date.
departure_monthInteger or stringFilter by month across all years.
durationRange hashFilter by experience duration.
product_idUUID or arrayRestrict to specific experience(s).
series_idUUIDFilter by series.
exclude_sold_out_productsBooleanExclude sold-out products.
active_promotionBooleanFilter by promotion status.
include_organisation_productsBooleanSearch across the organisation.
sortStringSort order. Default: "departure_date_asc".
page_sizeIntegerResults per page. Default: 10. Tag only.
Tag categoriesURL onlyFilter by company-defined tag categories.

name

Partial text search on the experience name. Case-insensitive.

{% product_search name: "my experience" %}
{% endproduct_search %}

URL: ?search[name]=my+experience

category

Filter by experience category. Case-sensitive. Accepts a single value or an array.

{% product_search category: "Active" %}
{% endproduct_search %}

{% product_search category: ["Active", "Adventure"] %}
{% endproduct_search %}

URL: ?search[category]=Active

subcategory

Filter by experience subcategory. Case-sensitive. Accepts a single value or an array.

{% product_search subcategory: "Wellness" %}
{% endproduct_search %}

{% product_search subcategory: ["4 Star", "5 Star"] %}
{% endproduct_search %}

URL: ?search[subcategory]=Wellness

city

Filter by experience location city. Case-insensitive. Accepts a single value or an array. Products without an address are excluded.

{% product_search city: "London" %}
{% endproduct_search %}

{% product_search city: ["London", "Paris"] %}
{% endproduct_search %}

URL: ?search[city]=London or ?search[city][]=London&search[city][]=Paris

country

Filter by country. Accepts ISO 3166 alpha-2 codes (e.g. "FR") or country names (e.g. "France"). Accepts a single value or an array.

{% product_search country: "FR" %}
{% endproduct_search %}

{% product_search country: ["FR", "DE"] %}
{% endproduct_search %}

URL: ?search[country]=DE

departure_date

Filter by departure date using a range hash. Date values should be in YYYY-MM-DD format. A product matches if any of its slots fall within the specified range.

{% product_search departure_date: { greater_than: "2024-06-01", less_than: "2024-07-01" } %}
{% endproduct_search %}

Liquid variables can be used for date values:

{% assign latest_date = selected_date %}

{% product_search departure_date: { less_than: latest_date } %}
{% endproduct_search %}

URL: ?search[departure_date][greater_than]=2024-06-01&search[departure_date][less_than]=2024-07-01

departure_month

Filter by month across all years. Accepts a month number (1–12) or a 3-letter abbreviation (e.g. "Apr").

For example, departure_month: 4 returns products with departures in April of every year.

{% product_search departure_month: "Apr" %}
{% endproduct_search %}

{% product_search departure_month: 4 %}
{% endproduct_search %}

URL: ?search[departure_month]=Apr or ?search[departure_month]=4

duration

Filter by experience duration using a range hash. Values represent the stored duration number.

Experience durations can represent hours or nights. duration: { equal_to: 2 } matches experiences with a duration of 2 regardless of the unit. Unlike experience_slot_search, product_search does not support duration_unit.

{% product_search duration: { greater_than: 3, less_than: 8 } %}
{% endproduct_search %}

URL: ?search[duration][greater_than]=3&search[duration][less_than]=8

product_id

Restrict to specific experience ID(s). Accepts a single UUID or an array.

{% product_search product_id: "abcd1234-1234-abcd-1234-abcd1234abcd" %}
{% endproduct_search %}

{% assign product_ids = products | map: "id" %}
{% product_search product_id: product_ids %}
{% endproduct_search %}

URL: ?search[product_id]=abcd1234-1234-abcd-1234-abcd1234abcd

series_id

Filter to products belonging to a specific series.

{% product_search series_id: "abcd1234-1234-abcd-1234-abcd1234abcd" %}
{% endproduct_search %}

URL: ?search[series_id]=abcd1234-1234-abcd-1234-abcd1234abcd

exclude_sold_out_products

When true, excludes products where all variants are sold out.

{% product_search exclude_sold_out_products: true %}
{% endproduct_search %}

URL: ?search[exclude_sold_out_products]=true

active_promotion

When true, returns only products with an active promotion. When false, returns only products without.

{% product_search active_promotion: true %}
{% endproduct_search %}

include_organisation_products

When true, searches across all companies in the organisation rather than just the current company.

{% product_search include_organisation_products: true %}
{% endproduct_search %}

sort

Sort order for results. Default: "departure_date_asc".

ValueDescription
departure_date_ascDeparture date ascending (default).
departure_date_descDeparture date descending.
name_ascProduct name A–Z.
name_descProduct name Z–A.
duration_ascDuration ascending.
duration_descDuration descending.
{% product_search sort: "departure_date_asc" %}
{% endproduct_search %}

URL: ?search[sort]=departure_date_asc

page_size

Number of results per page. Default: 10. Tag attribute only — not available via URL.

{% product_search page_size: 12 %}
{% endproduct_search %}

Tag categories

Products can be filtered by company-defined tag categories and values. This is only available as a URL query parameter — it cannot be passed as a tag attribute.

Filtering returns products that match at least one of the provided values in all filtered categories (AND across categories, OR within a category).

URL: ?search[Difficulty][]=Easy&search[Amenities][]=Pool

Examples:

  • ?search[Difficulty][]=Easy — Products tagged Easy under Difficulty.
  • ?search[Difficulty][]=Easy&search[Difficulty][]=Moderate — Products tagged Easy or Moderate (OR within one category).
  • ?search[Amenities][]=Pool&search[Difficulty][]=Hard — Products tagged Pool under Amenities and Hard under Difficulty (AND across categories).

In Liquid, tag category values are available on the search object using bracket syntax:

{% if search %}
  {{ search["Amenities"] }}
{% endif %}

Range parameters

The departure_date and duration parameters accept a hash with comparison keys:

KeyDescription
equal_toExact match.
greater_thanGreater than value.
greater_or_equal_thanGreater than or equal to value.
less_thanLess than value.
less_or_equal_thanLess than or equal to value. Tag attribute only — not available via URL.

For product_search, greater_or_equal_than and less_or_equal_than each take priority over the other operators. To filter a range, use greater_than with less_than together.

Pagination

Search results are paginated automatically.

Use the page_size parameter to control how many results are returned per page.

The paginate object can be used to build pagination controls:

{{ paginate | default_pagination }}

Available months

The search exposes an available_months array that can be used to build month filters.

Each item is a Date object representing the first day of a month that contains at least one future experience departure.

available_months reflects all future departures company-wide. It is not filtered by the current search parameters.

A typical use case is building a month selector that submits a departure_month filter:

{% product_search %}
  <select name="search[departure_month]">
    {% for month in available_months %}
      <option value="{{ month | date: '%m' }}">
        {{ month | date: "%B %Y" }}
      </option>
    {% endfor %}
  </select>
{% endproduct_search %}

Accessing search parameters

The applied URL parameters are available through the search object when the page URL includes ?search[...] query parameters. If no search params are present, search is not defined.

{% if search %}
  {{ search.departure_date }}
  {{ search["Amenities"] }}
{% endif %}

This is useful for preserving filter state in search forms and building navigable search interfaces.

Developer considerations

When implementing product search:

  • product_search returns Experience products only — not Accommodations or Packages.
  • If you want to return more than 12 results per page, use infinite scroll to append new results rather than increasing the pagination.
  • Use product_search when each product should appear once. Use experience_slot_search when each departure date should be a separate result.
  • Use URL query parameters to build shareable search pages.
  • Build month filters using available_months rather than generating calendar months manually.
  • Use the search object to preserve the current filter state in forms. Guard with {% if search %} as it is only defined when URL params are present.
  • Remember that URL query parameters override values passed directly to the tag.

On this page