DATACOUCH-596 - fix threadsafe parameters test

This commit is contained in:
mikereiche
2020-07-31 15:54:51 -07:00
parent fb4148025a
commit a839b4d135
10 changed files with 351 additions and 152 deletions

View File

@@ -0,0 +1,20 @@
package org.springframework.data.couchbase;
import org.junit.ClassRule;
import org.junit.runners.model.InitializationError;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* This runner initializes container for the container based testing.
*
* @author Subhashni Balakrishnan
*/
public class ContainerResourceRunner extends SpringJUnit4ClassRunner {
@ClassRule
public static final TestContainerResource resource = TestContainerResource.getResource();
public ContainerResourceRunner(Class<?> clazz) throws InitializationError {
super(clazz);
}
}

View File

@@ -0,0 +1,46 @@
package org.springframework.data.couchbase;
import java.util.concurrent.Callable;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
/**
* Helper to check if the Couchbase http endpoints are up.
*/
public class CouchbaseHttpPortListeningCheck implements Callable<Boolean> {
private final int port;
private final String path;
public CouchbaseHttpPortListeningCheck(int port, String path) {
this.port = port;
this.path = path;
}
private Boolean executeRequest(URIBuilder builder) throws Exception {
try {
HttpGet request = new HttpGet(builder.build());
HttpClient client = HttpClientBuilder.create().build();
HttpResponse response = client.execute(request);
int status = response.getStatusLine().getStatusCode();
if (status < 200 || status >= 300) {
return false;
}
return true;
} catch (Exception ex) {
Thread.sleep(1000);
throw ex;
}
}
@Override
public Boolean call() throws Exception {
URIBuilder builder = new URIBuilder();
builder.setScheme("http").setHost("localhost").setPort(this.port).setPath(this.path);
return executeRequest(builder);
}
}

View File

@@ -0,0 +1,85 @@
package org.springframework.data.couchbase;
import static java.time.temporal.ChronoUnit.SECONDS;
import java.time.Duration;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import com.couchbase.client.java.util.features.Version;
import org.rnorth.ducttape.ratelimits.RateLimiterBuilder;
import org.rnorth.ducttape.unreliables.Unreliables;
import org.testcontainers.containers.Container;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.WaitStrategy;
/**
* WaitStrategy for Couchbase containers which makes the Server node is initialized, RBAC user and default bucket is
* created.
*/
public class CouchbaseWaitStrategy implements WaitStrategy {
private Duration startupTimeout = Duration.of(60, SECONDS);
private final Boolean rbacEnabled;
public CouchbaseWaitStrategy(String serverVersion) {
Version version = Version.parseVersion(serverVersion);
rbacEnabled = version.major() >= 5;
}
private void checkResult(Container.ExecResult result, String command) throws Exception {
if (!result.getStdout().contains("SUCCESS")) {
throw new Exception(command + " command failed");
}
}
private void checkService(int port, String path) {
Callable<Boolean> externalCheck = new CouchbaseHttpPortListeningCheck(port, path);
Unreliables.retryUntilSuccess((int) startupTimeout.getSeconds(), TimeUnit.SECONDS, () -> externalCheck.call());
}
@Override
public void waitUntilReady(GenericContainer container) {
try {
checkService(8091, "/pools");
Container.ExecResult result;
if (rbacEnabled) {
result = container.execInContainer("/opt/couchbase/bin/couchbase-cli", "cluster-init",
"--cluster=127.0.0.1:8091", "--services=data,index,query", "--cluster-name=localcontainer",
"--cluster-username=Administrator", "--cluster-password=password", "--cluster-ramsize=512",
"--cluster-index-ramsize=512", "--index-storage-setting=default");
checkResult(result, "Cluster init");
result = container.execInContainer("/opt/couchbase/bin/couchbase-cli", "user-manage",
"--cluster=127.0.0.1:8091", "--username=Administrator", "--password=password", "--set",
"--rbac-username=protected", "--rbac-password=password", "--rbac-name=default", "--roles=admin",
"--auth-domain=local");
checkResult(result, "User manage");
result = container.execInContainer("/opt/couchbase/bin/couchbase-cli", "bucket-create",
"--cluster=127.0.0.1:8091", "--username=Administrator", "--password=password", "--bucket=protected",
"--bucket-type=couchbase", "--bucket-ramsize=200", "--enable-flush=1", "--wait");
} else {
result = container.execInContainer("/opt/couchbase/bin/couchbase-cli", "cluster-init",
"--cluster=127.0.0.1:8091", "--services=data,index,query", "-u", "Administrator", "-p", "password",
"--cluster-ramsize=512", "--cluster-index-ramsize=512", "--index-storage-setting=default");
checkResult(result, "Cluster init");
result = container.execInContainer("/opt/couchbase/bin/couchbase-cli", "bucket-create",
"--cluster=127.0.0.1:8091", "-u", "Administrator", "-p", "password", "--bucket=protected",
"--bucket-password=password", "--bucket-type=couchbase", "--bucket-ramsize=200", "--enable-flush=1",
"--wait");
}
checkResult(result, "Bucket create");
checkService(8093, "/query/ping");
} catch (Exception ex) {
ex.printStackTrace();
System.exit(1);
}
}
@Override
public WaitStrategy withStartupTimeout(Duration startupTimeout) {
this.startupTimeout = startupTimeout;
return this;
}
}

View File

@@ -0,0 +1,67 @@
package org.springframework.data.couchbase;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.rules.ExternalResource;
import org.testcontainers.containers.FixedHostPortGenericContainer;
/**
* Testcontainers as external resource. It is recommended to use it as ClassRule.
* It also does the internal reference counting, in case if the getResource is called again.
*
*/
public class TestContainerResource extends ExternalResource {
private static FixedHostPortGenericContainer couchbaseContainer = null;
private static final AtomicInteger referenceCount = new AtomicInteger();
private static TestContainerResource currentInstance;
private static String serverVersion;
public static TestContainerResource getResource() {
if (currentInstance == null) {
currentInstance = new TestContainerResource();
try {
Properties properties = new Properties();
properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("server.properties"));
serverVersion = properties.getProperty("server.version");
if(!properties.getProperty("server.resource").contentEquals("container")) {
return null;
}
} catch (Exception ex) {
serverVersion = "5.0.1";
}
couchbaseContainer = new FixedHostPortGenericContainer("couchbase:" + serverVersion)
.withFixedExposedPort(8091, 8091)
.withFixedExposedPort(18091, 18091)
.withFixedExposedPort(8092, 8092)
.withFixedExposedPort(18092, 18092)
.withFixedExposedPort(8093, 8093)
.withFixedExposedPort(18093, 18093)
.withFixedExposedPort(8094, 8094)
.withFixedExposedPort(18094, 18094)
.withFixedExposedPort(11210, 11210)
.withFixedExposedPort(11211, 11211)
.withFixedExposedPort(11207, 11207);
couchbaseContainer.waitingFor(new CouchbaseWaitStrategy(serverVersion));
couchbaseContainer.start();
}
return currentInstance;
}
@Override
protected void before() {
referenceCount.incrementAndGet();
}
@Override
protected void after() {
if (referenceCount.decrementAndGet() == 0 && couchbaseContainer != null) {
if(couchbaseContainer.isRunning()) {
couchbaseContainer.close();
}
currentInstance = null;
}
}
}

View File

@@ -1,9 +0,0 @@
package org.springframework.data.couchbase.repo;
import org.springframework.data.couchbase.repository.CouchbasePagingAndSortingRepository;
import org.springframework.data.couchbase.repository.Party;
import org.springframework.stereotype.Repository;
@Repository
public interface PartyPagingRepository extends CouchbasePagingAndSortingRepository<Party, String> {
}

View File

@@ -1,101 +0,0 @@
/*
* Copyright 2017-2020 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.repo;
import java.util.Date;
import java.util.List;
import org.springframework.data.couchbase.core.query.*;
import org.springframework.data.couchbase.repository.CouchbaseRepository;
import org.springframework.data.couchbase.repository.Party;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
/**
* @author Simon Baslé
* @author Subhashni Balakrishnan
*/
@Repository
@ViewIndexed(designDoc = "party", viewName = "all")
@N1qlPrimaryIndexed
@N1qlSecondaryIndexed(indexName = "party")
public interface PartyRepository extends CouchbaseRepository<Party, String> {
List<Party> findByAttendeesGreaterThanEqual(int minAttendees);
List<Party> findByName(String name);
List<Party> findByEventDateIs(Date targetDate);
@View(designDocument = "party", viewName = "byDate")
List<Party> findFirst3ByEventDateGreaterThanEqual(Date targetDate);
List<Object> findAllByDescriptionNotNull();
long countAllByDescriptionNotNull();
@Query("SELECT MAX(attendees) FROM #{#n1ql.bucket} WHERE #{#n1ql.filter}")
long findMaxAttendees();
@Query("SELECT `desc` FROM #{#n1ql.bucket} WHERE #{#n1ql.filter}")
String findSomeString();
@Query("SELECT count(*) + 5 FROM #{#n1ql.bucket} WHERE #{#n1ql.filter}")
long countCustomPlusFive();
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter}")
long countCustom();
@Query("SELECT 1 = 1")
boolean justABoolean();
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND `attendees` >= $1")
Page<Party> findPartiesWithAttendee(int count, Pageable pageable);
@Query("#{#n1ql.selectEntity}")
List<Party> findParties(Sort sort);
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND `desc` LIKE '%' || $included || '%' AND attendees >= $min" +
" AND `desc` NOT LIKE '%' || $excluded || '%'")
List<Party> findAllWithNamedParams(@Param("excluded") String ex, @Param("included") String inc,
@Param("min") long minimumAttendees);
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND `desc` LIKE '%' || $2 || '%' AND attendees >= $3" +
" AND `desc` NOT LIKE '%' || $1 || '%'")
List<Party> findAllWithPositionalParams(String ex, String inc, long minimumAttendees);
@Query("#{#n1ql.delete} WHERE #{#n1ql.filter} AND `desc` LIKE '%' || $2 || '%' AND attendees < $3" +
" AND `desc` NOT LIKE '%' || $1 || '%' #{#n1ql.returning}")
List<Party> removeWithPositionalParams(String ex, String inc, long minimumAttendees);
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND `desc` LIKE '%' || $2 || '%' AND attendees >= $3" +
" AND `desc` NOT LIKE '%' || $1 || '%' AND `desc` != \"this is \\\"$excluded\\\"\"")
List<Party> findAllWithPositionalParamsAndQuotedNamedParams(@Param("excluded") String ex,
@Param("included") String inc, @Param("min") long min);
List<Party> findByDescriptionOrName(String description, String name);
List<Party> removeByDescriptionOrName(String description, String name);
@Query("#{#n1ql.selectEntity} where #{#n1ql.filter} and eventDate = $1")
List<Party> getByEventDate(Date eventDate);
List<Party> findByDescriptionStartingWith(String description);
}

View File

@@ -1,13 +1,10 @@
package org.springframework.data.couchbase.repo;
package org.springframework.data.couchbase.repository;
import java.util.List;
import org.springframework.data.couchbase.core.query.N1qlPrimaryIndexed;
import org.springframework.data.couchbase.repository.Item;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
@N1qlPrimaryIndexed
public interface ItemRepository extends CrudRepository<Item, String> {

View File

@@ -25,67 +25,57 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataRetrievalFailureException;
//import org.springframework.data.couchbase.ContainerResourceRunner;
import org.springframework.data.couchbase.ContainerResourceRunner;
import org.springframework.data.couchbase.IntegrationTestApplicationConfig;
import org.springframework.data.couchbase.repository.config.EnableCouchbaseRepositories;
// must not be in same package as CouchbaseRepository, otherwise AutoWired will fail on couchbaseRepository
import org.springframework.data.couchbase.repo.ItemRepository;
import org.springframework.data.couchbase.repo.PartyRepository;
import org.springframework.data.couchbase.repo.PartyPagingRepository;
import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping;
import org.springframework.data.couchbase.repository.support.CouchbaseRepositoryFactory;
import org.springframework.data.couchbase.repository.support.IndexManager;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mapping.model.MappingInstantiationException;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.*;
/**
* This has been copied from 3.2.x for testing backport of fix in DATACOUCH-484
* This and dependent class have been copied from src/integration to src/test
* and the repository classes have been moved from org.springframework.data.couchbase.repository
* to org.springframework.data.couchbase.repo so that
* org.springframework.data.couchbase.repository.CouchbaseRepository is not instantiated by
* @Autowired
*
* This tests PaginAndSortingRepository features in the Couchbase connector.
*
* @author Simon Baslé
* @author Subhashni Balakrishnan
* @author Michael Reiche
*/
// @RunWith(ContainerResourceRunner.class)
@RunWith(SpringJUnit4ClassRunner.class)
@RunWith(ContainerResourceRunner.class)
@ContextConfiguration(classes = IntegrationTestApplicationConfig.class)
@SpringJUnitConfig(IntegrationTestApplicationConfig.class)
@TestExecutionListeners(PartyPopulatorListener.class)
@EnableCouchbaseRepositories
public class N1qlCouchbaseRepositoryIntegrationTests {
public class N1qlCouchbaseRepositoryTests {
@Autowired private PartyPagingRepository repository;
@Autowired private RepositoryOperationsMapping operationsMapping;
@Autowired private PartyRepository partyRepository;
@Autowired private IndexManager indexManager;
@Autowired private ItemRepository itemRepository;
private PartyPagingRepository repository;
private PartyRepository partyRepository;
private ItemRepository itemRepository;
private final String KEY_PARTY = "Party1";
private final String KEY_ITEM = "Item1";
@Before
public void setup() throws Exception {
RepositoryFactorySupport factory = new CouchbaseRepositoryFactory(operationsMapping, indexManager);
repository = factory.getRepository(PartyPagingRepository.class);
partyRepository = factory.getRepository(PartyRepository.class);
itemRepository = factory.getRepository(ItemRepository.class);
partyRepository.save(new Party(KEY_PARTY, "partyName", "MatchingDescription", null, 1, null));
itemRepository.save(new Item(KEY_ITEM, "MatchingDescription"));
}
@@ -144,7 +134,7 @@ public class N1qlCouchbaseRepositoryIntegrationTests {
@Test
public void shouldFindAllWithSort() {
Iterable<Party> allByAttendanceDesc = repository.findAll(Sort.by(Sort.Direction.DESC, "attendees"));
Iterable<Party> allByAttendanceDesc = repository.findAll(new Sort(Sort.Direction.DESC, "attendees"));
long previousAttendance = Long.MAX_VALUE;
for (Party party : allByAttendanceDesc) {
assertTrue(party.getAttendees() <= previousAttendance);
@@ -155,7 +145,7 @@ public class N1qlCouchbaseRepositoryIntegrationTests {
@Test
public void shouldSortOnRenamedFieldIfJsonNameIsProvidedInSort() {
Iterable<Party> parties = repository.findAll(Sort.by(Sort.Direction.DESC, "desc"));
Iterable<Party> parties = repository.findAll(new Sort(Sort.Direction.DESC, "desc"));
String previousDesc = null;
for (Party party : parties) {
if (previousDesc != null) {
@@ -168,7 +158,7 @@ public class N1qlCouchbaseRepositoryIntegrationTests {
@Test
public void shouldSortWithoutCaseSensitivity() {
Iterable<Party> parties = repository.findAll(Sort.by(new Sort.Order(Sort.Direction.DESC, "desc").ignoreCase()));
Iterable<Party> parties = repository.findAll(new Sort(new Sort.Order(Sort.Direction.DESC, "desc").ignoreCase()));
String previousDesc = null;
for (Party party : parties) {
if (previousDesc != null) {
@@ -181,7 +171,7 @@ public class N1qlCouchbaseRepositoryIntegrationTests {
@Test
public void shouldPageThroughEntities() {
Pageable pageable = PageRequest.of(0, 8);
Pageable pageable = new PageRequest(0, 8);
Page<Party> page1 = repository.findAll(pageable);
assertTrue("Query for parties should be atleast 12", page1.getTotalElements() >= 12);
@@ -190,7 +180,7 @@ public class N1qlCouchbaseRepositoryIntegrationTests {
@Test
public void shouldPageThroughSortedEntities() {
Pageable pageable = PageRequest.of(0, 8, Sort.Direction.DESC, "attendees");
Pageable pageable = new PageRequest(0, 8, Sort.Direction.DESC, "attendees");
Page<Party> page1 = repository.findAll(pageable);
assertTrue("Query for parties should be atleast 12", page1.getTotalElements() >= 12);
@@ -214,7 +204,7 @@ public class N1qlCouchbaseRepositoryIntegrationTests {
@Test
public void shouldPageWithStringBasedQuery() {
Pageable pageable = PageRequest.of(0, 8, Sort.Direction.DESC, "attendees");
Pageable pageable = new PageRequest(0, 8, Sort.Direction.DESC, "attendees");
Page<Party> page1 = partyRepository.findPartiesWithAttendee(1, pageable);
assertTrue("Query for parties with attendees should be atleast 12", page1.getTotalElements() >= 12);
assertEquals(8, page1.getNumberOfElements());
@@ -241,7 +231,7 @@ public class N1qlCouchbaseRepositoryIntegrationTests {
// Fails on deserialization as a different entity item is also present
@Test(expected = MappingInstantiationException.class)
public void shouldFailWithMissingFilterStringBasedQuery() {
Sort sort = Sort.by(Sort.Direction.DESC, "attendees");
Sort sort = new Sort(Sort.Direction.DESC, "attendees");
partyRepository.findParties(sort);
}
@@ -273,5 +263,4 @@ public class N1qlCouchbaseRepositoryIntegrationTests {
List<Party> partyList = partyRepository.findByDescriptionStartingWith(description);
assertTrue(partyList.size() == 0);
}
}

View File

@@ -0,0 +1,4 @@
package org.springframework.data.couchbase.repository;
public interface PartyPagingRepository extends CouchbasePagingAndSortingRepository<Party, String> {
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright 2017-2020 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.repository;
import java.util.Date;
import java.util.List;
import org.springframework.data.couchbase.core.query.N1qlPrimaryIndexed;
import org.springframework.data.couchbase.core.query.N1qlSecondaryIndexed;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.couchbase.core.query.View;
import org.springframework.data.couchbase.core.query.ViewIndexed;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.query.Param;
/**
* @author Simon Baslé
* @author Subhashni Balakrishnan
*/
@ViewIndexed(designDoc = "party", viewName = "all")
@N1qlPrimaryIndexed
@N1qlSecondaryIndexed(indexName = "party")
public interface PartyRepository extends CouchbaseRepository<Party, String> {
List<Party> findByAttendeesGreaterThanEqual(int minAttendees);
List<Party> findByEventDateIs(Date targetDate);
List<Party> findByName(String name);
@View(designDocument = "party", viewName = "byDate")
List<Party> findFirst3ByEventDateGreaterThanEqual(Date targetDate);
List<Object> findAllByDescriptionNotNull();
long countAllByDescriptionNotNull();
@Query("SELECT MAX(attendees) FROM #{#n1ql.bucket} WHERE #{#n1ql.filter}")
long findMaxAttendees();
@Query("SELECT `desc` FROM #{#n1ql.bucket} WHERE #{#n1ql.filter}")
String findSomeString();
@Query("SELECT count(*) + 5 FROM #{#n1ql.bucket} WHERE #{#n1ql.filter}")
long countCustomPlusFive();
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter}")
long countCustom();
@Query("SELECT 1 = 1")
boolean justABoolean();
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND `attendees` >= $1")
Page<Party> findPartiesWithAttendee(int count, Pageable pageable);
@Query("#{#n1ql.selectEntity}")
List<Party> findParties(Sort sort);
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND `desc` LIKE '%' || $included || '%' AND attendees >= $min"
+ " AND `desc` NOT LIKE '%' || $excluded || '%'")
List<Party> findAllWithNamedParams(@Param("excluded") String ex, @Param("included") String inc,
@Param("min") long minimumAttendees);
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND `desc` LIKE '%' || $2 || '%' AND attendees >= $3"
+ " AND `desc` NOT LIKE '%' || $1 || '%'")
List<Party> findAllWithPositionalParams(String ex, String inc, long minimumAttendees);
@Query("#{#n1ql.delete} WHERE #{#n1ql.filter} AND `desc` LIKE '%' || $2 || '%' AND attendees < $3"
+ " AND `desc` NOT LIKE '%' || $1 || '%' #{#n1ql.returning}")
List<Party> removeWithPositionalParams(String ex, String inc, long minimumAttendees);
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND `desc` LIKE '%' || $2 || '%' AND attendees >= $3"
+ " AND `desc` NOT LIKE '%' || $1 || '%' AND `desc` != \"this is \\\"$excluded\\\"\"")
List<Party> findAllWithPositionalParamsAndQuotedNamedParams(@Param("excluded") String ex,
@Param("included") String inc, @Param("min") long min);
List<Party> findByDescriptionOrName(String description, String name);
List<Party> removeByDescriptionOrName(String description, String name);
@Query("#{#n1ql.selectEntity} where #{#n1ql.filter} and eventDate = $1")
List<Party> getByEventDate(Date eventDate);
List<Party> findByDescriptionStartingWith(String description);
}