DATACOUCH-139 - Add limited support for @View query derivation.
@View can now trigger a basic query derivation. If viewName's explicitly set, query derivation is attempted. Otherwise, no derivation is made but instead the viewName is guessed by using the method name and removing a "find" or "count" prefix. Methods prefixed with "count" (and in general detected as count projections) will trigger a reduce on the view.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.data.couchbase.repository;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@@ -32,6 +33,7 @@ import org.springframework.test.context.support.DependencyInjectionTestExecution
|
||||
|
||||
/**
|
||||
* @author Michael Nitschinger
|
||||
* @author Simon Baslé
|
||||
*/
|
||||
public class CouchbaseRepositoryViewListener extends DependencyInjectionTestExecutionListener {
|
||||
|
||||
@@ -51,8 +53,10 @@ public class CouchbaseRepositoryViewListener extends DependencyInjectionTestExec
|
||||
|
||||
private void createAndWaitForDesignDocs(final Bucket client) {
|
||||
String mapFunction = "function (doc, meta) { if(doc._class == \"org.springframework.data.couchbase.repository.User\") { emit(null, null); } }";
|
||||
String mapFunctionName = "function (doc, meta) { if(doc._class == \"org.springframework.data.couchbase.repository.User\") { emit(doc.username, null); } }";
|
||||
View view = DefaultView.create("customFindAllView", mapFunction, "_count");
|
||||
List<View> views = Collections.singletonList(view);
|
||||
View customFindByNameView = DefaultView.create("customFindByNameView", mapFunctionName, "_count");
|
||||
List<View> views = Arrays.asList(view, customFindByNameView);
|
||||
DesignDocument designDoc = DesignDocument.create("user", views);
|
||||
client.bucketManager().upsertDesignDocument(designDoc);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,8 +18,10 @@ package org.springframework.data.couchbase.repository;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import com.couchbase.client.java.Bucket;
|
||||
import com.couchbase.client.java.view.Stale;
|
||||
@@ -33,12 +35,14 @@ import org.springframework.dao.InvalidDataAccessResourceUsageException;
|
||||
import org.springframework.data.couchbase.IntegrationTestApplicationConfig;
|
||||
import org.springframework.data.couchbase.core.CouchbaseTemplate;
|
||||
import org.springframework.data.couchbase.repository.support.CouchbaseRepositoryFactory;
|
||||
import org.springframework.data.mapping.PropertyReferenceException;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author David Harrigan
|
||||
* @author Simon Baslé
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(classes = IntegrationTestApplicationConfig.class)
|
||||
@@ -77,13 +81,89 @@ public class CouchbaseRepositoryViewTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldTrimOffFindOnCustomFinder() {
|
||||
public void shouldDetectMethodNameWithoutPropertyAndIssueGenericQueryOnView() {
|
||||
Iterable<User> users = repository.findRandomMethodName();
|
||||
assertNotNull(users);
|
||||
assertTrue(users.iterator().hasNext());
|
||||
|
||||
try {
|
||||
repository.findAllSomething();
|
||||
repository.findIncorrectExplicitView();
|
||||
fail("Expected InvalidDataAccessResourceException");
|
||||
} catch (InvalidDataAccessResourceUsageException e) {
|
||||
assertTrue(e.getMessage(), e.getMessage().startsWith("View user/allSomething does not exist"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = PropertyReferenceException.class)
|
||||
public void shouldFailDeriveOnBadProperty() {
|
||||
repository.findAllByUsernameEqualAndUserblablaIs("uname-1", "blabla");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldDeriveViewParametersAndReduce() {
|
||||
long count = repository.countByUsernameGreaterThanEqualAndUsernameLessThan("uname-8", "uname-9");
|
||||
assertEquals(12, count);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldDeriveViewParameters() {
|
||||
String lowKey = "uname-1";
|
||||
String middleKey = "uname-10";
|
||||
String highKey = "uname-11";
|
||||
List<String> keys = Arrays.asList(lowKey, middleKey, highKey);
|
||||
|
||||
User u1 = repository.findByUsernameIs(lowKey);
|
||||
User u2 = repository.findByUsernameIs(middleKey);
|
||||
User u3 = repository.findByUsernameIs(highKey);
|
||||
|
||||
|
||||
List<User> in = repository.findAllByUsernameIn(keys);
|
||||
List<User> gteLte = repository.findByUsernameGreaterThanEqualAndUsernameLessThanEqual(lowKey, highKey);
|
||||
List<User> between = repository.findByUsernameBetween(lowKey, highKey);
|
||||
List<User> gteLimited = repository.findTop3ByUsernameGreaterThanEqual(lowKey);
|
||||
|
||||
assertNotNull(u1);
|
||||
assertNotNull(u2);
|
||||
assertNotNull(u3);
|
||||
|
||||
assertEquals(lowKey, u1.getUsername());
|
||||
assertEquals(middleKey, u2.getUsername());
|
||||
assertEquals(highKey, u3.getUsername());
|
||||
|
||||
List<User> expected = Arrays.asList(u1, u2, u3);
|
||||
assertEquals(expected, in);
|
||||
assertEquals(expected, gteLte);
|
||||
assertEquals(expected, between);
|
||||
assertEquals(expected, gteLimited);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldDeriveToEmptyClause() {
|
||||
List<User> users = repository.findAllByUsername();
|
||||
assertNotNull(users);
|
||||
assertEquals(100, users.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldDetermineViewNameFromMethodPrefix() {
|
||||
try {
|
||||
repository.findByIncorrectView();
|
||||
fail("Expected InvalidDataAccessResourceException");
|
||||
} catch (InvalidDataAccessResourceUsageException e) {
|
||||
assertTrue(e.getMessage(), e.getMessage().startsWith("View user/byIncorrectView does not exist"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldDetermineViewNameFromCountPrefixAndReduce() {
|
||||
long count = repository.countCustomFindAllView();
|
||||
assertEquals(100, count);
|
||||
|
||||
try {
|
||||
repository.countCustomFindInvalid();
|
||||
fail("Expected InvalidDataAccessResourceException");
|
||||
} catch (InvalidDataAccessResourceUsageException e) {
|
||||
assertTrue(e.getMessage(), e.getMessage().startsWith("View user/customFindInvalid does not exist"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,10 +16,13 @@
|
||||
|
||||
package org.springframework.data.couchbase.repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.couchbase.core.view.View;
|
||||
|
||||
/**
|
||||
* @author David Harrigan
|
||||
* @author Simon Baslé
|
||||
*/
|
||||
public interface CustomUserRepository extends CouchbaseRepository<User, String> {
|
||||
|
||||
@@ -31,7 +34,42 @@ public interface CustomUserRepository extends CouchbaseRepository<User, String>
|
||||
@View(designDocument = "userCustom", viewName = "customCountView")
|
||||
long count();
|
||||
|
||||
@View
|
||||
Iterable<User> findAllSomething();
|
||||
@View(viewName = "allSomething")
|
||||
Iterable<User> findIncorrectExplicitView();
|
||||
|
||||
@View(viewName = "customFindAllView")
|
||||
Iterable<User> findRandomMethodName();
|
||||
|
||||
@View(viewName = "customFindByNameView")
|
||||
long countByUsernameGreaterThanEqualAndUsernameLessThan(String lowBound, String highBound);
|
||||
|
||||
@View(viewName = "customFindByNameView")
|
||||
User findByUsernameIs(String lowKey);
|
||||
|
||||
@View(viewName = "customFindByNameView")
|
||||
List<User> findAllByUsernameIn(List<String> keys);
|
||||
|
||||
@View(viewName = "customFindByNameView")
|
||||
List<User> findByUsernameGreaterThanEqualAndUsernameLessThanEqual(String lowKey, String highKey);
|
||||
|
||||
@View(viewName = "customFindByNameView")
|
||||
List<User> findByUsernameBetween(String lowKey, String highKey);
|
||||
|
||||
@View(viewName = "customFindByNameView")
|
||||
List<User> findTop3ByUsernameGreaterThanEqual(String lowKey);
|
||||
|
||||
@View(viewName = "customFindAllView")
|
||||
List<User> findAllByUsername();
|
||||
|
||||
@View(viewName = "customFindAllView")
|
||||
List<User> findAllByUsernameEqualAndUserblablaIs(String s, String blabla);
|
||||
|
||||
@View
|
||||
List<User> findByIncorrectView();
|
||||
|
||||
@View
|
||||
long countCustomFindAllView();
|
||||
|
||||
@View
|
||||
long countCustomFindInvalid();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,6 +20,7 @@ import org.springframework.data.annotation.Id;
|
||||
|
||||
/**
|
||||
* @author Michael Nitschinger
|
||||
* @author Simon Baslé
|
||||
*/
|
||||
public class User {
|
||||
|
||||
@@ -41,4 +42,21 @@ public class User {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
User user = (User) o;
|
||||
return key.equals(user.key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return key.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user