micronaut-data icon indicating copy to clipboard operation
micronaut-data copied to clipboard

Fix count query for Page response and criteria

Open radovanradic opened this issue 1 year ago • 19 comments

Changes fix for an earlier issue and adds fix for recently reported issue. Basically, when returning Page model will execute DISTINCT COUNT <identity> because when there are joins, results return more rows and we cannot count that since will return more results (while returning results using mapper returns correct number of records).

radovanradic avatar Jan 18 '24 11:01 radovanradic

How is pagination supposed to work here? The limit would need to be distinct, too, somehow.

dstepanov avatar Jan 18 '24 12:01 dstepanov

How is pagination supposed to work here? The limit would need to be distinct, too, somehow.

This is needed for totalSize in Page response only. Paging will work like before, or I am missing something.

radovanradic avatar Jan 18 '24 12:01 radovanradic

I don't think pagination like we have it now will work with JOINs, because our implementation just adds LIMIT to the end of the query. You would expect it to limit the root entity rows, but that will limit the entity records + other rows, so you can cut the relationship in the middle and fetch Author1(Book1, Book2) and then Author1(Book3), Author2(..). The correct way would be first to limit the entity table and join the other one.

dstepanov avatar Jan 18 '24 13:01 dstepanov

I don't think pagination like we have it now will work with JOINs, because our implementation just adds LIMIT to the end of the query. You would expect it to limit the root entity rows, but that will limit the entity records + other rows, so you can cut the relationship in the middle and fetch Author1(Book1, Book2) and then Author1(Book3), Author2(..). The correct way would be first to limit the entity table and join the other one.

Yes, but this PR does not change that behavior, only count query that returns total size. It changes previous fix (this is better now I think) and fixes count for criteria query. It just counts main table records (distinct ids) from the join query. Before this it would count all main table + joined results and produce invalid total size.

We could try to handle pagination in separate ticket.

radovanradic avatar Jan 18 '24 13:01 radovanradic

I wonder how Hibernate is doing; it looks like a pretty complex problem.

dstepanov avatar Jan 18 '24 14:01 dstepanov

I wonder how Hibernate is doing; it looks like a pretty complex problem.

Yes, I thought I should spend some time and see how spring is doing it in their paging methods.

radovanradic avatar Jan 18 '24 15:01 radovanradic

I wonder how Hibernate is doing; it looks like a pretty complex problem.

I looked at how spring and hibernate are doing this. Looks like in relation author->books they run count against author only but also when retrieve data, first fetch results for author with given offset/limit and then load joined collection of books for each author record. This is quite different from what we are doing (as we generate JOIN in the query and execute it). So, to be accurate we might need to change query execution with paging in jdbc. Looks like significant change and amount of work to me.

radovanradic avatar Jan 24 '24 12:01 radovanradic

@radovanradic @dstepanov I have noticed this has been here for a while. Is there anything I can do to help?

kareemelzayat avatar Feb 07 '24 13:02 kareemelzayat

I think this fixes count issue, but there are other issues with how we retrieve paged items incorrectly when there are joins. That would be major change in the code, but don't know how we want to handle that. It would require rewriting SQL builder, logic in fetching (like Hibernate loads joins separately with another call to the db) etc.

radovanradic avatar Feb 07 '24 13:02 radovanradic

@radovanradic could you be more specific? What exactly are we experiencing? What are the use cases that cause such issues to happen? From what I understood, some issue arise when users write a query like

val predicateSpecification = { root.fetch(childEntities) }
findAll(PredicateSpecification, Pageable) --> wrong results are returned

Is that so? Could it be related to the issue we have here: https://github.com/micronaut-projects/micronaut-data/issues/2780?

kareemelzayat avatar Feb 07 '24 14:02 kareemelzayat

@kareemelzayat It is what Denis commented above https://github.com/micronaut-projects/micronaut-data/pull/2740#issuecomment-1898445888 We use LIMIT on the result that returns parent + child records and then we don't return correct number. Hibernate does limit on parent records so it returns correct number of records and then fetches children for each parent.

radovanradic avatar Feb 07 '24 15:02 radovanradic

@radovanradic so the problem is only with JDBC? Can't it be solved with a countDistinct?

The downside will probably be a slower query

kareemelzayat avatar Feb 07 '24 15:02 kareemelzayat

Yes, only JDBC. It won't help, still will count author(s) + books as this is what is retrieved, need to return authors that satisfy given condition and then join books.

radovanradic avatar Feb 07 '24 15:02 radovanradic

The issue is only with Micronaut Data JDBC / R2DBC, paginating of joined tables is a bit problematic

dstepanov avatar Feb 07 '24 15:02 dstepanov

I mean, JDBC/R2DBC is for users who want to have full control of their SQL queries. They know what they are doing and what to expect.

This means, they know they will need to use distinct if they want correct results in case of *-to-Many joins. This means they know what a paginated result of a join query will look like.

So they will need to adapt their queries (via distinct) for example.

Or am I missing something?

kareemelzayat avatar Feb 07 '24 15:02 kareemelzayat

CLA assistant check
All committers have signed the CLA.

CLAassistant avatar Feb 07 '24 21:02 CLAassistant

there is any plan date to resolve this problem?

ibmsoft avatar Feb 23 '24 13:02 ibmsoft

I think this could be merged, but Denis noted that actual paging code will not return correct data with current implementation and this is another big and kind of complex issue. Just checked spring paging and they basically have similar issue with joined entities spring-hibernate-paging.zip However, count could be fixed with this and then paging handled (if there is a good solution) in separate ticket.

radovanradic avatar Feb 23 '24 15:02 radovanradic

Closing this for now, solving the whole issue might take much more time and perhaps different approach.

radovanradic avatar Mar 26 '24 08:03 radovanradic