Table of contents :

Filtering WooCommerce Products with YQL

100-days-of-vespa-ai-woocommerce-018

Table of contents :

WooCommerce stores often need more than a simple product search box. Customers may search for “running shoes” and then narrow the result set by price, stock status, brand, category, or product attributes. Vespa Query Language, usually called YQL, provides a structured way to express those constraints when WooCommerce product data has been indexed in Vespa.

For an agency, the important distinction is that YQL is not SQL and it does not query the WooCommerce database directly. It queries the Vespa document model. The product fields that can be filtered must therefore be represented in the Vespa schema and populated by the WooCommerce synchronization process.

Represent WooCommerce filter fields in Vespa

A product document might contain fields such as:

  • product_id: the WooCommerce product ID.
  • title: the product name.
  • description: searchable product content.
  • price: the current numeric price.
  • stock_status: a value such as instock or outofstock.
  • brand: the brand name.
  • category_ids: one or more WooCommerce category IDs.
  • attributes: product attributes such as color or size.

Fields used for frequent filters should normally be declared as Vespa attributes. Attributes are stored in memory and are efficient for filtering and sorting. A simplified schema might look like this:

schema product {
    document product {
        field product_id type int {
            indexing: attribute | summary
        }
        field title type string {
            indexing: index | summary
        }
        field price type double {
            indexing: attribute | summary
        }
        field stock_status type string {
            indexing: attribute | summary
        }
        field category_ids type array<int> {
            indexing: attribute | summary
        }
    }
}

The exact schema depends on the synchronization design. For example, a site may use a weighted set for category IDs or separate fields for normalized attribute values. The YQL expression must match the actual Vespa field type.

Basic YQL filtering

A YQL request has a where clause containing the conditions that determine which documents match. A basic product query can filter by stock status:

select * from product where stock_status = "instock"

When sent to Vespa, the query is commonly supplied as the yql request parameter:

GET /search/?yql=select%20*%20from%20product%20where%20stock_status%20%3D%20%22instock%22

In application code, build the query with a proper URL encoder rather than assembling an unescaped URL manually. This is especially important when a filter value originates from a WooCommerce storefront request.

Multiple conditions can be combined with and and or. Parentheses make the intended logic explicit:

select * from product
where stock_status = "instock"
and price >= 50
and price <= 150

This query returns in-stock products between 50 and 150 currency units. The currency itself is not inferred by Vespa, so the indexed price and the storefront display currency must be consistent or explicitly converted before indexing.

Filtering by WooCommerce categories

WooCommerce products often belong to several categories. If category IDs are stored in an array field, use contains to test whether the array contains a particular ID:

select * from product
where category_ids contains 42
and stock_status = "instock"

This is generally more reliable than filtering on a category label. Category names can change, may not be unique, and can vary by language. Keep the WooCommerce term ID as the canonical filter value, and retrieve the display label separately for the storefront facet.

If a product has a single scalar category field, equality may be appropriate instead:

select * from product where primary_category_id = 42

Use the expression that corresponds to the schema. An equality check on a scalar field is not interchangeable with an array membership check.

Combining keyword search and structured filters

Most catalog requests contain both free-text input and structured constraints. YQL can combine a text condition with product filters:

select * from product
where title contains "running shoes"
and brand = "Acme"
and price <= 120
and stock_status = "instock"

The text expression above is intentionally simple. For production search, agencies commonly pass the visitor’s text through Vespa query parsing, a query profile, or a dedicated query item rather than inserting raw user input directly into a YQL string. One common pattern is to use userInput for the free-text portion and keep controlled catalog filters in the YQL expression:

select * from product
where userInput(@query)
and brand = "Acme"
and stock_status = "instock"

The request can then provide query=running shoes. The query profile and ranking configuration determine how the text is interpreted. Filter values such as brand, category ID, and stock status should be validated against an allowlist or converted from known storefront values before they are added to the request.

Filtering product attributes

Product attributes require a deliberate indexing model. If color is stored as an array of normalized strings, a filter might look like this:

select * from product
where colors contains "black"
and sizes contains "42"

Normalize values during the WooCommerce-to-Vespa sync. For example, store Black, black, and a translated equivalent according to a documented normalization strategy. Otherwise, a filter can appear to fail even though the product visibly has the requested attribute.

For attributes that have both a name and a value, avoid flattening unrelated pairs into one generic string unless the matching behavior is acceptable. A product with material=cotton and color=blue should not accidentally match a query that combines the wrong attribute name and value. Agencies may model important attributes as separate fields, use structured arrays, or create normalized filter tokens during ingestion.

Price ranges and sale pricing

WooCommerce products frequently expose regular price, sale price, and effective display price. Decide which value powers filtering and index that value consistently. If the storefront filters on the current selling price, the Vespa query should use an indexed field such as effective_price:

select * from product
where effective_price >= 25
and effective_price < 75
and stock_status = "instock"

Use a half-open range such as >= 25 and < 75 when adjacent price buckets must not overlap. The synchronization job should update the Vespa document whenever a WooCommerce sale starts, ends, or changes.

Applying filters without damaging relevance

A filter should restrict the candidate products, while the ranking profile should decide the order of matching products. Keep catalog constraints in the YQL where clause and configure text relevance, popularity, conversion data, or business rules in the ranking profile.

select * from product
where userInput(@query)
and category_ids contains 42
and stock_status = "instock"

The request can select a ranking profile separately:

GET /search/?query=waterproof%20jacket&ranking=product_relevance

In a real integration, the client should URL-encode the query parameters. If a filter is intended only as a soft preference rather than a hard requirement, it should usually be represented in ranking logic instead of a mandatory YQL condition.

Debugging filter results

When a filter returns no products, inspect the indexed document rather than the WooCommerce admin screen alone. Verify the field name, field type, normalized value, document freshness, and whether the field is populated on every relevant product.

  1. Run the query without the filter to confirm that the product is indexed.
  2. Add one condition at a time.
  3. Check whether the value is scalar, an array, or a weighted set.
  4. Compare the exact indexed value, including case, whitespace, IDs, and numeric units.
  5. Confirm that the WooCommerce synchronization process has sent the latest product update.

For agency projects, keep representative YQL requests in automated integration tests. Include tests for category membership, price boundaries, stock changes, attribute combinations, and products with multiple categories. This catches schema or synchronization regressions before a filter disappears from a production catalog.

Trending posts
You might also like