Search code examples
c#.netmongodbgraphqlhotchocolate

Hot Chocolate + Mongo: No additional call when I ask only for referene id


Let's assume that I have the following documents in mongo:

Product:

  • ...
  • productTemplateId
  • ...

Product Template:

  • id
  • ...

I mapped product to the following schema:

product {
 ...
 productTemplate {
  id,
  ...
 }
 ...
}
  1. I would like to avoid to make additional call when I ask only for product { productTemplate {id}} because this information is in the product entity. Is it possible?
  2. I would like to get from mongo only product with productTemplateId instead of whole document. It is significant because I have about 100 req/s. Is it possible to ask mongo only for expected fields from document?

This is part of my code:

public class ProductType : ObjectType<ProductDocument>
{
    protected override void Configure(IObjectTypeDescriptor<ProductDocument> descriptor)
    {
        descriptor.BindFieldsExplicitly();
        descriptor.Name("Product");            
        descriptor.Field("productTemplate")
            .Type<ProductTemplateType>()
            .ResolveWith<ProductTemplatesResolver>(r => r.GetProductTemplate(default!, default!, default));
    }
}

public class ProductTemplatesResolver
{
    public Task<ProductTemplateDocument> GetProductTemplate([Parent] ProductDocument product,
        ProductTemplatesBatchDataLoader loader,
        CancellationToken cancellationToken)
    {
        return loader.LoadAsync(product.ProductTemplateId, cancellationToken);
    }
}

public class ProductTemplatesBatchDataLoader : BatchDataLoader<int, ProductTemplateDocument>
{
    private readonly ProductsDbContext _dbContext;
    
    public ProductTemplatesBatchDataLoader(IBatchScheduler batchScheduler, ProductsDbContext dbContext, DataLoaderOptions? options = null) : base(batchScheduler, options)
    {
        _dbContext = dbContext;
    }

    protected override async Task<IReadOnlyDictionary<int, ProductTemplateDocument>> LoadBatchAsync(IReadOnlyList<int> keys, CancellationToken cancellationToken)
    {
        var pts = await _dbContext.ProductTemplates
            .AsQueryable()
            .Where(x => keys.Contains(x.Id))
            .ToListAsync(cancellationToken);

        return pts.ToDictionary(x => x.Id, x => x);
    }
}

Thanks


Solution

  • You can get the selected field over IResolverContext GetSelections

    public class ProductTemplatesResolver
    {
        public Task<ProductTemplateDocument> GetProductTemplate(
            [Parent] ProductDocument product,
            IResolverContext context,
            ProductTemplatesBatchDataLoader loader,
            CancellationToken cancellationToken)
        {
            var selectedFields = context.GetSelections((ObjectType)context.Selection.Field.Type);
            if (selectedFields.Count == 1 && selectedFields[0].Field.Name == "id")
            {
                return new ProductDocument(product.TemplateId);
            }
            return loader.LoadAsync(product.ProductTemplateId, cancellationToken);
        }
    }