-
-
Notifications
You must be signed in to change notification settings - Fork 722
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
Overfetching problem when querying related entities and using intermediary projections #2373
Comments
i will track this down in the new implementation for 11. It should just work, because we prjoect like this: {
books {
name
author {
name
}
}
} .Select(x=> new BookDto
{
Name = x.Name,
Author = new AuthorDto
{
Name = x.Author.Name,
}
}) |
@PascalSenn Ok, great to hear. Then we'll have to wait for v11 before we can deploy our app so that this issue is fixed until then. Thanks.
Well, as I mentioned here, running the code that you said that Hot Chocolate would generate from the GraphQL query: queryableDto.Select(bDto => new
{
Name = bDto.Name,
Author = new
{
Name = bDto.Author.Name,
}
}).ToList(); would NOT generate the SQL that Hot Chocolate causes to be generated. I talked about this in the original comment and also here. So I think that's probably not exactly how the projection works in Hot Chocolate. |
Does this query generate the desired result?
|
@PascalSenn Yes, that query would generate the following SQL: SELECT [b].[Name], [a].[Name]
FROM [Books] AS [b]
INNER JOIN [Authors] AS [a] ON [b].[AuthorId] = [a].[Id] which is perfect. It only retrieves the name of the book and the name of the author, as requested. However, sending this GraphQL query to our Hot Chocolate server: {
books {
name
author {
name
}
}
} would generate this SQL instead: SELECT [b].[Name], [a].[Id], [a].[Name], [a].[Age]
FROM [Books] AS [b]
INNER JOIN [Authors] AS [a] ON [b].[AuthorId] = [a].[Id] which as you can see is unnecessarily fetching the I can also create a public GitHub repo and push this project into it so you can clone it and look into it and into the generated queries yourself. A super-simple project with less than 15 classes in total. |
Yes a repro would be great Are there any custom resolvers on author? |
@PascalSenn Sure! I'll do it and let you know. |
@PascalSenn Here you go, sir: https://github.com/AradAral/AspNetCoreGraphQL Edit: I added some seed data for the database so if you want to clone the project you can just adjust the connection string and then run one initial migration, as you know, and then you can use the GraphQL endpoint and inspect the generated SQL queries. I also added a |
@PascalSenn: :) |
@AradAral cool, the repro is definitely helpful. |
@PascalSenn I just noticed something else which I think might be important: |
Did you also remove it from the resolver declared in the QueryType? It is probably always requested in this case, because Select statement that maps from object to dto projects it already |
Yes, I actually removed the whole |
@PascalSenn e => new BookDto()
{
Name = e.Name,
Author = e.Author == null ? default(AuthorDto) : new AuthorDto() { Name = e.Author.Name }
} The important bit is where it's checking whether So, now, what you do think can be done about this? Does Hot Chocolate allow us to disable null checks so that it leaves it up to me to check for nulls, or perhaps do the null check differently so as to avoid this, or really anything that could solve this problem somehow? |
Based on the analysis above, I'll close this issue. |
@PascalSenn Hi! |
Sorry i messed something up. |
we just fixed UseProjections to do this: I need to check what it does with reprojection |
@PascalSenn |
Yes, but UseProjection did something different. |
@PascalSenn Aha! Okay, got it! |
It looks like there's still an issue on deeply nested entities. [UseProjection]
public IQueryable<BookDto> GetBooks([Service]AppDbContext dbContext)
{
return dbContext.Books.Select(book => new BookDto
{
Name = book.Name,
...
Author = new AuthorDto
{
Id = book.Author.Id,
Name = book.Author.Name,
Address = new AddressDto
{
Street = book.Author.Address.Street
}
}
});
} where the {
books {
name
author {
name
}
}
} |
@gojanpaolo Hi! The reason why in your case |
I was expecting the projection to behave similar to var dto =
dbContext.Books.Select(book => new BookDto
{
Name = book.Name,
...
Author = new AuthorDto
{
Id = book.Author.Id,
Name = book.Author.Name,
Address = new AddressDto
{
Street = book.Author.Address.Street
}
}
});
var result =
dto.Select(bookDto => new
{
Name = bookDto.Name,
Author = new
{
Name = bookDto.Author.Name
}
})
.ToList(); which EF correctly translates to the SQL query I expected, i.e. no join on the I believe this is something we can fix either in hotchocolate/ |
Im also experiencing overfetching: the query users { id } works fine and produces the following SQL SELECT [u].[Id]
FROM [User] AS [u] whereas this query users {
id
author { name }
} unecessary loads more data: SELECT [u].[Id], [a].[Id], [a].[Name], [a].[UserId], [b].[Id], [b].[AuthorId], [b].[Title]
FROM [User] AS [u]
LEFT JOIN [Author] AS [a] ON [u].[Id] = [a].[UserId]
LEFT JOIN [Book] AS [b] ON [a].[Id] = [b].[AuthorId]
ORDER BY [u].[Id], [a].[Id], [b].[Id] [UseProjection]
public IQueryable<User> Users([Service] DataContext ctx)
{
return ctx.User
.Include(v => v.Author)
.ThenInclude(v => v.Books);
} Using efcore 5 and hot chocolate 11 |
It does. That's what I'm currently doing — via a custom expression visitor, as I explained before — and it's working.
This is just a way to tell EF Core to check whether or not an |
@PascalSenn I can create a quick repro for you that demonstrates this, if you want. Let me know. |
@aradalvand I added a unittests with a small playground in the pr #4970. Can you add it there? |
@PascalSenn @michaelstaib has there been any progress on this issue? |
@aradalvand In your comment on 10 November 2020 you mentioned that you wrote a custom expression visitor replacing occurrences of |
@dotarj See https://gist.github.com/aradalvand/9b70e8bb455f5398affba610f2a25625 You would then need to do |
@michaelstaib any plans to get this one on the roadmap now that the v13 is live? :) |
Hey, any updates on this? Won't you consider it for v14? |
I think this was the latest state ... From our side this has no traction at the moment. From my perspective on the GraphQL core I do not want a Can you follow up on pascals question? |
Not really, this was the latest state — you said:
I'd be curious to know if you actually did speak to Shay about this, and if so then what was the result? I also wanted to point out #2365, if it's implemented it likely solves this whole class of problems, please consider that too. That was the very first issue I opened about this sort of thing, and I suspect it may be simpler to actually implement. |
We did at some point discuss projections. But the focus was more on Keyset pagination and nested queries. Overall we did not revamp projections for Hot Chocolate 13 and postponed it for some point later down the road. There is a new project from Microsoft that does true binding to the database. Called Azure Data API Builder which under the hood uses Hot Chocolate but does full projections. Might this be an alternative for you? For us projections at this point has no priority for this year as we are busy tackling other big topics like AOT, Fusion, Authorization, and related topics. We still have the larger overhaul of projections on our mind but pushed it back to later this year beginning of next year. Not sure yet. Depends also always on the need. |
Here is the link of to the project: |
You also could start a community project bringing in the missing parts... with type interceptors HC is highly extendable. |
Yeah I'm thinking about giving that a try. Though HC's lower-level components are generally not documented that well so I need some help as you said, where can I ask questions? The Slack channel? |
Slack channel is a good place ... we can have a chat together with pascal and charter a way forward. |
Hey everyone, was any progress made on this topic? |
Hi @kresimirlesic, see the latest dicussion at #6002 and specifically this comment of mine. |
Following #2365 (comment)
I have
Book
andAuthor
domain classes +BookDto
andAuthorDto
classes, I want to expose anIQueryable<BookDto>
for thebooks
field on myquery
root type.So here is the
GetBooks
method on theQuery
class: It simply projectsBook
intoBookDto
and returns anIQueryable<BookDto>
so that Hot Chocolate could then do its magic on it.Now, when you run the following query:
You would probably expect the following SQL to ultimately get generated:
However, instead, Hot Chocolate causes this one to get generated by EF Core:
Which retrieves all the fields on the author (including
Age
andId
in this case), even though the query requested only theName
.At first, I thought this must be an EF Core issue, but then I executed the following code:
And I realized it works as expected, and generates the following SQL which only selects the requested column (author name in this case):
Therefore, this must be related to Hot Chocolate, I believe.
This is really a serious problem for situations like mine when we don't want to expose
IQueryable<TheEntityFrameworkModelClass>
but rather a different type.I'm using Hot Chocolate version 10.5.2
And I've tried this with both EF Core version 3.1.8 and 5.0.0 RC.
The text was updated successfully, but these errors were encountered: