# Filtering - Hot Chocolate

> Generate filter input types from .NET models with the [UseFiltering] attribute in Hot Chocolate, translating client filters into native database queries.

Canonical source: https://chillicream.com/docs/hotchocolate/fetching-data/filtering

Hot Chocolate generates filter input types from your .NET models and translates client-supplied filter arguments into native database queries. The default implementation builds expression trees that apply to `IQueryable`, but you can customize filters for other data sources.

Given a model like `User` with a `Name` property, Hot Chocolate generates a `UserFilterInput` with string operations such as `eq`, `contains`, `startsWith`, and more. Clients use the `where` argument to filter results.

## Getting Started

Filtering is part of the `HotChocolate.Data` package.

Bash

```
dotnet add package HotChocolate.Data
```

Warning

All `HotChocolate.*` packages need to have the same version.

Register filtering on the schema:

C#

```
builder
    .AddGraphQL()
    .AddFiltering();
```

Apply the `[UseFiltering]` attribute to a resolver that returns `IQueryable<T>` or `IEnumerable<T>`:

C#

```
[QueryType]
public static partial class UserQueries
{
    [UseFiltering]
    public static IQueryable<User> GetUsers(CatalogContext db)
        => db.Users;
}
```

Clients can now filter users:

GraphQL

```
query {
  users(where: { name: { contains: "Alice" } }) {
    name
    email
  }
}
```

Warning

**Middleware order matters.** When combining multiple middleware, apply them in this order: `UsePaging` \> `UseProjection` \> `UseFiltering` \> `UseSorting`.

## Filter Types

Hot Chocolate generates filter operations based on the .NET type of each property.

### String Filters

Operations: `eq`, `neq`, `contains`, `ncontains`, `in`, `nin`, `startsWith`, `nstartsWith`, `endsWith`, `nendsWith`.

### Boolean Filters

Operations: `eq`, `neq`.

### Comparable Filters

For numeric types (`int`, `long`, `float`, `double`, `decimal`), `Guid`, `DateTime`, `DateTimeOffset`, and `TimeSpan`.

Operations: `eq`, `neq`, `in`, `nin`, `gt`, `ngt`, `gte`, `ngte`, `lt`, `nlt`, `lte`, `nlte`.

### Enum Filters

Operations: `eq`, `neq`, `in`, `nin`.

### Object Filters

Filters are generated for nested objects, supporting filtering across database relationships:

C#

```
public class User
{
    public string Name { get; set; }
    public Address Address { get; set; }
}

public class Address
{
    public string City { get; set; }
}
```

GraphQL

```
query {
  users(where: { address: { city: { eq: "Berlin" } } }) {
    name
  }
}
```

### List Filters

For collection properties, Hot Chocolate generates `all`, `none`, `some`, and `any` operations:

GraphQL

```
query {
  users(where: { orders: { some: { total: { gt: 100 } } } }) {
    name
  }
}
```

## Combining Filters with "and" / "or"

Every filter input type includes `and` and `or` fields for composing multiple conditions:

GraphQL

```
query {
  users(
    where: {
      or: [{ name: { contains: "Alice" } }, { name: { contains: "Bob" } }]
    }
  ) {
    name
  }
}
```

The `or` field must be used at the top level of the filter. Placing it inside a field operation results in `and` semantics instead.

### Removing "and" / "or"

Disable these combinators in a custom filter type:

C#

```
public class UserFilterType : FilterInputType<User>
{
    protected override void Configure(IFilterInputTypeDescriptor<User> descriptor)
    {
        descriptor.AllowAnd(false).AllowOr(false);
    }
}
```

## Custom Filter Types

Customize which fields are filterable and which operations are available by extending `FilterInputType<T>`:

C#

```
public class UserFilterType : FilterInputType<User>
{
    protected override void Configure(IFilterInputTypeDescriptor<User> descriptor)
    {
        descriptor.BindFieldsExplicitly();
        descriptor.Field(f => f.Name);
        descriptor.Field(f => f.Email).Type<CustomStringOperationFilterInputType>();
    }
}
```

Restrict operations on a field by defining a custom operation type:

C#

```
public class CustomStringOperationFilterInputType : StringOperationFilterInputType
{
    protected override void Configure(IFilterInputTypeDescriptor descriptor)
    {
        descriptor.Operation(DefaultFilterOperations.Equals).Type<StringType>();
        descriptor.Operation(DefaultFilterOperations.Contains).Type<StringType>();
    }
}
```

Apply the custom filter type:

C#

```
[QueryType]
public static partial class UserQueries
{
    [UseFiltering(typeof(UserFilterType))]
    public static IQueryable<User> GetUsers(CatalogContext db)
        => db.Users;
}
```

## Filter Conventions

Filter conventions let you change filtering behavior globally across your schema.

### Setting Up a Convention

Extend `FilterConvention` and override `Configure`, or use `FilterConventionExtension` to build on top of the defaults:

C#

```
public class CustomFilterConvention : FilterConvention
{
    protected override void Configure(IFilterConventionDescriptor descriptor)
    {
        descriptor.AddDefaults();
        descriptor.ArgumentName("filter");
    }
}
```

C#

```
builder
    .AddGraphQL()
    .AddConvention<IFilterConvention, CustomFilterConvention>();
```

### Binding Filter Types Globally

Bind custom filter types to .NET types through the convention:

C#

```
public class CustomFilterConvention : FilterConvention
{
    protected override void Configure(IFilterConventionDescriptor descriptor)
    {
        descriptor.AddDefaults();
        descriptor.BindRuntimeType<User, UserFilterType>();
    }
}
```

### Custom Scalar Filters

When you use custom scalars (including those from `HotChocolate.Types.Scalars`), you must create and bind a filter type for each scalar:

C#

```
public class EmailAddressOperationFilterInputType : FilterInputType
{
    protected override void Configure(IFilterInputTypeDescriptor descriptor)
    {
        descriptor.Operation(DefaultFilterOperations.Equals).Type<EmailAddressType>();
        descriptor.Operation(DefaultFilterOperations.NotEquals).Type<EmailAddressType>();
        descriptor.Operation(DefaultFilterOperations.Contains).Type<EmailAddressType>();
    }
}
```

C#

```
builder
    .AddGraphQL()
    .AddFiltering(x => x
        .AddDefaults()
        .BindRuntimeType<string, EmailAddressOperationFilterInputType>());
```

## Next Steps

- **Need to sort results?** See [Sorting](https://chillicream.com/docs/hotchocolate/fetching-data/sorting).
- **Need to page through results?** See [Pagination](https://chillicream.com/docs/hotchocolate/fetching-data/pagination).
- **Need to optimize database queries?** See [Projections](https://chillicream.com/docs/hotchocolate/fetching-data/projections).
- **Need to protect against expensive filter queries?** See [Cost Analysis](https://chillicream.com/docs/hotchocolate/security/cost-analysis).
[Edit this page on GitHub](https://github.com/ChilliCream/graphql-platform/edit/main/website/content/docs/hotchocolate/fetching-data/filtering.md)

Last updated on **June 30, 2026** by **Tobias Tengler**
