4

I'm working on a GraphQL -> SQL parser that includes some joins, so it makes a performance difference whether a certain field is requested. Is there a way to find that out?

I'm learning about object types, so I think it might have something to do with setting a resolver on it. But a resolver works at the level of the field that's being requested independently of other things. Whereas I'm trying to figure out on the top-most Query level which fields have been requested in the GraphQL query. That will shape the SQL query.

public class QueryType : ObjectType<Query>
{
    protected override void Configure(IObjectTypeDescriptor<Query> descriptor)
    {
        descriptor
            .Field(f => f.GetACUMonthlySummary(default!, default!, default!, default!, default!, default!))
            .Type<ListType<ACUMonthlySummaryType>>();
    }
}

I saw related questions for js, but didn't find any examples specifically in C# and HotChocolate, which is what we're using.

Oleksiy
  • 37,477
  • 22
  • 74
  • 122
  • 1
    [SingletonSean](https://www.youtube.com/c/SingletonSean/playlists) has a series of videos about [GraphQL](https://www.youtube.com/playlist?list=PLA8ZIAm2I03g9z705U3KWJjTv0Nccw9pj) using HotChocolate – McNets Apr 21 '22 at 17:49
  • @McNets Thanks for the suggestion! Have you gone through those videos? I've already completed the first 4 (Entity Framework is next) and didn't see anything that addresses my question so far. – Oleksiy Apr 22 '22 at 22:57
  • No, but Sean has a lot of other interesting videos about WPF – McNets Apr 23 '22 at 09:28
  • I made a [C# HotChocolate generator](https://github.com/MeaningOfLights/dB2GraphQL.Net) though from what I read doing JOINs in SQL isn't the way it works. Please correct me if I am wrong because this single problem defeats the point of using a RDBMS for a GraphQL data source. – Jeremy Thompson May 02 '22 at 06:31

3 Answers3

3

I'm not sure if this is recommended, so I appreciate feedback, but I found the following way to list all selected nodes:

  1. Inject IResolverContext context (using HotChocolate.Resolvers) as one of the parameters in the query.
  2. context.Selection.SyntaxNode.SelectionSet.Selections gives an IEnumerable<ISelectionNode>. That contains exactly the fields the user has selected.
Oleksiy
  • 37,477
  • 22
  • 74
  • 122
1

Say for example(A simple one) you have a class called employee and it has FirstName and LastName properties. You may want want the GraphQL endpoint to expose a FullName field for the employee that will internally concatenate the first and last name. Note that FirstName and LastName values exist as columns of the Employee database table but the FullName field will be derived.

public class EmployeeType : ObjectType<Employee> {
    protected override void Configure(IObjectTypeDescriptor<Employee> descriptor) {
        descriptor.Field(@"FullName")
            .Type<StringType>()
            .ResolveWith<Resolvers>( p => p.GetFullName(default!, default!) )
            .UseDbContext<AppDbContext>()
            .Description(@"Full name of the employee");
    }

    private class Resolvers {
        
        public string GetFullName([Parent] Employee e, [ScopedService] AppDbContext context) {
            return e.FirstName + " " + e.LastName;
        }
    }
}

I'm pretty sure you'd have to annotate the Employee using the ParentAttribute.

Learn more about this in the resolver documentation

David Kariuki
  • 1,522
  • 1
  • 15
  • 30
  • 1
    Thanks for your answer, but I'm not sure how this would tell me if the user has requested FirstName and/or LastName in their query. Suppose it's a heavy calculation to get some field, and we only want to perform it if the user explicitly listed that particular field in their GraphQL query, and ignore it otherwise. – Oleksiy May 02 '22 at 18:48
0

Just add IResolverContext parameter on query method if you are using Annotation based approach.

 public async Task<Form> GetForm(long id, string organisationId, [Service] IFormRepository formRepository,
    IResolverContext context)
{
    //context.Selection.SyntaxNode.SelectionSet.Selections
    var form = await formRepository.GetFormById(id, organisationId);
    return new Form
    {
        Id = form.Id,
        Name = form.Name,
        ShortUrl = form.ShortUrl,
        PublishStatus = form.PublishStatus,
        Description = form.Description,
        OrganisationId = form.OrganizationId
    };
}

Get fields by using context.Selection.SyntaxNode.SelectionSet.Selections