Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add example for adding basic content type filters #16111

Merged
merged 21 commits into from
Jun 4, 2024
Merged
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
d53d2df
Add example for adding basic content type filters
PUXMKU May 20, 2024
7f3695c
fix nesting
PUXMKU May 20, 2024
07ed9ba
Update src/docs/reference/core/Apis.GraphQL.Abstractions/README.md
MikeKry May 21, 2024
8ed0019
Update src/docs/reference/core/Apis.GraphQL.Abstractions/README.md
MikeKry May 21, 2024
742be79
Update src/docs/reference/core/Apis.GraphQL.Abstractions/README.md
MikeKry May 21, 2024
3ecdf6a
Update docs, clarify use cases
MikeKry May 21, 2024
790ea34
Update src/docs/reference/core/Apis.GraphQL.Abstractions/README.md
MikeKry May 31, 2024
5c857ce
Update src/docs/reference/core/Apis.GraphQL.Abstractions/README.md
MikeKry May 31, 2024
9732c8a
Update src/docs/reference/core/Apis.GraphQL.Abstractions/README.md
MikeKry May 31, 2024
79f1479
Update src/docs/reference/core/Apis.GraphQL.Abstractions/README.md
MikeKry May 31, 2024
a324117
Update src/docs/reference/core/Apis.GraphQL.Abstractions/README.md
MikeKry May 31, 2024
6f4913a
Update src/docs/reference/core/Apis.GraphQL.Abstractions/README.md
MikeKry May 31, 2024
75afa18
Update src/docs/reference/core/Apis.GraphQL.Abstractions/README.md
MikeKry May 31, 2024
2a1312d
apply suggestions
PUXMKU May 31, 2024
c3ec127
little tweaks
PUXMKU Jun 1, 2024
162dd1a
Update src/docs/reference/core/Apis.GraphQL.Abstractions/README.md
hishamco Jun 4, 2024
2979549
Update src/docs/reference/core/Apis.GraphQL.Abstractions/README.md
hishamco Jun 4, 2024
58dc86f
Update src/docs/reference/core/Apis.GraphQL.Abstractions/README.md
hishamco Jun 4, 2024
5cb91bb
Update src/docs/reference/core/Apis.GraphQL.Abstractions/README.md
hishamco Jun 4, 2024
c2daca3
Update src/docs/reference/core/Apis.GraphQL.Abstractions/README.md
hishamco Jun 4, 2024
a7a905c
Merge branch 'main' into update-graphql-docs
hishamco Jun 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 144 additions & 3 deletions src/docs/reference/core/Apis.GraphQL.Abstractions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,20 @@ public class Startup : StartupBase

Thats it, your part will now be exposed in GraphQL... just go to the query explorer and take a look. Magic.

### Define a query filter type
## Filtration

### Define a custom query filter type

So now you have lots of data coming back, the next thing you want to do is to be able to filter said data.

We follow a similar process from step #1, so at this point I will make the assumption you have implemented step #1.

What we are going to cover here is;
Use this approach if you:

- want to add a new filter on Content-Type queries,
- need to use custom logic for filtering. For example, fetching data from a service, or comparing complex objects.

What we are going to cover here is:

1. Implement an Input type.
2. Register it in Startup class.
Expand Down Expand Up @@ -127,7 +134,7 @@ When an input part is registered, it adds in that part as the parent query, in t
}
```

Next we want to implement a filter. The filter takes the input from the class we just built and the above example, and performs the actual filter against the object passed to it.
Next, we want to implement a filter. The filter takes the input from the class we just built and the above example, and performs the actual filter against the object passed to it. Note that `GraphQLFilter` also provides `PostQueryAsync` that can be used in other use cases too, like checking permissions.

```csharp
public class AutoroutePartGraphQLFilter : GraphQLFilter<ContentItem>
Expand Down Expand Up @@ -176,6 +183,140 @@ Shown in the example above, we have an autoroutePart argument, this is registere

Done.

### Using default Content-Type query filters

In the previous section, we demonstrated how to create filters for complex requirements, allowing you to create custom filtration methods. However, in case you need to add a simple filter on Content-Type queries, there is also a simpler solution.

Use this approach if you:

* want to add a new filter on Content-Type queries,
* will have a database index with data for your filters,
* you can use simple comparison (equals, contains, in...) against index values. For example, `AutoroutePartIndex.Path = filterValue`.

We will cover:

1. Implementing a `WhereInputObjectGraphType`.
2. Implementing `IIndexAliasProvider`.
3. Registering it in the `Startup` class.

#### Implementing WhereInputObjectGraphType

The `WhereInputObjectGraphType` enhances the `InputObjectGraphType` by introducing methods to define filters such as equality, substrings, or array filters. Inheriting from `WhereInputObjectGraphType` is essential since it's the expected type for the `ContentItemsFieldType` that is responsible for the filtering logic.

Here is an example implementation:

```csharp
// Assuming we've added the necessary using directives.
// It is essential to inherit from WhereInputObjectGraphType.
// Do not use the InputObjectGraphType type as it will not be
// handled by default ContentItem queries.
public class AutorouteInputObjectType : WhereInputObjectGraphType<AutoroutePart>
{
// Binds the filter fields to the GraphQL type representing AutoroutePart
public AutorouteInputObjectType()
{
Name = "AutoroutePartInput";

// Utilize the method for adding scalar fields from the base class.
AddScalarFilterFields<StringGraphType>("path", S["Filter by the path of the content item"]);
}
}
```

This method will add scalar filters to all ContentItem queries, including your own custom Content Types. Scalar filters include following:

1. equals, not equals
2. contains, not contains
3. starts with, ends with, not starts with, not ends with
4. in, not in

These filters are checked against an index that is bound to the given ```ContentPart```.

#### Implementing IIndexAliasProvider

To bind ```ContentPart``` to an Index, you have to implement ```IIndexAliasProvider```. Ensure that field names in your filter object are the same as fields in the index. It is needed for filter automatching.

```csharp
public class AutoroutePartIndexAliasProvider : IIndexAliasProvider
{
private static readonly IndexAlias[] _aliases =
[
new IndexAlias
{
Alias = "autoroutePart", // alias of graphql ContentPart. You may also use nameof(AutoroutPart).ToFieldName()
Index = nameof(AutoroutePartIndex), // name of index bound to part - keep in mind, that fields need to correspond. E.g. 'path' has the same name in the index and part.
IndexType = typeof(AutoroutePartIndex)
}
];

public IEnumerable<IndexAlias> GetAliases()
{
return _aliases;
}
}
```

#### Updating the Startup Class

Update Startup class like below.

```csharp
[RequireFeatures("OrchardCore.Apis.GraphQL")]
public class Startup : StartupBase
{
// Assuming we've added the necessary using directives.
public override void ConfigureServices(IServiceCollection services)
{
// Code to register the AutoroutePart and AutorouteQueryObjectType is assumed to be present.
// Register WhereInputObjectGraphType
services.AddInputObjectGraphType<AutoroutePart, AutorouteInputObjectType>();

// Register IIndexAliasProvider
services.AddTransient<IIndexAliasProvider, AutoroutePartIndexAliasProvider>();
services.AddWhereInputIndexPropertyProvider<AutoroutePartIndex>();
}
}
```
MikeKry marked this conversation as resolved.
Show resolved Hide resolved
With these configurations, you can navigate to your GraphQL interface, and you should see the new filters available for use in all Content type queries.

#### Example Query Filters

Below are the resulting query filters applied to an autoroutePart:

```json
{
person(where: {path: {path_contains: "", path: "", path_ends_with: "", path_in: "", path_not: "", path_not_contains: "", path_not_ends_with: "", path_not_in: "", path_not_starts_with: "", path_starts_with: ""}}) {
name
}
}
```

Alternatively, if you register the part with ```collapse = true```, fields will not be nested inside object:

```json
{
person(where: {path_contains: "", path: "", path_ends_with: "", path_in: "", path_not: "", path_not_contains: "", path_not_ends_with: "", path_not_in: "", path_not_starts_with: "", path_starts_with: ""}) {
name
}
}
```

For a more detailed understanding, refer to the implementation of [WhereInputObjectGraphType](https://github.com/OrchardCMS/OrchardCore/blob/main/src/OrchardCore/OrchardCore.Apis.GraphQL.Abstractions/Queries/WhereInputObjectGraphType.cs) and [ContentItemFieldsType](https://github.com/OrchardCMS/OrchardCore/blob/main/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/ContentItemsFieldType.cs). Also, you can check the [`AutoroutePartIndex`](https://github.com/OrchardCMS/OrchardCore/blob/main/src/OrchardCore/OrchardCore.Autoroute.Core/Indexes/AutoroutePartIndex.cs) that was used for examples.

### Using arguments for query filtering

There is also the possibility to utilize query arguments and use them for filtering query results, or customizing query output inside the `Resolve` method. For more information, visit the [GraphQL documentation](https://graphql-dotnet.github.io/docs/getting-started/arguments/).

Use this approach if you:

* want to add a new filter on any type of query, content part, or field,
* or will use custom logic for filtration.
* or you need to switch data sources, or logic, based on the argument's value

Orchard Core's implementation of a filtering query by an argument can be seen in [`ContentItemQuery`](https://github.com/OrchardCMS/OrchardCore/blob/main/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/ContentItemQuery.cs) or `MediaAssetQuery`.

Orchard Core's implementation of applying an argument on a field can be seen in `MediaFieldQueryObjectType`.

## Querying related content items

One of the features of Content Items, is that they can be related to other Content Items.
Expand Down