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

Improve guidance on use of projections with Querydsl and Query By Example #248

Closed
Thinkenterprise opened this issue Jan 5, 2022 · 11 comments
Assignees
Labels
in: data Issues related to working with data status: superseded Issue is superseded by another type: documentation A documentation task
Milestone

Comments

@Thinkenterprise
Copy link

Are you planning to support pagination, sorting and maybe also projections for querydsl and query by example? These are topics that also play a role for GraphQL APIs. The implementation of some attributes that go in that direction, but not all of them are supported yet, right?

The Builder for the different DataFetchers are set to default values.

Snippet from QuerysldDataFetcher

@SuppressWarnings("unchecked")
Builder(QuerydslPredicateExecutor<T> executor, Class<R> domainType) {
	this(executor,
	ClassTypeInformation.from((Class<T>) domainType),
		domainType,
		Sort.unsorted(),
		NO_OP_BINDER_CUSTOMIZER);
}

The attributes will be considered, but always have the same default values. Pagination is not supported but part of the QuerydslPredicateExecutor interface. It would be possible if you want to support pagination.

Snippet from QuerysldDataFetcher

@Override
@SuppressWarnings("unchecked")
public Iterable<R> get(DataFetchingEnvironment env) {
	return this.executor.findBy(buildPredicate(env), query -> {
		FetchableFluentQuery<R> queryToUse = (FetchableFluentQuery<R>) query;

		if (this.sort.isSorted()){
			queryToUse = queryToUse.sortBy(this.sort);
		}

		if (requiresProjection(this.resultType)){
			queryToUse = queryToUse.as(this.resultType);
		}
		else {
			queryToUse = queryToUse.project(buildPropertyPaths(env.getSelectionSet(), this.resultType));
		}

		return queryToUse.all();
	});
}

}```
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Jan 5, 2022
@rstoyanchev
Copy link
Contributor

We have an issue to track this, see #103, but have not planned support for it yet. From what I've seen, there seems to be some consensus around how pagination is to be done in GraphQL, including an emerging spec but nothing like it for sorting. It might be worth splitting into separate issues, for pagination and sorting.

As for projections, I'm not sure what is missing there?

@Thinkenterprise
Copy link
Author

@rstoyanchev Thank you for the explanation. I think a suggestion for pagination could be agreed and supported. Since there is no suggestion for sorting, it could be left out for now. On the subject of projections: The Spring GraphQL documentation says:

QuerydslDataFetcher supports interface and DTO projections to transform query results before 
returning these for further GraphQL processing.

As I wrote above, the projection type resultType of the QuerydslDataFetcher is always set to the same value and therefore does not correspond to the projection type. Therefore, this condition is never met:

if (requiresProjection(this.resultType)){
	queryToUse = queryToUse.as(this.resultType);
}

From my point of view, projections in GraphQL are not that important, even superfluous, because you can use the GraphQL Query to select the data that you want to see :-)

I would therefore suggest removing projections from the Spring GraphQL documentation.

Or, perhaps a little example of how projection should be used together with Spring GraphQL and Query DSL would be helpful?

@rstoyanchev
Copy link
Contributor

The resultType is fixed initially, and therefore for automated registration, but it can be customized through the Builder with manual registration. As for projections vs GraphQL selection sets, they are rather complementary, with projections more focused on transformations from the domain type to the GraphQL schema. It is a good question though where we can provide more clarity and guidance. Expect a follow-up on that.

AS we already have an issue for pagination and sorting, I'm going to re-purpose this issue to provide more clarity around the use of projections.

@rstoyanchev rstoyanchev changed the title Sorting, Pagination and Projections Features for Querydsl and Query by Example Support? Improve guidance on use of projections with Querydsl and Query By Example Jan 13, 2022
@rstoyanchev rstoyanchev added in: data Issues related to working with data type: documentation A documentation task and removed status: waiting-for-triage An issue we've not yet triaged labels Jan 13, 2022
@rstoyanchev rstoyanchev added this to the 1.0.0-M5 milestone Jan 13, 2022
@mp911de mp911de self-assigned this Jan 13, 2022
@bclozel bclozel modified the milestones: 1.0.0-M5, 1.0 Backlog Jan 18, 2022
@Thinkenterprise
Copy link
Author

A small contribution to the follow-up from me.

GraphQL supports the principle of client-directed queries. The client (frontend) is in control of what it wants to see. This allows many different frontends to read very different data. That was one of the motivations for Facebook to specify GraphQL.

From my point of view, projections support the backend-for-frontend pattern more. In this case, different projections onto the data are required for each frontend. Control is in the backend. A rather static concept.

From my point of view, projections don't make much sense in the context of GraphQL. They are also not supported by the GraphQL schema. You would have to build an Object in GraphQL for each projection. I don't think that's very practical.

But I'm curious about other opinions and I'm happy to learn!

@mp911de
Copy link
Member

mp911de commented Jan 19, 2022

There are a few points you touched upon and it is important to see these items in the context of Spring. Spring for GraphQL is not a data gateway that translates GraphQL queries into SQL or a JSON query. There are other tools that handle such requirements much better than we could ever do. Spring for GraphQL is an API gateway that leverages existing Spring technology to expose underlying data sources through GraphQL.

In the light of Spring Data, data is modeled as an aggregate. Domain-driven design is the predominant approach when using Spring Data. So by design, a GraphQL API built on top of Spring Data can leverage only what's already there and must adhere to the constraints of the aggregate. Also, by definition, an aggregate is only valid if it is loaded in its entirety. Partially loaded aggregates may impose a limitation on aggregate functionality.

While some domain models are more lenient on that requirement, some others are stricter. Projections remove the limitation of aggregate integrity by defining properties within a model that can be selected and exposed towards a consumer. Projections can transform data from a model that serves a particular purpose within an application towards a representation that is useful to be used through GraphQL without the need to duplicate data or apply any database-level transformations. This can happen either through SpEL or DTO projections.

So it really circles back to what you have and what you want to achieve. If you want to expose your database 1:1 via GraphQL then Spring for GraphQL is not what you're looking for. If your data and application leverage Spring Data already or you want to follow the Spring Data programming models and you want to expose your aggregates through GraphQL then Spring for GraphQL can help you with these task within the boundaries of the supported programming models.

@rstoyanchev
Copy link
Contributor

rstoyanchev commented Jan 20, 2022

Further guidance in the actual documentation update of #264. Closing as superseded by the PR but feel free to comment further.

@rstoyanchev rstoyanchev added the status: superseded Issue is superseded by another label Jan 20, 2022
@rstoyanchev rstoyanchev modified the milestones: 1.0 Backlog, 1.0.0-M6 Jan 20, 2022
@Thinkenterprise
Copy link
Author

Thinkenterprise commented Jan 20, 2022

@mp911de First of all, thank you for the detailed explanation of Spring Data, DDD, aggregations and projection in the context of Spring GraphQL. @rstoyanchev I don't want to be rude but we now have a very long introduction to the Data Integration chapter but no concrete example of how QueryDSL or QueryByExample is supposed to work with projections in Spring GraphQL. Also in Spring Data itself I can't find any example but only the explanation how projections are used with normal repositories. It is therefore difficult for me to reproduce whether it works at all. A source code snippet in chapter 5.1.2 or 5.2.2 would be nice. Or an example in the GraphQL sample. One last note. I found the introduction very long. I think you could shorten it a bit and put the explanations about projection in an extra chapter. But that's just a suggestion.

@mp911de
Copy link
Member

mp911de commented Jan 21, 2022

I updated the pull request and added code snippets for projection usage. Can you have a look and check whether that's what you've been looking for?

@Thinkenterprise
Copy link
Author

@mp911de Thanks for the code snippet, that definitely helps. At this point there are no more questions about Spring Data.

@rstoyanchev And Spring GraphQL also correctly describes in 5.1 that the QueryDSLDataFetchers and ReactiveQuerySQLDataFetcher have to be created themselves and provides examples.

The only problem I still have is that the QueryDSLDataFetchers and ReactiveQuerySQLDataFetcher are generated automatically with Spring Boot GraphQL Data Autoconfiguration GraphQlDSLAutoConfiguration debending on

public interface AccountRepository extends Repository<Account, Long>,
            QuerydslPredicateExecutor<Account> {
}

and I have no influence on them, right?

So my last questions would be

  1. How do I register my own QueryDSLDataFetcher with Projections in Spring GraphQL Code Snippet in Chapter 5.1?

  2. How can I define projections via QueryDSL over Repositories and have QueryDSLDataFetcher with Projections generated automatically, or is that not possible - over signature?

  3. If 2. doesn't work, you should put it in the documentation Chapter 5.1 or in Spring Boot Documentation, but like me, you'll end up on the wrong track

@rstoyanchev
Copy link
Contributor

I've moved the advice on projections from the overview into a separate section. I've added information on how to register a DataFetcher manually via RuntimeWiringConfigurer. I've also mentioned explicitly that auto-registration does not support customizations, so for anything more advanced, you'll need to use the builder on QuerydslDataFetcher directly.

Please, have a look. Hopefully it's all good now. Thanks for the feedback!

@Thinkenterprise
Copy link
Author

@rstoyanchev Great, thank you, it couldn't be better!! Based on this I can now implement my own examples. The point is now closed for me. I have no more questions. Thanks to all

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: data Issues related to working with data status: superseded Issue is superseded by another type: documentation A documentation task
Projects
None yet
5 participants