diff --git a/website/src/docs/hotchocolate/fetching-data/filtering.md b/website/src/docs/hotchocolate/fetching-data/filtering.md index 0ea62df3666..1fe049099b4 100644 --- a/website/src/docs/hotchocolate/fetching-data/filtering.md +++ b/website/src/docs/hotchocolate/fetching-data/filtering.md @@ -5,7 +5,8 @@ title: Filtering # What is filtering With _Hot Chocolate_ filters, you can expose complex filter objects through your GraphQL API that translates to native database queries. The default filter implementation translates filters to expression trees that are applied to `IQueryable`. -Filters by default work on `IQueryable` but you can also easily customize them to use other interfaces. Hot Chocolate by default will inspect your .NET model and infer the possible filter operations from it. +Hot Chocolate by default will inspect your .NET model and infer the possible filter operations from it. +Filters use `IQueryable` (`IEnumerable`) by default, but you can also easily customize them to use other interfaces. The following type would yield the following filter operations: @@ -98,7 +99,7 @@ public class Query # Customization -Under the hood, filtering is based on top of normal Hot Chocolate input types. You can easily customize them with a very familiar fluent interface. The filter input types follow the same `descriptor` scheme as you are used to from the normal filter input types. Just extend the base class `FilterInputType` and override the descriptor method. +Under the hood, filtering is based on top of normal Hot Chocolate input types. You can easily customize them with a very familiar fluent interface. The filter input types follow the same `descriptor` scheme as you are used to from the normal input types. Just extend the base class `FilterInputType` and override the descriptor method. `IFilterInputTypeDescriptor` supports most of the methods of `IInputTypeDescriptor`. By default filters for all fields of the type are generated. If you do want to specify the filters by yourself you can change this behavior with `BindFields`, `BindFieldsExplicitly` or `BindFieldsImplicitly`. @@ -525,7 +526,7 @@ public class CustomConvention } services.AddGraphQLServer() - .AddConvention(); + .AddConvention(); // or services.AddGraphQLServer() .AddConvention(new FilterConvention(x => x.AddDefaults())) @@ -543,7 +544,7 @@ public class CustomConventionExtension } services.AddGraphQLServer() - .AddConvention(); + .AddConvention(); // or services.AddGraphQLServer() .AddConvention(new FilterConventionExtension(x => /*config*/)) diff --git a/website/src/docs/hotchocolate/fetching-data/sorting.md b/website/src/docs/hotchocolate/fetching-data/sorting.md index 63b66a41e10..29f78dd5cd7 100644 --- a/website/src/docs/hotchocolate/fetching-data/sorting.md +++ b/website/src/docs/hotchocolate/fetching-data/sorting.md @@ -1,85 +1,383 @@ --- -title: "Sorting" +title: Sorting --- -Use this section as an introduction to explain what a reader can expect of this document. +# What is sorting -# Headlines +Ordering results of a query dynamically is a common case. With _Hot Chocolate_ sorting, you can expose a sorting argument, that abstracts the complexity of ordering logic. +With little configuration your GraphQL API has sorting capabilities which translates to native database queries. +The default sort implementation translates sorting statements to expression trees that are applied to `IQueryable`. +Hot Chocolate by default will inspect your .NET model and infer the possible filter operations from it. +Sorting uses `IQueryable` (`IEnumerable`) by default but you can also easily customize them to use other interfaces. -Use headlines to separate a document into several sections. First level headlines will appear in the left hand navigation. This will help the reader to quickly skip sections or jump to a particular section. +The following type would yield the following sorting operation -# Use Diagrams +```csharp + public class User + { + public string Name { get; set; } + + public Address Address { get; set; } + } + + public class Address + { + public string Street { get; set; } + } + +``` + +```sdl +type Query { + users(order: [UserSortInput]): [User] +} + +type User { + name: String! + address: Address! +} + +input AddressSortInput { + street: SortEnumType +} + +input UserSortInput { + name: SortEnumType + address: AddressSortInput +} + +enum SortEnumType { + ASC + DESC +} +``` + +# Getting started + +Sorting is part of the `HotChocolate.Data` package. You can add the dependency with the `dotnet` cli + +``` + dotnet add package HotChocolate.Data +``` + +To use sorting you need to register it on the schema: + +```csharp +services.AddGraphQLServer() + // Your schmea configuration + .AddSorting(); +``` + +Hot Chocolate will infer the sorting types directly from your .Net Model and then use a Middleware to apply the order to `IQueryable` or `IEnumerable` on execution. + +> ⚠️ **Note:** If you use more than middleware, keep in mind that **ORDER MATTERS**. The correct order is UsePaging > UseProjections > UseFiltering > UseSorting + + +**Code First** + +```csharp +public class QueryType + : ObjectType +{ + protected override void Configure(IObjectTypeDescriptor descriptor) + { + descriptor.Field(t => t.GetPersons(default)).UseSorting(); + } +} + +public class Query +{ + public IQueryable GetPersons([Service]IPersonRepository repository) => + repository.GetPersons(); +} +``` -Use [mermaid diagrams](https://mermaid-js.github.io/mermaid) to help a reader understand complex problems. Jump over to the [mermaid playground](https://mermaid-js.github.io/mermaid-live-editor) to test your diagrams. +**Pure Code First** -```mermaid -graph TD - A[Christmas] -->|Get money| B(Go shopping) - B --> C{Let me think} - C -->|One| D[Laptop] - C -->|Two| E[iPhone] - C -->|Three| F[fa:fa-car Car] +The field descriptor attribute `[UseSorting]` does apply the extension method `UseSorting()` on the field descriptor. + +```csharp +public class Query +{ + [UseSorting] + public IQueryable GetPersons([Service]IPersonRepository repository) + { + repository.GetPersons(); + } +} ``` -# Use Code Examples +# Customization -A code example is another tool to help readers following the document and understanding the problem. Here is an list of code blocks that are used often with the ChilliCream GraphQL platform. +Under the hood, sorting is based ontop of normal Hot Chocolate input types. You can easily customize them with a very familiar fluent interface. The sorting input types follow the same `descriptor` scheme as you are used to from the normal input types. Just extend the base class `SortInputType` and override the descriptor method. -Use `sdl` to describe GraphQL schemas. +`ISortInputTypeDescriptor` supports most of the methods of `IInputTypeDescriptor`. By default, operations are generated for all fields of the type. +Members that are collections are skipped because you cannot order based on lists. +If you do want to specify the sorting types by yourself you can change this behavior with `BindFields`, `BindFieldsExplicitly` or `BindFieldsImplicitly`. +When fields are bound implicitly, meaning sorting is added for all valid properties, you may want to hide a few fields. You can do this with `Ignore(x => Bar)`. +It is also possible to customize the GraphQL field of the operation further. You can change the name, add a description or directive. +```csharp +public class UserSortType + : SortInputType +{ + protected override void Configure(ISortInputTypeDescriptor descriptor) + { + descriptor.BindFieldsExplicitly(); + descriptor.Field(x => x.Name).Name("custom_name"); + } +} +``` + +If you want to change the sorting operations on a field, you need to declare you own operation enum type. + +```csharp{7} +public class UserSortType + : SortInputType +{ + protected override void Configure(ISortInputTypeDescriptor descriptor) + { + descriptor.BindFieldsExplicitly(); + descriptor.Field(x => x.Name).Type(); + } +} + +public class AscOnlySortEnumType + : DefaultSortEnumType +{ + protected override void Configure(ISortEnumTypeDescriptor descriptor) + { + descriptor.Operation(DefaultSortOperations.Ascending); + } +} + +``` ```sdl -type Author { +type Query { + users(order: [UserSortInput]): [User] +} + +type User { name: String! + address: Address! +} + +input UserSortInput { + name: AscOnlySortEnumType +} + +enum AscOnlySortEnumType { + ASC } ``` -Use `graphql` to describe GraphQL operations. +To apply this sorting type we just have to provide it to the `UseSorting` extension method with as the generic type argument. -```graphql -query { - author(id: 1) { - name - } +**Code First** +```csharp +public class QueryType + : ObjectType +{ + protected override void Configure(IObjectTypeDescriptor descriptor) + { + descriptor + .Field(t => t.GetUser(default)) + .UseSorting() + } } ``` +**Pure Code First** +```csharp +public class Query +{ + [UseSorting(typeof(UserSortType))] + public IQueryable GetUsers([Service]IUserRepository repository) + { + repository.GetUsers(); + } +} +``` + +# Sorting Conventions + +If you want to change the behavior sorting globally, you want to create a convention for sorting. The sorting convention comes with a fluent interface that is close to a type descriptor. -Use `json` for everything JSON related for example a GraphQL result. +## Get Started -```json +To use a sort convention you have to extend `SortConvention` and override the `Configure` method. Alternatively, you can directly configure the convention over the constructor argument. +You then have to register your custom convention on the schema builder with `AddConvention`. +By default a new convention is empty. To add the default behaviour you have to add `AddDefaults`. + +```csharp +public class CustomConvention + : SortConvention { - "data": { - "author": { - "name": "ChilliCream" + protected override void Configure(ISortConventionDescriptor descriptor) + { + descriptor.AddDefaults(); } - } } + +services.AddGraphQLServer() + .AddConvention(); +// or +services.AddGraphQLServer() + .AddConvention(new Convention(x => x.AddDefaults())) ``` -Use `sql` for SQL queries. +Often you just want to extend the default behaviour of sorting. If this is the case, you can also use `SortConventionExtension` +```csharp +public class CustomConventionExtension + : SortConventionExtension +{ + protected override void Configure(ISortConventionDescriptor descriptor) + { + // config + } +} -```sql -SELECT id FROM Authors WHERE id = 1 +services.AddGraphQLServer() + .AddConvention(); +// or +services.AddGraphQLServer() + .AddConvention(new FilterConventionExtension(x => /*config*/)) ``` -Use `csharp` for C# code. +## Argument Name + +With the convention descriptor, you can easily change the argument name of the `FilterInputType`. + +**Configuration** ```csharp -public interface Author +descriptor.ArgumentName("example_argument_name"); +``` + +**Result** + +```sdl +type Query { + users(example_argument_name: [UserSortInput]): [User] +} +``` + +## Binding of SortTypes + +`SortInputType`'s **cannot** just be registered on the schema. You have to bind them to the runtime type on the convention. + +### SortInputType bindings +By default only the `string` type is bound explicitly. If you want to configure sorting globally you are free to bind additional types. + +**Configuration** +```csharp +public class CustomSortInputType + : SortInputType +{ + protected override void Configure(ISortInputTypeDescriptor descriptor) + { + descriptor.Name("CustomSortInputType"); + } +} +public class CustomConvention + : SortConvention { - int Id { get; } + protected override void Configure(ISortConventionDescriptor descriptor) + { + descriptor.AddDefaults().BindRuntimeType(); + } +} +``` - string Name { get; } +**Result** + +```sdl + +type Query { + users(order: [CustomSortInputType!]): [User] +} + +type User { + name: String! } + +input CustomSortInputType { + name: SortEnumType +} + +enum SortEnumType { + ASC + DESC +} +``` + +### Default bindings + +For fields all fields where no explicit binding is found, a default is applied. This default is `DefaultSortEnumType`. +This can be configured with the method `DefaultBinding`. + + +**Configuration** + +```csharp +public class CustomConvention + : SortConvention +{ + protected override void Configure(ISortConventionDescriptor descriptor) + { + descriptor.AddDefaults().DefaultBinding(); + } +} +``` + +**Result** + +```sdl +type Query { + users(order: [UserSortInput]): [User] +} + +type User { + logonCount: Int! +} + +input UserSortInput { + logonCount: AscOnlySortEnumType +} + +enum AscOnlySortEnumType { + ASC +} +``` +## Extend Types +### SortEnumType +When you build extensions for sorting, you may want to modify or extend the `DefaultSortEnumType`. + +```csharp +descriptor.ConfigureEnum( + x => x.Operaion(CustomOperations.NULL_FIRST).Name("NULL_FIRST)); ``` -# Use Images +```sdl +enum SortEnumType { + ASC + DESC + NULL_FIRST +} +``` -When using images make sure it's a PNG file which is at least 800 pixels wide. -# Use Tables +### SortType +In case you want to change a specific sort type you can do this too. +You can use `Configure()` to alter the configuration of a type. -When using tables make sure you always use titles. +```csharp +descriptor.Configure( + x => x.Description("This is my custome description")); +``` -| Name | Description | -| ----------- | ------------------ | -| ChilliCream | A GraphQL platform | +```sdl +"This is my customer description" +input CustomSortInputType { + name: SortEnumType +} +```