Search code examples
javaspring-datacouchbasespring-data-couchbase

spring-data-couchbase error while finding all documents with PageRequest


Using Spring Data Couchbase I created a very simple repository

public interface UserDao extends PagingAndSortingRepository<User, String>

This should allow me to execute a paged findAll as follows:

Page<User> userResult = repo.findAll(new PageRequest(1, 20));

However the the following is always thrown:

Exception in thread "main" java.lang.IllegalStateException: Unknown query param: Page request [number: 1, size 20, sort: null]
at org.springframework.data.couchbase.repository.query.ViewBasedCouchbaseQuery.execute(ViewBasedCouchbaseQuery.java:47)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:337)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.couchbase.repository.support.ViewPostProcessor$ViewInterceptor.invoke(ViewPostProcessor.java:80)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at $Proxy14.findAll(Unknown Source)
at com.polycom.cloudAxis.proxymanagement.model.Main.main(Main.java:41)

This doesn't happen if I create a Query and use the skip/limit/startKeyDocId, but would like to use PagingAndSortingRepository if possible.

Any idea what could be wrong? Thanks for all friendly help :)


Solution

  • i also had this same issue, here was the approach i took. its actually a very small change to the core implementation (which currently only support Query object types), essentially all I did was add another instance of check:

     if (param instanceof Query) {
                query = (Query) param;
            } else if (param instanceof Pageable) {
                pageable = (Pageable) param;
            }
    

    then added the logic for paging here, its been working so far but i havent fully vetted it so any comments from the community would be appreciated.

     if (pageable != null) {
    
            CouchbaseClient client = operations.getCouchbaseClient();
            View view = client.getView(designDocName(), viewName());
    
            // Paginator p = new Paginator(client, view, query,
            // pageable.getPageSize());
            Paginator paginator = client.paginatedQuery(view, query, pageable.getPageSize());
            // now we need to goto the start point
            ViewResponse viewResponse = null;
            // views are 0 base
            int i = 0;
            while (paginator.hasNext()) {
                viewResponse = paginator.next();
                if (pageable.getPageNumber() == i++) {
                    LOGGER.debug("Found the response for this page: {} ", i);
                    break;
                }
            }
    
            if (viewResponse == null) {
                LOGGER.debug("no view response so leaving now");
                return null;
            }
            Class<?> type = method.getEntityInformation().getJavaType();
    
            final List result = new ArrayList(viewResponse.size());
            for (final ViewRow row : viewResponse) {
                result.add(operations.findById(row.getId(), type));
            }
            return result;
    
        } 
    

    To get this wired up i had to do some things i didnt want to :D, i wanted to just override one method however to get to it required me to override many other things, so i ended up copying a little bit of code, ideally i would like to get this added as part of https://jira.spring.io/browse/DATACOUCH-93

    And the whole impl here:

    public class DCORepositoryFactory extends CouchbaseRepositoryFactory {
    
    CouchbaseOperations                                                                 couchbaseOperations;
    MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext;
    
    public DCORepositoryFactory(CouchbaseOperations couchbaseOperations) {
        super(couchbaseOperations);
        mappingContext = couchbaseOperations.getConverter().getMappingContext();
        this.couchbaseOperations = couchbaseOperations;
    }
    
    @Override
    protected Object getTargetRepository(RepositoryMetadata metadata) {
        // TODO Auto-generated method stub
        CouchbaseEntityInformation<?, Serializable> entityInformation = getEntityInformation(metadata.getDomainType());
        final DCORepository simpleCouchbaseRepository = new DCORepository(entityInformation, couchbaseOperations);
    
        simpleCouchbaseRepository.setViewMetadataProvider(ViewPostProcessor.INSTANCE.getViewMetadataProvider());
        return simpleCouchbaseRepository;
    }
    
    @Override
    protected QueryLookupStrategy getQueryLookupStrategy(QueryLookupStrategy.Key key) {
        return new CouchbaseQueryLookupStrategy();
    }
    
    /**
     * Currently, only views are supported. N1QL support to be added.
     */
    private class CouchbaseQueryLookupStrategy implements QueryLookupStrategy {
        @Override
        public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, NamedQueries namedQueries) {
            CouchbaseQueryMethod queryMethod = new CouchbaseQueryMethod(method, metadata, mappingContext);
            return new PagingViewBasedCouchbaseQuery(queryMethod, couchbaseOperations);
        }
    }
    
    private static class PagingViewBasedCouchbaseQuery extends ViewBasedCouchbaseQuery {
    
        private static final org.slf4j.Logger   LOGGER  = org.slf4j.LoggerFactory
                                                                .getLogger(DCORepositoryFactory.PagingViewBasedCouchbaseQuery.class);
    
        private CouchbaseOperations             operations;
        private CouchbaseQueryMethod            method;
    
        public PagingViewBasedCouchbaseQuery(CouchbaseQueryMethod method, CouchbaseOperations operations) {
            super(method, operations);
            this.operations = operations;
            this.method = method;
        }
    
        /*
         * (non-Javadoc)
         * 
         * @see org.springframework.data.couchbase.repository.query.
         * ViewBasedCouchbaseQuery#execute(java.lang.Object[]) added the ability
         * to support paging
         */
        @Override
        public Object execute(Object[] runtimeParams) {
            Query query = null;
            Pageable pageable = null;
            for (Object param : runtimeParams) {
                if (param instanceof Query) {
                    query = (Query) param;
                } else if (param instanceof Pageable) {
                    pageable = (Pageable) param;
                } else {
                    throw new IllegalStateException(
                            "Unknown query param: (btw null is also not allowed and pagable cannot be null) " + param);
                }
            }
    
            if (query == null) {
                query = new Query();
            }
            query.setReduce(false);
            if (pageable != null) {
    
                CouchbaseClient client = operations.getCouchbaseClient();
                View view = client.getView(designDocName(), viewName());
    
                // Paginator p = new Paginator(client, view, query,
                // pageable.getPageSize());
                Paginator paginator = client.paginatedQuery(view, query, pageable.getPageSize());
                // now we need to goto the start point
                ViewResponse viewResponse = null;
                // views are 0 base
                int i = 0;
                while (paginator.hasNext()) {
                    viewResponse = paginator.next();
                    if (pageable.getPageNumber() == i++) {
                        LOGGER.debug("Found the response for this page: {} ", i);
                        break;
                    }
                }
    
                if (viewResponse == null) {
                    LOGGER.debug("no view response so leaving now");
                    return null;
                }
                Class<?> type = method.getEntityInformation().getJavaType();
    
                final List result = new ArrayList(viewResponse.size());
                for (final ViewRow row : viewResponse) {
                    result.add(operations.findById(row.getId(), type));
                }
                return result;
    
            } else {
                return operations.findByView(designDocName(), viewName(), query, method.getEntityInformation()
                        .getJavaType());
            }
        }
    
        /**
         * Returns the best-guess design document name.
         *
         * @return the design document name.
         */
        private String designDocName() {
            if (method.hasViewAnnotation()) {
                return method.getViewAnnotation().designDocument();
            } else {
                return StringUtils.uncapitalize(method.getEntityInformation().getJavaType().getSimpleName());
            }
        }
    
        /**
         * Returns the best-guess view name.
         *
         * @return the view name.
         */
        private String viewName() {
            if (method.hasViewAnnotation()) {
                return method.getViewAnnotation().viewName();
            } else {
                return StringUtils.uncapitalize(method.getName().replaceFirst("find", ""));
            }
        }
    }
    
    @Override
    protected Class<?> getRepositoryBaseClass(RepositoryMetadata repositoryMetadata) {
        return DCORepository.class;
    }
    
    }