spring-projects / spring-data-neo4j

Provide support to increase developer productivity in Java when using Neo4j. Uses familiar Spring concepts such as a template classes for core API usage and lightweight repository style data access.
http://spring.io/projects/spring-data-neo4j
Apache License 2.0
825 stars 619 forks source link

Exception on method with pageable parameter [DATAGRAPH-949] #1510

Closed spring-projects-issues closed 6 years ago

spring-projects-issues commented 7 years ago

Mark Angrish opened DATAGRAPH-949 and commented

It looks like pageable works only for non @Query repository methods. The issue seems to be due to using Sort in a 'PageRequest' with a @Query type repository method.

SDN: 4.2.0.BUILD-SNAPSHOT OGM: 2.1.0-SNAPSHOT

Exception:

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessResourceUsageException: Error executing Cypher "Neo.ClientError.Statement.SyntaxError"; Code: Neo.ClientError.Statement.SyntaxError; Description: Variable `accountId` not defined (line 1, column 149 (offset: 148))
"MATCH (a:AccountGraph) <-[:follows]-(b:AccountGraph) where ( a.username = {0} or a.emailAddress = {0} ) and ( a.accountId < {1} ) RETURN b ORDER BY accountId DESC SKIP {sdnSkip} LIMIT {sdnLimit}"
 "MATCH (a:AccountGraph) <-[:follows]-(b:AccountGraph) where ( a.username = {0} or a.emailAddress = {0} ) and ( a.accountId < {1} ) RETURN b ORDER BY accountId DESC SKIP {sdnSkip} LIMIT {sdnLimit}"
                                                                                                                                                     ^
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982)
    org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:624)
    org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:731)
    org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter.doFilterInternal(OpenEntityManagerInViewFilter.java:178)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:317)
    org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127)
    org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:115)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:158)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:169)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:134)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:121)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:100)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:66)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214)
    org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177)
    org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    //Controller Method
    List<AccountGraph> followers(@RequestParam("username") String username, @PathVariable("maxId") Long maxId){
        PageRequest page = pageable(0,pageMaxRows, "accountId", "desc");
        List<AccountGraph> graphs =  neo4jAccountGraphService.findFollowingAccount(username,maxId,page).getContent();//friendOperations.getFriends(username);
        return graphs;
    }

    public static final PageRequest pageable(int page, int rows, String sortBy, String order) {
        String orderBy = sortBy;
        Sort sort = null;
        if (order.equals("desc"))
            sort = new Sort(Sort.Direction.DESC, orderBy);
        else
            sort = new Sort(Sort.Direction.ASC, orderBy);
        return new PageRequest(page, rows, sort);

    }

 //Above method calls following Repository Method, which throws the Exception
    @Query("MATCH (a:AccountGraph) <-[:follows]-(b:AccountGraph) where ( a.username = {0} or a.emailAddress = {0} ) and ( a.accountId < {1} ) RETURN b")
    Page<AccountGraph> findFollowingAccount(String principal, Long maxId, Pageable pageable);

Entity:

@NodeEntity
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@id")
public class AccountGraph extends AbstractEntity implements Serializable{
    private static final long serialVersionUID = -8993083097119456547L; 
    @GraphId
    private Long id;
    @Index(unique=true)
    private Long accountId;

    //@Fetch
    @Relationship(type = "blocked")
    private Set<AccountGraph> blocked = AppUtil.newHashSetInstance();

    //@Fetch
    @Relationship(type="follows",
            direction= Relationship.OUTGOING)
    private Set<AccountGraph> following = AppUtil.newHashSetInstance();
    //@Fetch
    @Relationship(type="follows",
            direction= Relationship.INCOMING)
    private Set<AccountGraph> followers = AppUtil.newHashSetInstance();

    private AccountGraph(){}

}

Affects: 4.2 RC1 (Ingalls), 5.0 M1 (Kay), 4.2 GA (Ingalls)

spring-projects-issues commented 6 years ago

Nicolas Mervaillie commented

Pagination for @Query annotated methods are a kind of a hack.

The order by clause is added dynamically to the end of the query by SDN. Because SDN cannot know what's inside the user specified query, the user has either to alias the attribute name in the query to make it match with what's given in the sort, or use in the sort the name of the return attribute of the query