The Search Query

Plan: Composable Developer

Lesson 15 of 27 · 30 min

In the previous lab exercise, you queried a list of products as a sub-field of the category information being fetched with site.route. In this section, we’ll look at a different strategy using site.search.

The search query is, naturally, the best fit for fetching products based on a search term. But it’s also a versatile option for any product listing page, including category and brand pages, due to the flexibility it offers for inspecting and applying filters.

Category Page

You can use the search query as a different way to get a list of products in a specific category. To do so, you will create a filter on the searchProducts query and specify the categoryId. Then you can add product fields for the data you need.

The query below is asking for the same information as the site.category query in the previous lesson. The difference here is that you are using the category filter on a searchProducts query instead of using the site.category query. The response will be a list of product entityIds and names that are assigned to that category.

query MyQuery($categoryId: Int!) {
site {
search {
searchProducts(filters: {categoryEntityId: $categoryId}) {
products {
edges {
node {
entityId
name
}
}
}
}
}
}
}

Like the query in the Category Page lesson, you can add a sort field that will organize the returned list of products. The example query below is the same query as above, but it now contains a sort field that tells the GraphQL Storefront API to organize the list of products by price (lowest to highest).

query MyQuery($categoryId: Int!) {
site {
search {
searchProducts(filters: {categoryEntityId: $categoryId}, sort: LOWEST_PRICE) {
products {
edges {
node {
entityId
name
}
}
}
}
}
}
}

What sets this query apart from other queries we’ve looked at is the ability to fetch, and apply, a list of available filters for a product, brand, category page, and more. Continuing with the example query above, let’s query for available product attribute filters for this category.

First we’ll write a basic query that will return data about available product filters. __typename will tell you the type of each available filter.

query MyQuery($categoryId: Int!) {
site {
search {
searchProducts(filters: {categoryEntityId: $categoryId}, sort: LOWEST_PRICE) {
products {
edges {
node {
entityId
sku
}
}
}
filters {
edges {
node {
__typename
}
}
}
}
}
}
}

The results will look something like this:

{
"data": {
"site": {
"search": {
"searchProducts": {
"products": {
"edges": [
{
"node": {
"entityId": 130,
"sku": "Shirt-1"
}
},
{
"node": {
"entityId": 126,
"sku": "Shirt-2"
}
},
{
"node": {
"entityId": 129,
"sku": "Shirt-3"
}
},
{
"node": {
"entityId": 127,
"sku": "Shirt-4"
}
},
{
"node": {
"entityId": 128,
"sku": "Shirt-5"
}
}
]
},
"filters": {
"edges": [
{
"node": {
"__typename": "ProductAttributeSearchFilter"
}
},
{
"node": {
"__typename": "ProductAttributeSearchFilter"
}
},
{
"node": {
"__typename": "PriceSearchFilter"
}
},
{
"node": {
"__typename": "OtherSearchFilter"
}
}
]
}
}
}
}
}
}

The example response above tells you that there are four available filters for this category and these are the types: ProductAttributeSearchFilter, PriceSearchFilter, and OtherSearchFilter. Each filter type has its own unique fields, and you can expand the initial query for more data using type-specific fields as shown in the next example query.

query MyQuery($entityId: Int!) {
site {
search {
searchProducts(filters: {categoryEntityId: $entityId}, sort: LOWEST_PRICE) {
products {
edges {
node {
entityId
name
}
}
}
filters {
edges {
node {
__typename
... on ProductAttributeSearchFilter {
attributes {
edges {
node {
value
isSelected
}
}
}
}
}
}
}
}
}
}
}

The ...on ProductAttributeSearchFilter section that was added to the query above is telling the GraphQL Storefront API to add information about product attributes, if any of the filters are that type. An example response would look like:

{
"data": {
"site": {
"search": {
"searchProducts": {
"products": {
"edges": [
{
"node": {
"entityId": 128,
"name": "Plaid Jacket"
}
},
{
"node": {
"entityId": 135,
"name": "Awesome Shirt",
}
}
]
},
"filters": {
"edges": [
{
"node": {
"__typename": "PriceSearchFilter"
}
},
{
"node": {
"__typename": "OtherSearchFilter"
}
},
{
"node": {
"__typename": "ProductAttributeSearchFilter",
"name": "Size",
"attributes": {
"edges": [
{
"node": {
"value": "4oz",
"isSelected": false
}
},
{
"node": {
"value": "8oz",
"isSelected": false
}
},
{
"node": {
"value": "Large",
"isSelected": false
}
},
{
"node": {
"value": "Medium",
"isSelected": false
}
},
{
"node": {
"value": "Small",
"isSelected": false
}
}
]
}
}
},
{
"node": {}
},
{
"node": {
"__typename": "ProductAttributeSearchFilter",
"name": "Color",
"attributes": {
"edges": [
{
"node": {
"value": "Blue",
"isSelected": false
}
},
{
"node": {
"value": "Red",
"isSelected": false
}
},
{
"node": {
"value": "Grey",
"isSelected": false
}
},
]
}
}
}
]
}
}
}
}
}
}

From the response above, you know that there are two available product attribute filters for the specified category: Size and Color. The response also includes the options for each filter such as color has two options: blue, red, and grey.

Now you can write a new query that further filters the results set with values from the available productAttributes filter.

query Search($categoryId: Int!) {
site {
search {
searchProducts(
filters: {
categoryEntityId: $categoryId,
productAttributes: {attribute: "Color", values: ["Red", "Blue", "Grey"]}
}
) {
...
}
}
...

This workflow easily facilitates a typical product listing page with facet filtering, allowing you to present the user with the filters available for the current product set, then apply their filter selections.

Other Filter Types

There are other filter types, each of which have their own unique fields. Those other types include:

  • BrandSearchFilter
  • CategorySearchFilter
  • OtherSearchFilter
  • PriceSearchFilter
  • RatingSearchFilter

Each of these types have their own set of fields that can be included in the query. You can include as many sections of filters as you want and GraphQL Storefront API will only include the available information in the response. See the GraphQL Storefront API reference for more details about the fields for each fragment.

Use Case - Search Page

Another use case for the search query is to use it for the Search page on your storefront where customers can use search terms to find products. Let’s use the search query to filter on a search term, find what filter types are available, and find which products are available when those filters are applied.

The query below is asking for all available filters for the search term “shirt,” and more information if the term matches a CategorySearchFilter or ProductAttribute Filter. The query is also asking the GraphQL Storefront API to return the products that match the search term.

query Search($term: String) {
site {
search {
searchProducts(filters: {searchTerm: $term}) {
filters {
edges {
node {
... on CategorySearchFilter {
__typename
categories {
edges {
node {
entityId
name
}
}
}
}
... on ProductAttributeSearchFilter {
__typename
attributes {
edges {
node {
value
isSelected
}
}
}
}
}
}
}
products {
edges {
node {
entityId
name
prices {
basePrice {
currencyCode
value
}
}
}
}
}
}
}
}
}

Putting It Together

The following example demonstrates presenting a search results list along with available product attribute filters that can be used to further narrow the results.

const token = // GraphQL token
const storeHash = // BigCommerce store hash
const channelId = // BigCommerce channel ID
const searchTerm = // A search term
const currentFilters = // An array of applied filters, formatted something like:
/*
[{attribute: "Color", values: ["Black", "Blue"]}];
*/
const SearchResultsComponent = async () => {
const searchResult = await fetch(
`https://store-${storeHash}-${channelId}.mybigcommerce.com/graphql`,
{
...
body: JSON.stringify({
query: `
query Search(
$searchTerm: String,
$attrFilters: [ProductAttributeSearchFilterInput!]
) {
site {
search {
searchProducts(
filters: {
searchTerm: $searchTerm,
productAttributes: $attrFilters
}
) {
filters {
edges {
node {
__typename
... on ProductAttributeSearchFilter {
filterName
attributes {
edges {
node {
value
isSelected
}
}
}
}
}
}
}
products {
edges {
node {
...
}
}
}
}
}
}
}
`,
variables: {
searchTerm,
attrFilters: currentFilters,
}
}),
}
);
const { filters, products } = await searchResult.json().then(json => json.data.site.search.searchProducts);
return (
<>
<div className="filters">
{filters.edges.map(filter => (
<>
{filter.node.__typename === "ProductAttributeSearchFilter" && (
<div key={filter.node.filterName}>
<h3>{filter.node.filterName}</h3>
<ul>
{filter.node.attributes.edges.map(attr => (
<li key={attr.node.value}>
<input type="checkbox"
value={attr.node.value}
checked={attr.node.isSelected} />
<label>{attr.node.value}</label>
</li>
))}
</ul>
</div>
)}
</>
))}
</div>
<ul>
// Display the products
</ul>
</>
)
}

Resources