javaspring-bootgraphqlgraphql-javagraphql-spqr

Filtering nested fields, or mapping types to queries


Excuse the probably-basic question. I've used GraphQL SPQR to implement a fetch of a product DTO like so:

@GraphQLQuery(name = "products")
public Page<ProductEntity> getProducts(@GraphQLArgument(name = "count", defaultValue = "10") int count, @GraphQLArgument(name = "offset") int offset) {
    Pageable queryPage = PageRequest.of(offset, count);
    return productRepository.findAll(queryPage);
}

Note that I'm paging through my data repository with this query.

My DTO's are set up in a way that I can query as follows:

products(count: 10, offset: 0) {
    store {
        products /* attempting to query using the count and offset parameters here is invalid*/ {
            productName
        }
    }
}

Under the second type (store), I am again fetching a list of products. How can I tell GraphQL to fetch the second, nested list of products in the same way that it would fetch the first list of products?

I'm imagining the ability to bind the GraphQLQuery to my ProductEntityDAO class or something like this, so that fetches against that type can all resolve in the same way.

Your assistance is appreciated.

EDIT:

Thanks Kaqqao. The solution worked perfectly, and I had the requirement to solve this for the general case of "An entity that has products".

To do that, I defined an Interface on my product entity that looks as follows:

@GraphQLInterface(name = "hasProducts", implementationAutoDiscovery = true)
public interface ProductEdge {
    Collection<ProductEntity> getProducts();
}

I then make my entity that has a connection to a list of products fetch it in a generalized way by implementing this interface on my entities that need to do this:

public class CommercialPartnerEntity implements ProductEntity.ProductEdge

And in my repository:

@Query("select child from CommercialPartnerEntity p inner join p.products child where p = :parent")
@GraphQLQuery(name = "relatedProducts")
Page<ProductEntity> findBy(@Param("parent") ProductEntity.ProductEdge parent, Pageable pageable);

Allowing me to, in my service, do things like this:

@GraphQLQuery(name = "productsList")
public Page<ProductEntity> getProducts(@GraphQLContext ProductEntity.ProductEdge hasProductsEntity, @GraphQLArgument(name = "count", defaultValue = "10") int count, @GraphQLArgument(name = "offset") int offset) {
    Pageable queryPage = PageRequest.of(offset, count);
    return productRepository.findBy(hasProductsEntity, queryPage);
}

And accordingly I have defined my resolver for any specific type in a generalized way. Would love to hear other people's thoughts on the solution.

When implementing things like "filter by name" I imagine this will come in very useful, too.


Solution

  • If I get you right, you're looking for a way to call an external method to resolve store.products. If that's the case, this is easy to achieve using @GraphQLContext.

    You could e.g. do something like:

    //Any class with queries
    public class ProductService {
    
        @GraphQLQuery(name = "products")
        public Page<ProductEntity> getProducts(@GraphQLContext Store store, @GraphQLArgument(name = "count", defaultValue = "10") int count, @GraphQLArgument(name = "offset") int offset) {
            //your logic here, maybe delegating to the existing getProducts method
        }
    }
    

    If Store already has getProducts, you might want to @GraphQLIgnore it, but not mandatory.

    If you're asking how to get the same arguments passed to products inside store.products, check my answer here. You can inject ResolutionEnvironment using @GraphQLEnvironment ResolutionEnvironment, and from there you can obtain DataFetchingEnvironment if you need it.