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

@BatchMapping support @Argument #232

Closed
dugenkui03 opened this issue Dec 22, 2021 · 9 comments
Closed

@BatchMapping support @Argument #232

dugenkui03 opened this issue Dec 22, 2021 · 9 comments

Comments

@dugenkui03
Copy link
Contributor

dugenkui03 commented Dec 22, 2021

I think that both source and argument are necessary. It will great that @BatchingMapping support @Argument.

Schema like blow. 'like' means whether the visitor like this book or author, and Book.author bind DataLoader.

type Query{
      books(visitorId:Int, bookIds:[Int]):[Book]
}

type Book{
    id: Int
    like: Boolean
    authorId: Int
    author(visitorId:Int): Author
}

type Author{
    id: Int
    name: String
    like: Boolean
}

query like blow:

query ($visitorId: Int){
   books(visitorId:$visitorId, bookIds:[1,2,3]){
      authorId
      # need visitorId
       author(visitorId:$visitorId){
             id
       }
  }
}
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Dec 22, 2021
@pinguinjkeke
Copy link

Any current workarounds for this?

@rstoyanchev
Copy link
Contributor

rstoyanchev commented Feb 8, 2022

A @BatchMapping method is a BatchLoader and that has access to BatchLoaderEnvironment which doesn't provide access to arguments. So there is no straight-forward way to do this.

More generally, my understanding is that a BatchLoader is just a contract for loading Objects given their id's and it could be invoked by GraphQL Java for different parts of the query which may have different arguments and different selection sets. So this is at a lower level where arguments as well as caching of values can't apply in a straight-forward way.

Note a @BatchMapping method has access to the same GraphQLContext instance as do @SchemaMapping methods, so you could pass context that way from a @SchemaMapping method and access it via BatchLoaderEnvironment#getContext().

There is also a more advanced option to use DataLoader directly (instead of the @BatchMapping method), and then you can pass more detailed "key context" for each item. Check the section on "Passing Context" in the GraphQL Java docs.

@rstoyanchev
Copy link
Contributor

I'm closing this as there isn't anything specific we can add here, but feel free to comment further.

@rstoyanchev rstoyanchev removed the status: waiting-for-triage An issue we've not yet triaged label Feb 8, 2022
@dugenkui03
Copy link
Contributor Author

@rstoyanchev I think user which use spring grpahql would not care about the underlying difference between @SchemaMapping and @BatchMapping: SchemaMappingDataFetcher just invoke method, and BatchMappingDataFetcher invoke method by DataLoad.

The only reason user use @BatchMapping is to avoid n+1 problem, and they still likely want the capacity which @SchemaMapping have.BatchLoader could get DataFetchingEnvironment by CompletableFuture<V> DataLoader#load(K key, Object keyContext) which could pass HandlerMethodArgumentResolver#resolveArgument, maybe more details need to handle.

Further more, I think it will be great that user could custom HandlerMethodArgumentResolver.

@rstoyanchev
Copy link
Contributor

Re-opening to discuss further.

@rstoyanchev rstoyanchev reopened this Mar 17, 2022
@rstoyanchev rstoyanchev added the status: waiting-for-triage An issue we've not yet triaged label Mar 17, 2022
@rstoyanchev
Copy link
Contributor

@dugenkui03 I closed the issue too soon perhaps, because I don't think the proposal is feasible. I see now that you had something concrete in mind for the implementation, so let me address it.

Your want to pass DataFetchingEnvironment through the per-key context, i.e. via DataLoader#load(K key, Object keyContext). That can work sometimes but not generally because DataLoader caches by key, and if there is anything that influences the result, other than the key, it becomes an issue.

Arguments are one of those things. We could make them available to BatchMapping methods, but it would be possible to use that to load a different entity, e.g. partially populated, etc. Once we pass arguments through, we might as well allow anything from DataFetchingEnvironment but there is a reason why that's not available in GraphQL Java.

@BatchMapping methods are meant to provide a simple shortcut to eliminate boilerplate for the most common case. If it doesn't do what you need, you don't have to use it and it's still quite straight forward:

@Controller
public class BookController {

    public BookController(BatchLoaderRegistry registry) {
        registry.forTypePair(Long.class, Author.class).registerMappedBatchLoader((authorIds, env) -> {
            // return Map<Long, Author>
        });
    }

    @SchemaMapping
    public CompletableFuture<Author> author(Book book, DataLoader<Long, Author> loader) {
        return loader.load(book.getAuthorId());
    }

}

@dugenkui03
Copy link
Contributor Author

dugenkui03 commented Mar 18, 2022

@rstoyanchev Thank for your response.

about cacheKey, and why DataLoader#load(K key, Object keyContext) always work

That can work sometimes but not generally because DataLoader caches by key, and if there is anything that influences the result, other than the key, it becomes an issue.

Developer can create cacheKey from both key and context(DataFetchingEnvironment), instead of only key. Details in DataLoaderHelper and CacheKey. The only thing we need to do is allow develop custom CacheKey.

image

image

In fact, I think DataLoader#load(K key, Object keyContext) will always work, because DataFetcher pass all the information(DataFetchingEnvironment) to DataLoader, and DataLoader can easily solve a variety of problems, such as cache thing. DataLoader is pretty flexible if we can custom DataLoaderOptions in spring-graphql.

not available in GraphQL Java

Sorry I could not understand this deeply, do you mean that java-dataloader could not get DataFetchingEnvironment?

There seems to be no limit.

The purpose of pr-324 and this talk

The point is that: we could make @BatchMapping as flexible as @SchemaMapping does, and the only difference between them should be batch-load and one-shot-invoke, and it is easy to achieve. And the pr-324 become how to use the DataLoader easily, instead of how to use it.

@rstoyanchev
Copy link
Contributor

rstoyanchev commented Mar 24, 2022

I think you're missing the main points.

One, @BatchMapping is a convenience that aims to hide the details of using a DataLoader. You point to customization of the caching key. That is a step in the opposite direction towards a deeper customization and for that, the @BatchMapping shortcut is not a good fit, just don't use it.

Two, I think the caching problem is deeper than you suspect. To work generally, the caching key would have to take into account the arguments used and the selection set. Imagine entities loaded for different parts of the response with different arguments and selection sets. There is a library called GOM that solves this problem in a general way but this is not something we are looking to do at this time.

As for this not being available in GraphQL Java, you can see this issue for example graphql-java/java-dataloader#26.

@dugenkui03
Copy link
Contributor Author

@rstoyanchev Thanks very much. I misunderstand the design philosophy of @Schemamapping, and I find that graphql-dataloader haven't consider such a complex situation too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants