# Product catalog ## Product Projections In modern commerce, every millisecond counts, and sometimes, the data you don't load is just as important as the data you do. Before the introduction of [attribute filtering](/api/projects/productProjections.md#attributes), when you fetched a [Product Projection](/api/projects/productProjections.md), the response included all associated attributes for the Master Variant and all other Variants. While comprehensive, this often led to overfetching and unnecessarily large payload sizes, especially for complex products with hundreds of attributes. We're excited to introduce the new `filter[attributes]` query parameter. This allows you to surgically specify exactly which Product Attributes (on the Product, Master Variant, or other Variants) you want to be included in (or excluded from) the API response. The new `filter[attributes]` parameter is available across all core Product Projection endpoints, including queries, fetching by `ID`, fetching by `key`, and store-scoped requests. Using it is straightforward, supporting both inclusion (whitelist) and exclusion (blacklist) modes: ### Including specific Attributes (Whitelist) To request only a specific set of attributes, simply provide a list of the attribute names. Any attribute not listed will be omitted from the response. Syntax: `filter[attributes]=attributeName1,attributeName2` For example, let's try to [get a current Product Projection](/search.md?urn=ctp:api:endpoint:/{projectKey}/product-projections/key={key}:GET) of a specific product (Rustic Country Queen Bed), but we only want to see the information about the color and finish for each variant: ```bash title="Request" curl --get "https://api.{region}.commercetools.com/{projectKey}/product-projections/key=rustic-country-queen-bed?filter[attributes]=search-color&filter[attributes]=search-finish" --header "Authorization: Bearer {bearerToken}" | jq ``` ```json title="Result" { "id": "70ab1f54-277e-4504-87f3-cdae3d5be73d", "version": 3, "productType": { "typeId": "product-type", "id": "415bec3b-e1de-4bbd-8c62-e537937ec817" }, "name": { "en-US": "Rustic Country Queen Bed", "en-GB": "Rustic Country Queen Bed", "de-DE": "Rustikales Queensize-Bett im Landhausstil" }, ... "attributes": [ { "name": "search-finish", "value": { "key": "brown", "label": { "en-US": "Brown", "en-GB": "Brown", "de-DE": "Braun" } } }, { "name": "search-color", "value": { "key": "yellow", "label": { "en-US": "Yellow", "en-GB": "Yellow", "de-DE": "Gelb" } } } ], ... } ``` ### Excluding Specific Attributes (Blacklist) To fetch all attributes except a few, use the minus sign (-) prefix before the attribute name. Syntax: `filter[attributes]=-attributeName1` If we want to get all attributes except search-color: ```bash title="Request" curl --get "https://api.{region}.commercetools.com/{projectKey}/product-projections/key=rustic-country-queen-bed?filter%5Battributes%5D=-search-color" --header "Authorization: Bearer {bearerToken}" | jq ``` ```json title="Result" { ... "attributes": [ { "name": "search-finish", "value": { "key": "brown", "label": { "en-US": "Brown", "en-GB": "Brown", "de-DE": "Braun" } } }, { "name": "finish-code", "value": "#8b4513" }, { "name": "productspec", "value": { "en-US": "- Leather upholstery\n- Standard Queen size\n- Assembly on site", "en-GB": "- Leather upholstery\n- Standard Queen size\n- Assembly on site", "de-DE": "- Leather upholstery\n- Standard Queen size\n- Assembly on site" } }, { "name": "finish-label", "value": { "en-GB": "Saddle Brown", "de-DE": "Sattel braun", "en-US": "Saddle Brown" } }, { "name": "color-label", "value": { "en-GB": "Tan", "de-DE": "Bräunen", "en-US": "Tan" } }, { "name": "color-code", "value": "#D2B48C" } ], ... } ``` This new Attribute Filtering functionality is a prime example of how commercetools is continuously focused on developer experience and performance. By reducing unnecessary data payloads, you gain immediate, measurable improvements in application speed and efficiency. Make sure to integrate the `filter[attributes]` parameter into your next project or optimization sprint to unlock these benefits! ## Product Search A strategic advantage in e-commerce, effective product search goes beyond a mere technical function. It directly impacts customer satisfaction, drives conversion rates, and is crucial for overall success. This is precisely why the Product Search API was introduced in 2024 and continuously improved throughout 2025. Let's have a look at some of the new features that have been introduced over the course of last year. Product Search relies on an index of your Product data to return fast and relevant results, so indexing must be enabled for your Project before you can use it. Before diving in, you'll need to check if Product Search is activated for your Project. This can be done by a simple API request to [get your Project information](/search.md?urn=ctp:api:endpoint:/{projectKey}:GET), or by opening your Project in the Merchant Center, going to **Settings → Project Settings → Storefront Search**, and making sure that **Product Search** indexing is set to **active**. The Product Search API's speed comes from its ID-first design; it traditionally returned only the IDs of matching Products, requiring a second step to fetch product details via GraphQL. Now, with Product Search functionality integrated directly into the GraphQL API, you can query and retrieve product details in a single streamlined step. Now all we need is a tool that would help us create, validate, and test our GraphQL Product Search queries. Fortunately, you don't have to wish for a tool like that—we already have one! Simply open the Merchant Center and navigate to **Settings → Developer Settings → GraphQL Explorer**. How about we use it to test out some new features that were introduced to Product Search in 2025? ### Fuzzy search Searching for desired products is a crucial functionality that is not only desired, but expected from commerce platforms. We can all agree however how annoying it can be when we can't recall the exact name of the product or when typos creep in, preventing customers from finding exactly what they were looking for. Fortunately, Product Search now supports [fuzzy search queries](/api/search-query-language.md#fuzzy). Not only that, you can adjust the "fuzziness level" to determine how tolerant the query can be. While allowed fuzziness values are 0, 1, and 2, the API dynamically adjusts the effective level based on the length of each search term: 0 for 1–2 characters (exact match), 1 for 3–5 characters (up to one difference), and 2 for terms over 5 characters (up to two differences). Without the fuzzy query, a standard fullText search for a misspelling like "tarditional" would typically fail to find any relevant products. However, the introduction of the new fuzzy query increases the likelihood of successfully finding these products despite the typo: ```graphql title="Request" query FuzzySearch { productsSearch( query: {fuzzy: {field: "name", value: "tarditional", level: 2, language: "en-GB"}} ) { results { product { key } } } } ``` ```json title="Result" { "data": { "productsSearch": { "results": [ { "product": { "key": "traditional-armchair" } }, { "product": { "key": "traditional-l-seater-sofa" } }, { "product": { "key": "traditional-three-seater-sofa" } } ] } } } ``` ### Statistical facets But what if we would like to get some statistical information about some numeric properties of our products? For example about their EUR price for German customers? This is now possible thanks to a new addition to the Product Search API, [statistical facets](/api/projects/product-search.md#stats-facets)—a new type of facet that provides statistical aggregations (min, max, average, sum, count) for number and date fields in search results. This offers insights into product data without client-side calculation using multiple queries. Let's use it to expand our previous request: ```graphql title="Request" query FuzzySearchWithGermanPriceStats { productsSearch( query: {fuzzy: {field: "name", value: "tarditional", level: 2, language: "en-GB"}} facets: {stats: {name: "priceStatistics", field: "variants.prices.centAmount", filter: {and:[{exact: {field: "variants.prices.currencyCode", value: "EUR"}}, {exact: {field: "variants.prices.country", value: "DE"}}]}}} ) { results { product { key masterData { current { allVariants { prices { country value { centAmount currencyCode } } } } } } } facets { ... on ProductSearchFacetResultStats { name count sum min max mean } } } } ``` ```json title="Result" { "data": { "productsSearch": { "results": [ ... ], "facets": [ { "name": "priceStatistics", "count": 3, "sum": 629700, "min": 29900, "max": 359900, "mean": 209900 } ] } } } ``` We now have not only the count of prices that meet our requirements but also valuable statistical information about them. ### Filter by discounted prices What if you only want to retrieve discounted "traditional" products? This is now achievable thanks to the newly introduced feature that allows you to [query Products based on whether any of their Product Variants are currently discounted](/api/releases/2025-09-12-added-filter-for-discounted-prices-to-product-search). ```graphql title="Request" query FuzzySearchForDiscountedProducts { productsSearch( query: {and: [{fuzzy: {field: "name", language: "en-GB", level: 2, value: "tarditional"}}, {exact: {field: "variants.prices.discounted", value: true}}]} ) { offset limit results { id product { key masterData { current { allVariants { prices { discounted { value { centAmount currencyCode } } value { centAmount currencyCode } } } } } } } } } ``` ```json title="Result" { "data": { "productsSearch": { "offset": 0, "limit": 10, "results": [ { "id": "58084c1e-ccde-4c6d-9fd7-afc2ecd05673", "product": { "key": "traditional-armchair", "masterData": { "current": { "allVariants": [ { "prices": [ { "discounted": { "value": { "centAmount": 25415, "currencyCode": "EUR" } }, "value": { "centAmount": 29900, "currencyCode": "EUR" } }, { "discounted": { "value": { "centAmount": 25415, "currencyCode": "GBP" } }, "value": { "centAmount": 29900, "currencyCode": "GBP" } }, { "discounted": { "value": { "centAmount": 25415, "currencyCode": "USD" } }, "value": { "centAmount": 29900, "currencyCode": "USD" } } ] } ] } } } } ] } } } ``` ### Search for Product Variants in Stores A significant enhancement to Product Search is the new capability to locate Product Variants that are available in specific Stores, regardless of how many active selections that Store has, or within specific Store Selections. Previously, this was only feasible if the entire Product belonged to the selection. Let's give it a try: ```graphql title="Request" query FindVariantsBelongingToStore { productsSearch( query: {exact: {value: "a748b5f7-54b3-48f4-b744-fafb4b5d9f2c", field: "variants.stores"}} markMatchingVariants: true ) { results { product { key masterData { current { allVariants(onlyMatching: true) { sku } } } } } } } ``` ```json title="Result" { "data": { "productsSearch": { "results": [ { "product": { "key": "modern-three-seater-sofa", "masterData": { "current": { "allVariants": [ { "sku": "MTSS-01" } ] } } } }, { "product": { "key": "maya-pillow-cover", "masterData": { "current": { "allVariants": [ { "sku": "MPC-02" } ] } } } } ] } } } ``` The significant enhancements to Product Search in 2025—including fuzzy search, statistical facets, discounted price filters, and granular variant selection—collectively deliver a powerful, flexible, and high-performance search experience. By leveraging these new capabilities, Functional Architects can design storefronts that are more resilient to user error, provide deeper analytical insights on pricing, and offer precise control over store-specific product visibility, ultimately leading to improved conversion rates and a superior customer experience. ## Product Projection Search Since we're on the topic of GraphQL and searching, when you perform a product search (via the `productProjectionSearch` query in GraphQL), your search criteria might target specific attributes of a variant (for example, "red shirt, size medium"). While the search results correctly return the parent product, the `variants` and `allVariants` fields on that product traditionally returned all variants of that product. This meant that if your search was successful, you still had to implement client-side filtering to isolate the one or two variants that actually matched your original search query. This adds unnecessary complexity and overhead to your application. Thankfully that is in the past, because in 2025, we've introduced the `onlyMatching: Boolean` argument to the `variants` and `allVariants` fields within the `ProductProjection` type. When you set `onlyMatching: true`, the commercetools API takes on the responsibility of filtering the list of variants, ensuring that the response only includes the Product Variants that matched your original `productProjectionSearch` query. To illustrate the elegance and efficiency of this new feature, let's look at a GraphQL query example. Let's take a product like "Nala Two-Seater Sofa" with seven variants (various colors and finishes). You search for a sofa that is blue: ```graphql title="Request" query FindBlueSofa { productProjectionSearch( text: "sofa" locale: "en-US" filters: {model: {value: {path: "variants.attributes.search-color.key", values: "blue"}}} markMatchingVariants: true ) { results { key allVariants(onlyMatching: true) { sku } } } } ``` ```json title="Result" { "data": { "productProjectionSearch": { "results": [ { "key": "nala-two-seater-sofa", "allVariants": [ { "sku": "NTSS-02" }, { "sku": "NTTS-04" } ] } ] } } } ``` As you can see in the result, even though this product has seven variants, our search only returned the two which meet the search criteria. ## Related pages - [Area overview page with navigation](/certifications.md) - [Previous page: Release highlights of 2025](/certifications/composable-commerce-functional-refresher-2026/2025-release-highlights.md) - [Next page: Customers](/certifications/composable-commerce-functional-refresher-2026/customers.md) - [Search documentation and API specs](/search.md)