Product Search enables you to create storefront discovery experiences for your customers where they can browse and search across your Products.
After completing this page, you should be able to:
- Explain the main differences between Product Search and other product-related endpoints
- Enable Product Search in your project
- Create basic Product Search queries
- Use compound expressions in your Product Search queries
Time to complete page: 25 minutes
Good product search capability is not just a technical necessity but a strategic asset that directly impacts customer satisfaction, conversion rates, and overall business success in a digital commerce environment.
You should be familiar with a range of product-related endpoints that can help us manage products (/products
), view specific product projections (/product-projections
) and search for products along with performing a price selection (/product-projections/search
). These allow us to achieve a lot, but what if we want to search products only belonging to a specific product selection? Or if we want to control which products from our search results should appear before other ones? The Product Search endpoint can help us achieve that.
The new Product Search endpoint is designed to deliver more precise, personalized, and faster results by offering users greater flexibility and control over their search queries, which are now provided in the request body. Unlike the older Product Projection Search, this endpoint enables searching across Stores, Product Selections, and Standalone Prices, ensuring a more tailored and efficient search experience.
But let’s not get ahead of ourselves!
To use this endpoint we need to first enable it in our project. This currently can only be done via the API by submitting an update request to your Project
endpoint containing the changeProductIndexingEnabled
Update Action:
// Ensure all necessary imports are included and the API client is instantiated as demonstrated in the first example.
System.out.println(
"Product Search indexing status: " +
apiRoot
.post(
ProjectUpdateBuilder
.of()
.version(project.getVersion())
.actions(
ProjectChangeProductSearchIndexingEnabledActionBuilder
.of()
.enabled(true)
.build()
)
.build()
)
.executeBlocking()
.getBody()
.getSearchIndexing()
.getProductsSearch()
.getStatus()
);
apiRoot.close();
It’s worth pointing out that once activated, Product Search will deactivate itself if the feature is not used for the duration of 30 consecutive days. Once Product Search is activated (and we give it a bit of time to index all of our Products) we can write our first Product Search Request:
// Ensure all necessary imports are included and the API client is instantiated as demonstrated in the first example.
// Build search request
var searchRequest = ProductSearchRequestBuilder
.of()
.query(
SearchFullTextExpressionBuilder
.of()
.fullText(
SearchFullTextValueBuilder
.of()
.value("Cocktail")
.field("name")
.language("en-GB")
.mustMatch(SearchMatchType.ANY)
.build()
)
.build()
)
// If productProjectionSearchParameters are not specified, only Product ID's will be returned
.productProjectionParameters(
ProductSearchProjectionParamsBuilder.of().staged(false).build()
)
.build();
// Perform product search
ProductPagedSearchResponse searchResponse = apiRoot
.products()
.search()
.post(searchRequest)
.executeBlocking()
.getBody();
// Close API client
apiRoot.close();
System.out.println("Found Products:");
// Print all product names in English
searchResponse
.getResults()
.forEach(product ->
System.out.println(product.getProductProjection().getName().get("en-GB"))
);
From the results we can see that the result contains a list of Products which have the word “Cocktail” in their en-GB
name
:
Found Products:
Cocktail Strainer
Cocktail Shaker
Cocktail Shaker Set
Cocktail Stirring Spoon
If you think that we could do this previously using the /product-projections/search
endpoint, you would be almost right about that, but we are just getting started.
Notice that in Product Search the query
is posted in the body
of the request. This means that we have a lot more flexibility when it comes to shaping our query. In our basic example above we are making a fullText
search, but Product Search also supports other query expressions, such as exact
, prefix
, range
, wildcard
and exists
. Not only that, but these expressions can also be compounded!
With this knowledge, let’s update our example and search for a Product where the en-GB
name
starts with the word “Cotton” and has either “King” or “Queen” size:
// Ensure all necessary imports are included and the API client is instantiated as demonstrated in the first example.
var searchRequest = ProductSearchRequestBuilder
.of()
.query(
SearchAndExpressionBuilder
.of()
.and(
SearchPrefixExpressionBuilder
.of()
.prefix(
SearchAnyValueBuilder
.of()
.value("Cotton")
.language("en-GB")
.field("name")
.build()
)
.build()
)
.plusAnd(
SearchOrExpressionBuilder
.of()
.or(
SearchExactExpressionBuilder
.of()
.exact(
SearchAnyValueBuilder
.of()
.value("King")
.language("en-GB")
.field("variants.attributes.size")
.fieldType(SearchFieldType.LTEXT)
.build()
)
.build()
)
.plusOr(
SearchExactExpressionBuilder
.of()
.exact(
SearchAnyValueBuilder
.of()
.value("Queen")
.language("en-GB")
.field("variants.attributes.size")
.fieldType(SearchFieldType.LTEXT)
.build()
)
.build()
)
.build()
)
.build()
)
.productProjectionParameters(
ProductSearchProjectionParamsBuilder.of().staged(false).build()
)
.markMatchingVariants(true)
.build();
// Perform product search
ProductPagedSearchResponse searchResponse = apiRoot
.products()
.search()
.post(searchRequest)
.executeBlocking()
.getBody();
// Close API client
apiRoot.close();
System.out.println("Found Products:");
// Print all product names in English
searchResponse
.getResults()
.forEach(product -> {
System.out.println(
product.getProductProjection().getName().get("en-GB") +
"\nMatching variants:"
);
product
.getMatchingVariants()
.getMatchedVariants()
.forEach(variant -> System.out.println("SKU: " + variant.getSku()));
});
As you can see in the results, several SKUs of the Cotton Silk Bedsheet meet our search criteria:
Found Products:
Cotton Silk Bedsheet
Matching variants:
SKU: CSKW-093
SKU: CSKW-9822
SKU: CSKP-0932
SKU: CSKP-083
SKU: CSKG-023
SKU: CSKG-2345
Product Search is not just powerful; it’s incredibly versatile! The examples we’ve explored merely scratch the surface of its potential. You can enhance your results through boosting and sorting, apply filters and facets for precise searches, and even implement price selection to fit the search results to your needs:
// Ensure all necessary imports are included and the API client is instantiated as demonstrated in the first example.
// Build the search request
var searchRequest = ProductSearchRequestBuilder
.of()
.query(
SearchAndExpressionBuilder
.of()
.and(
SearchFilterExpressionBuilder
.of()
.filter(
SearchExistsExpressionBuilder
.of()
.exists(
SearchExistsValueBuilder
.of()
.field("variants.availability")
.build()
)
.build()
)
.build()
)
.plusAnd(
SearchOrExpressionBuilder
.of()
.or(
SearchFullTextExpressionBuilder
.of()
.fullText(
SearchFullTextValueBuilder
.of()
.value("Modern")
.language("en-US")
.field("name")
.boost(2.0)
.build()
)
.build()
)
.plusOr(
SearchFullTextExpressionBuilder
.of()
.fullText(
SearchFullTextValueBuilder
.of()
.value("Rustic")
.language("en-US")
.field("name")
.build()
)
.build()
)
.build()
)
.build()
)
.facets(
ProductSearchFacetRangesExpressionBuilder
.of()
.ranges(
ProductSearchFacetRangesValueBuilder
.of()
.name("Availability")
.field("variants.availability.availableQuantity")
.ranges(
ProductSearchFacetRangesFacetRangeBuilder
.of()
.key("almost_out")
.to(10)
.build()
)
.plusRanges(
ProductSearchFacetRangesFacetRangeBuilder
.of()
.key("limited")
.from(10)
.to(100)
.build()
)
.plusRanges(
ProductSearchFacetRangesFacetRangeBuilder
.of()
.key("available")
.from(100)
.build()
)
.build()
)
.build()
)
.productProjectionParameters(
ProductSearchProjectionParamsBuilder
.of()
.staged(false)
.priceCurrency("USD")
.priceCountry("US")
.build()
)
.sort(
SearchSortingBuilder
.of()
.field("name")
.language("en-US")
.order(SearchSortOrder.ASC)
.build()
)
.build();
// Execute the search request
ProductPagedSearchResponse searchResponse = apiRoot
.products()
.search()
.post(searchRequest)
.executeBlocking()
.getBody();
apiRoot.close();
// Display results
System.out.println("Found Products:");
searchResponse
.getResults()
.forEach(product -> {
var productProjection = product.getProductProjection();
System.out.println("Product: " + productProjection.getName().get("en-US"));
System.out.println(
"Master Variant: " +
productProjection.getMasterVariant().getSku() +
" | Price: " +
productProjection.getMasterVariant().getPrice().getValue().getCentAmount()
);
productProjection
.getVariants()
.forEach(variant ->
System.out.println(
"Variant: " +
variant.getSku() +
" | Price: " +
variant.getPrice().getValue().getCentAmount()
)
);
});
searchResponse
.getFacets()
.forEach(facet -> {
try {
System.out.println(
"Facet results:\n" +
JsonUtils.prettyPrint(JsonUtils.toJsonString(facet))
);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
});
In this example, we search for products with either “Modern” (boosted) or “Rustic” in their English names, filtering for those with specified inventory. How much inventory? The range facet provides this information, while the Price Selection reveals the cost for American customers. Finally, we sort the results alphabetically by their English names:
RESULT:
Found Products:
Product: Minimalist Modern Side Table
Master Variant: MMST-01 | Price: 12000
Variant: MMST-02 | Price: 4999
Product: Modern Black Coaster
Master Variant: BCOAS-08 | Price: 1999
Product: Modern Bookcase
Master Variant: MB-0973 | Price: 29900
Product: Modern Ceramic Plate
Master Variant: MCP-01 | Price: 299
Product: Modern Glam Dresser
Master Variant: MGD-01 | Price: 179900
Product: Modern Gold Coffee Table
Master Variant: GMCT-01 | Price: 25999
Product: Modern Landscape Painting
Master Variant: MLP-01 | Price: 5299
Product: Modern Three Seater Sofa
Master Variant: MTSS-01 | Price: 249900
Product: Modern Upholstered Queen Bed
Master Variant: MUQB-01 | Price: 259900
Product: Red Modern Painting
Master Variant: RMP-01 | Price: 3299
Product: Rustic Bowl
Master Variant: RB-01 | Price: 199
Product: Rustic Country Dresser
Master Variant: RCD-01 | Price: 159900
Product: Rustic Country Queen Bed
Master Variant: RCQB-01 | Price: 329900
Product: Rustic Oven Casserole
Master Variant: RCC-09 | Price: 2599
Facet Results:
Facet: 0
{
"name": "Availability",
"buckets": [
{
"key": "almost_out",
"count": 1
},
{
"key": "limited",
"count": 8
},
{
"key": "available",
"count": 5
}
]
}
The possibilities with Product Search are vast and exciting—dive in and discover all that it has to offer!