# Filtering and Searching In API

We can do effective filtering, searching and sorting in the following ways -&#x20;

### **1. Using Query Parameters**

Query parameters in a REST API are key-value pairs appended to the URL after a `?`. For filtering and searching:

* **Standardized Syntax**: Use clear and intuitive names for query parameters:
  * `field=value`: Simple equality filter (e.g., `/users?role=admin`).
  * `field[operator]=value`: For advanced filtering using operators (`[gt]`, `[lt]`, `[gte]`, `[lte]`, etc.):
  * Combine filters with logical operators like `AND` or `OR`.
* **Consistent Format**: Ensure naming conventions are consistent:
  * Use camelCase (`userName`, `minPrice`) or snake\_case (`user_name`, `min_price`) based on your API style guide.
* **Reserved Characters**: Encode special characters properly to avoid parsing issues.

The simplest way to implement filtering is by using query parameters in the URL. Spring Boot makes it easy to handle query parameters with `@RequestParam`&#x20;

### **2: Using Specifications (Dynamic Queries)**

For more complex filtering, you can use **Specifications** with **JPA Criteria API**. This approach is flexible and allows you to build dynamic queries.

```java
public class SearchCriteria {
    private String keyword;
    private String fieldName;
    private String operator; // e.g., EQUAL, GREATER_THAN
    private Object value;

    // Getters and Setters
}



public class GenericSpecification<T> implements Specification<T> {
    private SearchCriteria criteria;

    @Override
    public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
        if (criteria.getOperator().equalsIgnoreCase("EQUAL")) {
            return builder.equal(root.get(criteria.getFieldName()), criteria.getValue());
        }
        if (criteria.getOperator().equalsIgnoreCase("GREATER_THAN")) {
            return builder.greaterThan(root.get(criteria.getFieldName()), criteria.getValue().toString());
        }
        // Add other conditions here
        return null;
    }
}

```

{% hint style="info" %}
For more details info see [Specification](/gyan/jpa/specification.md)
{% endhint %}

### **3. Combining Filtering and Searching**

We can combine filtering and searching in a single endpoint to provide a powerful querying mechanism.

Ex.   `/products?category=electronics&price[gt]=100&brand=Samsung`

Using spring data specification we can combine multiple specifications -&#x20;

`Specification spec = Specification.where(spec1).and(spec2).or(spec3);`

### 4. Pagination and Sorting

We can utilize effective pagination and sorting based on our need to display properly ordered data in chunks.

* **Query Parameter for Sorting**: Use a `sort` query parameter:

  ```bash
  /products?sort=price,desc
  ```

  * `field`: Field to sort by (e.g., `price`).
  * `direction`: Sorting direction (`asc` or `desc`).
* **Multiple Sort Fields**: Support sorting by multiple fields:

  ```bash
  /products?sort=price,asc&sort=name,desc

  ```
* **Spring Data PagingAndSortingRepository**: Use the `Pageable` interface:

  ```java
  @GetMapping("/products")
  public Page<Product> getProducts(Pageable pageable) {
      return productRepository.findAll(pageable);
  }
  ```

  Example `Pageable` request:

  ```bash
  bashCopyEdit/products?page=0&size=10&sort=price,desc
  ```

#### Example -

1. **Simple  -**

   &#x20;Filter record based on exact match

   1. `GET /api/users?role=admin`

2. **Rangle Filters**&#x20;

   Support range queries using operators like `gt`, `lt`, `gte`, `lte`.

   1. `GET  /api/products?price[gt]=100&price[lte]=500`

3. **Multiple Filters**&#x20;

   1. `GET  /api/orders?status=delivered&date[gte]=2023-01-01&date[lte]=2023-12-31`

4. **Full Text Search**&#x20;

   1. `GET /api/books?search=Spring Boot`

5. **Logical Operators**

   1. `GET  /api/movies?filter=(genre=action AND rating[gte]=8) OR (director=Nolan)`

6. **Sorting**

   1. `GET /api/products?sort=price,asc`
   2. `GET /api/products?sort=price,desc&sort=name,asc`

7. **Pagination**

   1. `GET /api/users?page=1&size=10`

8. **Filtering on Nested or Related Fields**

   1. `GET /api/orders?customer.name=John&product.category=electronics`

   Retrieves orders where the `customer`’s name is `John` and the `product` belongs to the `electronics` category.

9. **Case-Insensitive Search**

   1. `GET  /api/users?email=example@example.com`

   Matches `example@example.com`, `Example@example.com`, etc.

10. **Include/Exclude Fields**

    1. `GET  /api/users?fields=id,name,email`

    Includes only the `id`, `name`, and `email` fields in the response.

11. **Reserved Characters Handling**

    Ensure special characters in query parameters are encoded.

    1. `/products?name=Spring%20Boot&description=advanced%2Fbeginner`

    Retrieves products where the `name` is `Spring Boot` and the `description` contains `advanced/beginner`.

```java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.web.bind.annotation.*;
import java.util.List;

@RestController
@RequestMapping("/api")
public class ProductController {

    @Autowired
    private ProductService productService;

    // 1. Simple Equality Filters
    @GetMapping("/users")
    public List<User> getUsersByRole(@RequestParam(required = false) String role) {
        return productService.getUsersByRole(role);
    }

    // 2. Range Filters
    @GetMapping("/products")
    public List<Product> getProductsByPriceRange(
        @RequestParam(required = false) Double priceGt,
        @RequestParam(required = false) Double priceLte
    ) {
        return productService.getProductsByPriceRange(priceGt, priceLte);
    }

    // 3. Full-Text Search
    @GetMapping("/books")
    public List<Book> searchBooks(@RequestParam(required = false) String search) {
        return productService.searchBooks(search);
    }

    // 4. Logical Operators
    @GetMapping("/movies")
    public List<Movie> getMoviesWithFilters(
        @RequestParam(required = false) String filter
    ) {
        return productService.getMoviesWithFilters(filter);
    }

    // 5. Sorting
    @GetMapping("/products/sorted")
    public List<Product> getSortedProducts(
        @RequestParam(required = false, defaultValue = "id,asc") String[] sort
    ) {
     List<Sort.Order> orders = new ArrayList<>();
    
    if (sort[0].contains(",")) {
        // sort=[field,direction] format
        for (String sortOrder : sort) {
            String[] _sort = sortOrder.split(",");
            orders.add(new Sort.Order(
                getSortDirection(_sort[1]), 
                _sort[0]));
        }
    } else {
        // sort=[field] format
        orders.add(new Sort.Order(
            getSortDirection("asc"), 
            sort[0]));
    }
    
    Pageable pageable = PageRequest.of(page, size, Sort.by(orders));
    
    return ResponseEntity.ok(
        productService.findProducts(name, minPrice, maxPrice, 
                                  categories, pageable));
    }

    // 6. Pagination
    @GetMapping("/users/paginated")
    public Page<User> getUsersWithPagination(Pageable pageable) {
        return productService.getUsersWithPagination(pageable);
    }

    // 7. Nested/Related Field Filtering
    @GetMapping("/orders")
    public List<Order> getOrdersByCustomerAndCategory(
        @RequestParam(required = false) String customerName,
        @RequestParam(required = false) String productCategory
    ) {
        return productService.getOrdersByCustomerAndCategory(customerName, productCategory);
    }

    // 8. Case-Insensitive Search
    @GetMapping("/users/search")
    public List<User> getUsersByEmail(@RequestParam String email) {
        return productService.getUsersByEmail(email);
    }

    // 9. Include/Exclude Fields
    @GetMapping("/users/fields")
    public List<User> getUsersWithSelectedFields(@RequestParam List<String> fields) {
        return productService.getUsersWithSelectedFields(fields);
    }
}

```

#### **Summary**

* **Filtering**: Use query parameters or Specifications to narrow down results.
* **Searching**: Implement simple or advanced search using query parameters or Specifications.
* **Combining Filtering and Searching**: Provide a unified endpoint for flexible querying.
* **Pagination and Sorting**: Improve performance and usability by paginating and sorting results.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://wisdom.gitbook.io/gyan/core/rest-apis-design/filtering-and-searching-in-api.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
