Blazebit / blaze-persistence

Rich Criteria API for JPA providers
https://persistence.blazebit.com
Apache License 2.0
742 stars 90 forks source link

Unable to make a paginated query (GraphQl) for an entity view with inheritance configured #1522

Open dsarlo-viso opened 2 years ago

dsarlo-viso commented 2 years ago

Description

I have an entity view that has EntityViewInheritance configured. I am trying to create a paginated graphql query wrapped in a GraphQLRelayConnection.

This is my entity view and graphql schema:

@JsonTypeInfo (use = JsonTypeInfo.Id.NAME, property = "type", visible = true)
@JsonSubTypes({
    @JsonSubTypes.Type (value = EventNotification.class, name = Notification.EVENT_TYPE),
    @JsonSubTypes.Type (value = TestNotification.class, name = Notification.TEST_TYPE),
    @JsonSubTypes.Type (value = TipNotification.class, name = Notification.TIP_TYPE),
})
@EntityView (Notification.class)
@EntityViewInheritance
public abstract class NotificationView {
    @IdMapping
    public abstract Long getId();

    public abstract Long getUserId();
}

type Notification {
    id: ID!
    userId: ID!
}

type NotificationConnection {
    edges: [NotificationEdge]
    pageInfo: PageInfo
}

type NotificationEdge {
    node: Notification!
    cursor: String!
}

type PageInfo {
    startCursor: String
    endCursor: String
}

The following query is being made:

 CriteriaBuilder<Notification> cb = cbf.create(em, Notification.class).from(Notification.class);

        cb = cb.where("userId").eq(userId);

        EntityViewSetting<NotificationView, PaginatedCriteriaBuilder<NotificationView>> setting = graphQLEntityViewSupport.createPaginatedSetting(NotificationView.class, dataFetchingEnvironment);
        setting.addAttributeSorter("id", Sorters.ascending());
        if (setting.getMaxResults() == 0) {
            return new GraphQLRelayConnection<>(Collections.emptyList());
        }
        PaginatedCriteriaBuilder<NotificationView> paginatedCriteriaBuilder = evm.applySetting(setting, cb);
        return new GraphQLRelayConnection<>(paginatedCriteriaBuilder.getQuery().getResultList());

Expected behavior

I expect that I would get the entity view's back as edge nodes in the response

Actual behavior

All of the node data - the entity views I'm querying for - are coming back null

"data": {
    "findAllReadAndNewNotificationsForCurrentUser": {
      "edges": [
        null,
        null,
        null,
        null,
        null
      ],
      "pageInfo": {
        "startCursor": "rO0ABXcCAAp1cgAXW0xqYXZhLmlvLlNlcmlhbGl6YWJsZTuu0AmsU9ftSQIAAHhwAAAAAXNyAA5qYXZhLmxhbmcuTG9uZzuL5JDMjyPfAgABSgAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAAAAAAFFQ==",
        "endCursor": "rO0ABXcCAAp1cgAXW0xqYXZhLmlvLlNlcmlhbGl6YWJsZTuu0AmsU9ftSQIAAHhwAAAAAXNyAA5qYXZhLmxhbmcuTG9uZzuL5JDMjyPfAgABSgAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAAAAAAKKQ=="
      }
    }
  }

Steps to reproduce

Environment

Version: 1.6.6 JPA-Provider: Hibernate 5.6.9.Final (Spring Data JPA 2.7.1) DBMS: PostgreSQL 13.3 Application Server: Spring Boot 2.7.1

maxhov commented 8 months ago

Hi. We are also seeing the same issue in the latest version (1.6.11). The data seems to be fetched from the database correctly, but somehow it is not mapped correctly. I tried tracking down where it might go wrong, but I am not sure.

What I found is that when quering for the base type the null result comes from the following lines:

    @Override
    protected T buildObject(Object[] originalTuple, Object[] tuple) {
        // Cast to Number instead of integer since datanucleus will return a Long
>>>        Number index = (Number) originalTuple[subtypeDiscriminatorIndex];
        if (index == null) {
            return null;
        } else {
            if (hasId) {
                if (tuple[0] == null) {
                    return null;
                }
            } else if (nullIfEmpty) {
                for (int i = 0; i < tuple.length; i++) {
                    if (tuple[i] != null) {
                        return subtypeInstantiators[index.intValue()].newInstance(tuple);
                    }
                }

                return null;
            }
            return subtypeInstantiators[index.intValue()].newInstance(tuple);
        }
    }

The first element in the tuple is a null value, which is why it will return null at the first predicate. Is it using the correct array there? Shouldn't it use tuple array instead of the originalTuple array?

beikov commented 8 months ago

Hi @maxhov, could you maybe try to reproduce this problem with e.g. one of our quickstarts? That way I can easily debug and verify if your assumption is correct.

maxhov commented 8 months ago

Ofcourse. Here is a sample project that reproduces the issue: https://github.com/maxhov/blaze-graphql-issue. I added a short 'how to run' in the README. The dependencies are updated to the latest Spring managed versions (3.2.4).

dsarlo-viso commented 1 month ago

@beikov Any update on a fix for this? Ran into it again today actually

beikov commented 1 month ago

Hi @dsarlo-viso, sorry, but I did not find the time yet to look into this as I was mostly focused on sponsored work. One easy way for moving this up my priority list is to sponsor this as well :)