Search code examples
c#entity-frameworkgraphqlhotchocolate

Hot Chocolate: Transforming results from [UseFiltering] Query


I'm looking to use Hot Chocolate's Filtering to query against one data type; and then transform that filtered output to another type before returning it as an IQueryable. But I can't seem to find anyway to capture the filter input to start my transform.

Here's an example of what I'm trying to accomplish:

Given the data classes

public class TypeA
{
    public string Foo { get; set; }
}

public class TypeB
{
    public string Fizz { get; set; }
    public string Buzz { get; set; }
}

I want to be able to create a query endpoint like

public class Query
{
    [UseDbContext(typeof(DbContext))]
    [UseFiltering(typeof(TypeA))]
    public IQueryable<TypeB> GetTypeB(
        [ScopedService] DbContext context,
        [SomeAttributeToCaptureTheFilter] Filter filter) // <- this is the line I'm trying to figure out
    {
        IQueryable<TypeA> filteredTypeAs = context.TypeA.Filter(filter); // .Filter() doesn't exist, its just for example.
        IQueryable<TypeB> filteredTypeBs;
 
        /* Complex transformation logic that populates 'filteredTypeBs' 
         * requiring the 'filteredTypeAs' and additional Data from 
         * the database to complete. */

        return filteredTypeBs;
    }
}

Against which, I can use a GraphQL Query like the following

query {
  typeB(where: { foo: { eq: "bar" } }) {
    fizz
    buzz
  }
}

where: { foo: { eq: "bar" } } Being the filter against TypeA, and the

typeB {
  fizz
  buzz
} 

pulling the content from the transformed TypeB.


Using [UseFiltering(typeof(TypeA))] does work, It sets up the schema to act as I want.

What I'm looking for is something to the effect of the line [SomeAttributeToCaptureTheFilter] Filter filter. Just some way of capturing the filter and applying it to the data within the DbContext.

I will also say I'm very new to GraphQL in general, so how I'm approaching this problem may be entirely wrong. Any advice would be helpful.


Solution

  • Answering my own question for anyone who may stumble upon this in the future.

    The answer was in the HotChocolate.Data package. It contains Filter extensions for IQueryable<T> and IEnumerable<T>; that allow you to run the filter inline by passing an IResolverContext. I couldn't find references to any of this in the documentation, I came across it in an example about data aggregation: https://github.com/ChilliCream/hotchocolate/issues/924#issuecomment-921793977

    This is essentially what my final query method looked like:

    using HotChocolate.Data;
    using HotChocolate.Data.Filters.Expressions;
    
    public class Query
    {
        [UseDbContext(typeof(DbContext))]
        [UseFiltering(typeof(TypeA))]
        public IQueryable<TypeB> GetTypeB(
            [ScopedService] DbContext context,
            IResolverContext resolverContext)
        {
            IQueryable<TypeA> filteredTypeAs = context.TypeA.Filter(resolverContext);
            IQueryable<TypeB> filteredTypeBs;
     
            /* Complex transformation logic that populates 'filteredTypeBs' 
             * requiring the 'filteredTypeAs' and additional Data from 
             * the database to complete. */
    
            return filteredTypeBs;
        }
    }
    

    I know my example's a bit esoteric, but the point of this is that it opens up access to a filtered version of the results within the query method itself. This allows for a whole world of possibilities when it comes to data aggregation/manipulation; while still taking advantage of the HotChocolate's built in functionality.

    As a side note there also appears to be a Sort extension, which is the equivalent for [UseSorting].