# Queries and the SDKs On the following page, you will learn about how to use queries in your API requests to make sure that your response from the Composable Commerce API delivers only the results that you need. We will learn the concepts in three steps, where we will: - Construct simple queries following the basic syntax checking the value of one field on the resource. - Combine multiple simple queries using logical operators. - Enhance our multiple queries with input variables. Let’s get into it! ## Predicate Syntax Basics Query Predicates are used in the `where` query parameter. Each query endpoint lists its possible query parameters. Let's take a look at the following example for Query Customers: ![List of possible query parameters listed in the docs for the Query Customers endpoint.](https://docs.commercetools.com/learning-developer-essentialsimages/api-queries-query-predicates/query-parameters.png) As mentioned on the previous page, the basic syntax follows this schema: ```bash field operator value ``` Fields are taken from the resource under inspection, but it’s important to note that not all fields that a resource representation provides can be used in a query. In the docs, the [Query Predicates](/api/predicates/query.md) page is a great starting point for learning about which fields can be used in a query. Have a look at the documentation for all possible operators and a long list of examples. ## Use the SDKs to make queries Let’s have a look at how we can use Query Predicates with our SDKs. We will take a simple example to start with: let's try to fetch all customers that live in Berlin. First, let’s break that statement down: all customers living in Berlin. How can we turn it into a predicate? First, let’s rewrite it in straightforward English; we want to fetch all customers whose Shipping Address has Berlin as its city. Then, in our predicate syntax, this could look something like: ```bash addresses(city = "Berlin") ``` Now let’s see how we can write that with our TypeScript and Java SDKs: ```TypeScript import { apiRoot } from "../impl/apiClient.js"; const predicate = 'addresses(city = "Berlin")'; async function customerByPredicate(predicate) { try { const response = await apiRoot .customers() .get({ queryArgs: { where: predicate, }, }) .execute(); console.log("Success", JSON.stringify(response.body, null, 2)); } catch (error) { console.log(error); } } customerByPredicate(predicate); ``` ```java logger.info( "Customers in Berlin: " + apiRoot .customers() .get() .withWhere("addresses(city = \"Berlin\")") .executeBlocking() .getBody() .getResults() .size() ); ``` The query is not limited to a single customer, so we would see a [CustomerPagedQueryResponse](/urn?urn=ctp%3Aapi%3Atype%3ACustomerPagedQueryResponse) in the response body. Inside the results fields, we can find the list of all customers that matched the predicate. ## Compound expressions Now imagine a scenario where you have to search for customers from Berlin who are not in a particular Customer Group. You could approach creating a query for this scenario in two ways. You could either fetch this data in two calls (Customers from Berlin, and Customers who are not in the Customer Group) and then go through the results to find the intersecting elements. Or, you could use a compound expression to do the job for you. commercetools predicate compound expressions have the following syntax: ```bash predicate logical-operator predicate ``` We can use the following logical operators: `and`, `or`, and `not` with our compound expressions. It is also possible to combine multiple compound expressions together to make even more complex compound expressions by using parentheses. Here are a few examples: - To search for a Customer with first name and last name: `firstName = "Peter" and lastName = “Porter”` - Find customers from either Berlin or Munich: `addresses(city = "Berlin" or city = "Munich")` - Find all the customers from Germany except Berlin: `addresses(country = "DE" and not(city= "Berlin"))` Let's look for a moment at examples related to Products. Have a go yourself at translating the following queries into natural English: - `variants(attributes(name="color" and value="red"))` - `variants(attributes(name="color" and value(en-US="red")))` Both of these queries would achieve the same result: returning all Products that have Product Variants in red. Take a special note however of the use of the localized attribute in the second example. When querying Product Attributes, you must include both the `name` and `value` parameters in your Query Predicate. The format `variants(attributes(value="value"))` (without the `name` parameter) is no longer accepted for new queries and will return a 400 Bad Request error. Existing queries using the old format continue to work but should be updated for better performance. For storefront applications with complex product filtering, use the [Product Search API](/api/projects/product-search.md) instead. For details on when to use each endpoint, see [Product Projections usage guidance](/api/projects/productProjections.md). Now let’s see how to create compound expressions with the SDK. Let’s run a request with a compound expression. Pay particular attention to the lines where the predicate is presented. The predicate we are using is based on the commercetools sample data that is downloaded with a trial Project. If you are using your own data, then you need to adjust the predicate to match your data set. ```TypeScript highlightLines="8" import { apiRoot } from "../impl/apiClient.js"; async function productPredicate() { try { const response = await apiRoot.productProjections().get({ queryArgs: { where: 'variants(attributes(name = "colorlabel" and value(en-GB = "Black")))', }, }); console.log("Summary", JSON.stringify(response.body, null, 2)); } catch (error) { console.log(JSON.stringify(error, null, 2)); } } productPredicate(); ``` ```java highlightLines="7" logger.info( "Products matching" + projectApiRoot .productProjections() .get() .withWhere( "variants(attributes(name = \"colorlabel\" and value(en-GB = \"Black\")))" ) .executeBlocking() .getBody() .getResults() .size() ); ``` Using the SDK you can easily combine multiple queries together. In the following examples, we will cover how this works in the Java and TypeScript SDK. You will see that there are some slight differences in how each SDK allows you to express predicates with multiple conditions. ```TypeScript highlightLines="10" import { apiRoot } from "../impl/apiClient.js"; async function productPredicate() { try { const response = await apiRoot .productProjections() .get({ queryArgs: { where: 'variants(attributes(name = "colorlabel" and value(en-GB = "Black")))', }, }) .execute(); console.log("Summary", JSON.stringify(response.body, null, 2)); } catch (error) { console.log(JSON.stringify(error, null, 2)); } } productPredicate(); ``` Using the Java SDK we can chain multiple conditions using `.withWhere()` and `.addWhere()`. This combines the predicates with an `and` operator. This predicate will be converted to this: `variants(attributes(name = "colorlabel" and value(en-GB = "Black")))` ```java highlightLines="6,7" logger.info( "Products available in Red: " + projectApiRoot .productProjections() .get() .withWhere("variants(attributes(name = \"color\"))") .addWhere("variants(attributes(value(en = \"red\")))") .executeBlocking() .getBody() .getResults() .size() ); ``` ## Input Variables Let’s move on to a different use case. Imagine you want to display the number of customers living in the same city as another customer. The solution here would involve creating a query that is based on the current Customer's city, and then you would need to query for the remaining Customers. The city in this example is a dynamic input. We can use [input variables](/api/predicates/query.md#input-variables) for dynamic inputs using the following syntax: ```bash field operator :variable&var.variable = value ``` Input variables are useful when you are creating a Query Predicate that uses a dynamic value supplied only at the time of running the query. For example, getting all Products from a Category where the Category will be selected by the user. ```bash category = :categoryId&var.categoryId = categoryId ``` You can even use multiple variables to check multiple values. See the following example: ```bash variants(sku in :skus)&var.skus=sku1&var.skus=sku2&var.skus=sku3 ``` This query is intended to retrieve multiple products where any of the variants match one of the SKUs provided in the `:skus` variable. Let’s break down this predicate to better understand it: - `variants(sku in :skus)`: this part of the query is checking for any Product Variants where the `sku` is in the list of SKUs provided by the `:skus` variable. - `&var.skus=sku1&var.skus=sku2&var.skus=sku3`: these are the predicate parameter values that define the `:skus` variable. Each `var.skus` parameter specifies an SKU value to be included in the search. The query will return products that have any of the specified SKUs (`sku1`, `sku2`, `sku3`) in their Product Variants. It uses the logical `in` operator, which matches any Product Variant that has an SKU within the provided list. Let’s apply this to a short example request and use the SDKs. We are interested in getting a count of Customers in the same city as the current Customer. In our example, we want to get a Customer’s ID for a Customer that has a default Shipping Address and update the variable `customerId`. ```TypeScript import { apiRoot } from "../impl/apiClient.js"; const customerId = "89288c06-4569-491a-a20f-5d87a4750346"; // Use your own customer Id async function customerCity(customerId) { try { const customerResponse = await apiRoot .customers() .withId(customerId) .get() .execute(); let cityName = customerResponse.body.addresses.find( (address) => address.id === customerResponse.body.defaultShippingAddressId ).city; const queryArgs = { where: 'addresses(city != :city)', 'var.city': cityName }; const customersResponse = await apiRoot .customers() .get({ queryArgs }) .execute(); console.log("Success", JSON.stringify(customersResponse.body.total, null, 2)); } catch (error) { console.log(JSON.stringify(error, null, 2)); } } customerCity(customerId); ``` ```java final String customerId = "{customer's id}"; final String cityName = projectApiRoot .customers() .withId(customerId) .get() .executeBlocking() .getBody() .withCustomer(customer -> customer .getAddresses() .stream() .filter(a -> a.getId().equals(customer.getDefaultShippingAddressId())) .findFirst() .orElse(null) .getCity() ); logger.info( "Customers in the same city : " + projectApiRoot .customers() .get() .withWhere("addresses(city = :city)") .addPredicateVar("city", cityName) .executeBlocking() .getBody() .getResults() .size() ); ``` ## Related pages - [Area overview page with navigation](/learning-developer-essentials.md) - [Previous page: Query Predicates](/learning-developer-essentials/api-queries-query-predicates/query-predicates.md) - [Next page: Improve query performance](/learning-developer-essentials/api-queries-query-predicates/improve-query-performance.md)