DATACOUCH-484 - Thread Safety of query parameters.

This commit is contained in:
mikereiche
2020-07-01 12:22:18 -07:00
parent ae0933978f
commit 272fcf10f3
9 changed files with 704 additions and 6 deletions

View File

@@ -50,16 +50,21 @@ import org.springframework.util.Assert;
public class PartTreeN1qlBasedQuery extends AbstractN1qlBasedQuery {
private final PartTree partTree;
private JsonValue placeHolderValues;
private ThreadLocal<JsonValue> placeHolderValues;
public PartTreeN1qlBasedQuery(CouchbaseQueryMethod queryMethod, CouchbaseOperations couchbaseOperations) {
super(queryMethod, couchbaseOperations);
this.partTree = new PartTree(queryMethod.getName(), queryMethod.getEntityInformation().getJavaType());
this.placeHolderValues = new ThreadLocal<JsonValue>() {
@Override public JsonValue initialValue() {
return JsonArray.create();
}
};
}
@Override
protected JsonValue getPlaceholderValues(ParameterAccessor accessor) {
return this.placeHolderValues;
return this.placeHolderValues.get();
}
@Override
@@ -70,7 +75,7 @@ public class PartTreeN1qlBasedQuery extends AbstractN1qlBasedQuery {
N1qlCountQueryCreator queryCountCreator = new N1qlCountQueryCreator(partTree, accessor, countFrom,
getCouchbaseOperations().getConverter(), getQueryMethod());
Statement statement = queryCountCreator.createQuery();
this.placeHolderValues = queryCountCreator.getPlaceHolderValues();
this.placeHolderValues.set(queryCountCreator.getPlaceHolderValues());
return statement;
}
@@ -83,7 +88,7 @@ public class PartTreeN1qlBasedQuery extends AbstractN1qlBasedQuery {
DeleteUsePath deleteUsePath = deleteFrom(bucket);
N1qlMutateQueryCreator mutateQueryCreator = new N1qlMutateQueryCreator(partTree, accessor, deleteUsePath, getCouchbaseOperations().getConverter(), getQueryMethod());
MutateLimitPath mutateFromWhereOrderBy = mutateQueryCreator.createQuery();
this.placeHolderValues = mutateQueryCreator.getPlaceHolderValues();
this.placeHolderValues.set(mutateQueryCreator.getPlaceHolderValues());
if (partTree.isLimiting()) {
return mutateFromWhereOrderBy.limit(partTree.getMaxResults());
@@ -99,9 +104,9 @@ public class PartTreeN1qlBasedQuery extends AbstractN1qlBasedQuery {
}
WherePath selectFrom = select.from(bucket);
N1qlQueryCreator queryCreator = new N1qlQueryCreator(partTree, accessor, selectFrom,
getCouchbaseOperations().getConverter(), getQueryMethod());
getCouchbaseOperations().getConverter(), getQueryMethod());
LimitPath selectFromWhereOrderBy = queryCreator.createQuery();
this.placeHolderValues = queryCreator.getPlaceHolderValues();
this.placeHolderValues.set(queryCreator.getPlaceHolderValues());
if (queryMethod.isPageQuery()) {
Pageable pageable = accessor.getPageable();

View File

@@ -0,0 +1,78 @@
package org.springframework.data.couchbase;
import java.util.Collections;
import java.util.List;
import com.couchbase.client.java.env.CouchbaseEnvironment;
import com.couchbase.client.java.env.DefaultCouchbaseEnvironment;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration;
import org.springframework.data.couchbase.config.CouchbaseConfigurer;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.data.couchbase.core.WriteResultChecking;
import org.springframework.data.couchbase.core.query.Consistency;
import org.springframework.data.couchbase.repository.config.EnableCouchbaseRepositories;
import org.springframework.data.couchbase.repository.support.IndexManager;
@Configuration
@EnableCouchbaseRepositories(basePackages = "org.springframework.data.couchbase.repo")
public class IntegrationTestApplicationConfig extends AbstractCouchbaseConfiguration {
@Bean
public String couchbaseAdminUser() {
return "Administrator";
}
@Bean
public String couchbaseAdminPassword() {
return "password";
}
@Override
protected List<String> getBootstrapHosts() {
return Collections.singletonList("127.0.0.1");
}
@Override
protected String getBucketName() {
return "protected";
}
@Override
protected String getBucketPassword() {
return "password";
}
// TODO maybe create the bucket if doesn't exist
@Override
protected CouchbaseEnvironment getEnvironment() {
return DefaultCouchbaseEnvironment.builder().connectTimeout(10000).kvTimeout(10000).queryTimeout(10000)
.viewTimeout(10000).build();
}
@Override
public CouchbaseTemplate couchbaseTemplate() throws Exception {
CouchbaseTemplate template = super.couchbaseTemplate();
template.setWriteResultChecking(WriteResultChecking.LOG);
return template;
}
// this is for dev so it is ok to auto-create indexes
@Override
public IndexManager indexManager() {
return new IndexManager();
}
@Override
protected Consistency getDefaultConsistency() {
return Consistency.READ_YOUR_OWN_WRITES;
}
@Override
protected CouchbaseConfigurer couchbaseConfigurer() {
return this;
}
}

View File

@@ -0,0 +1,15 @@
package org.springframework.data.couchbase.repo;
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> {
List<Object> findAllByDescriptionNotNull();
}

View File

@@ -0,0 +1,9 @@
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

@@ -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.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

@@ -0,0 +1,38 @@
package org.springframework.data.couchbase.repository;
import com.couchbase.client.java.repository.annotation.Field;
import org.springframework.data.annotation.Id;
public class Item {
@Id
public String id;
@Field("desc")
public String description;
public Item(String id, String description) {
this.id = id;
this.description = description;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Item item = (Item) o;
if (!id.equals(item.id)) return false;
return !(description != null ? !description.equals(item.description) : item.description != null);
}
@Override
public int hashCode() {
int result = id.hashCode();
result = 31 * result + (description != null ? description.hashCode() : 0);
return result;
}
}

View File

@@ -0,0 +1,277 @@
/*
* Copyright 2013-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 static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
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.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.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.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;
/**
* 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)
@ContextConfiguration(classes = IntegrationTestApplicationConfig.class)
@SpringJUnitConfig(IntegrationTestApplicationConfig.class)
@TestExecutionListeners(PartyPopulatorListener.class)
@EnableCouchbaseRepositories
public class N1qlCouchbaseRepositoryIntegrationTests {
@Autowired private PartyPagingRepository repository;
@Autowired private PartyRepository partyRepository;
@Autowired private ItemRepository itemRepository;
private final String KEY_PARTY = "Party1";
private final String KEY_ITEM = "Item1";
@Before
public void setup() throws Exception {
partyRepository.save(new Party(KEY_PARTY, "partyName", "MatchingDescription", null, 1, null));
itemRepository.save(new Item(KEY_ITEM, "MatchingDescription"));
}
@After
public void cleanUp() {
try {
itemRepository.deleteById(KEY_ITEM);
} catch (DataRetrievalFailureException e) {}
try {
partyRepository.deleteById(KEY_PARTY);
} catch (DataRetrievalFailureException e) {}
}
@Test
public void shouldBeThreadsafe() {
// This doesn't guarantee it, but we should catch most thread issues without
// taking too long here...
int runs = 50;
for (int i = 0; i < runs; i++) {
doShouldBeThreadsafe();
}
}
public void doShouldBeThreadsafe() {
int threads = 50;
for (int thread = 0; thread < threads; thread++) {
partyRepository.save(new Party(KEY_PARTY + thread, "Party like it's 199" + thread, "", null, 1, null));
}
ExecutorService service = Executors.newFixedThreadPool(threads);
List<Callable<Boolean>> callables = new ArrayList<>();
for (int thread = 0; thread < threads; ++thread) {
final int counter = thread;
Callable<Boolean> booleanSupplier = () -> {
String expectedName = "Party like it's 199" + counter;
String foundName = partyRepository.findByName(expectedName).get(0).getName();
return expectedName.equals(foundName); // should never get false
};
callables.add(booleanSupplier);
}
try {
List<Future<Boolean>> futures = service.invokeAll(callables);
service.shutdown();
service.awaitTermination(5, TimeUnit.SECONDS);
for (Future<Boolean> future : futures) {
assertTrue(future.get());
}
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
fail("Threads failed to run " + e.getMessage());
}
for (int thread = 0; thread < threads; thread++) {
partyRepository.delete(new Party(KEY_PARTY + thread, "Party like it's 199" + thread, "", null, 1, null));
}
}
@Test
public void shouldFindAllWithSort() {
Iterable<Party> allByAttendanceDesc = repository.findAll(Sort.by(Sort.Direction.DESC, "attendees"));
long previousAttendance = Long.MAX_VALUE;
for (Party party : allByAttendanceDesc) {
assertTrue(party.getAttendees() <= previousAttendance);
previousAttendance = party.getAttendees();
}
assertFalse("Expected to find several parties", previousAttendance == Long.MAX_VALUE);
}
@Test
public void shouldSortOnRenamedFieldIfJsonNameIsProvidedInSort() {
Iterable<Party> parties = repository.findAll(Sort.by(Sort.Direction.DESC, "desc"));
String previousDesc = null;
for (Party party : parties) {
if (previousDesc != null) {
assertTrue(party.getDescription().compareTo(previousDesc) <= 0);
}
previousDesc = party.getDescription();
}
assertNotNull("Expected to find several parties", previousDesc);
}
@Test
public void shouldSortWithoutCaseSensitivity() {
Iterable<Party> parties = repository.findAll(Sort.by(new Sort.Order(Sort.Direction.DESC, "desc").ignoreCase()));
String previousDesc = null;
for (Party party : parties) {
if (previousDesc != null) {
assertTrue(party.getDescription().compareToIgnoreCase(previousDesc) <= 0);
}
previousDesc = party.getDescription();
}
assertNotNull("Expected to find several parties", previousDesc);
}
@Test
public void shouldPageThroughEntities() {
Pageable pageable = PageRequest.of(0, 8);
Page<Party> page1 = repository.findAll(pageable);
assertTrue("Query for parties should be atleast 12", page1.getTotalElements() >= 12);
assertEquals(8, page1.getNumberOfElements());
}
@Test
public void shouldPageThroughSortedEntities() {
Pageable pageable = PageRequest.of(0, 8, Sort.Direction.DESC, "attendees");
Page<Party> page1 = repository.findAll(pageable);
assertTrue("Query for parties should be atleast 12", page1.getTotalElements() >= 12);
assertEquals(8, page1.getNumberOfElements());
List<Party> parties = page1.getContent();
Long previousAttendees = null;
for (Party party : parties) {
if (previousAttendees != null) {
assertTrue(party.getAttendees() <= previousAttendees);
}
previousAttendees = party.getAttendees();
}
}
@Test
public void testWrapWhereCriteria() {
List<Party> partyList = partyRepository.findByDescriptionOrName("MatchingDescription", "partyName");
assertTrue(partyList.size() == 1);
}
@Test
public void shouldPageWithStringBasedQuery() {
Pageable pageable = PageRequest.of(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());
List<Party> parties = page1.getContent();
Long previousAttendees = null;
for (Party party : parties) {
if (previousAttendees != null) {
assertTrue(party.getAttendees() <= previousAttendees);
}
previousAttendees = party.getAttendees();
}
Page<Party> page2 = partyRepository.findPartiesWithAttendee(1, page1.nextPageable());
assertEquals(8, page2.getNumberOfElements());
parties = page2.getContent();
for (Party party : parties) {
if (previousAttendees != null) {
assertTrue(party.getAttendees() <= previousAttendees);
}
previousAttendees = party.getAttendees();
}
}
// 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");
partyRepository.findParties(sort);
}
@Test
public void testDeleteQuery() {
partyRepository.save(new Party("testDeleteQuery", "delete", "delete", null, 0, null));
List<Party> partyList = partyRepository.removeByDescriptionOrName("delete", "delete");
assertTrue(partyList.size() == 1);
}
@Test
public void testSpelDateConvertion() {
final String key = "testSpelDateConvertion";
Calendar cal = Calendar.getInstance();
cal.clear();
cal.set(2018, Calendar.SEPTEMBER, 10);
Date date = cal.getTime();
partyRepository.save(new Party(key, "", "", date, 0, null));
List<Party> partyList = partyRepository.getByEventDate(date);
assertTrue(partyList.size() == 1);
assertEquals("Key mismatch", partyList.get(0).getKey(), key);
}
@Test
public void testN1qlQueryWithInvalidValue() {
partyRepository
.save(new Party("testN1qlQueryWithInvalidValue", "", "testN1qlQueryWithInvalidValue", null, 0, null));
final String description = "testN1qlQueryWithInvalidValue* OR `description` LIKE \"\"";
List<Party> partyList = partyRepository.findByDescriptionStartingWith(description);
assertTrue(partyList.size() == 0);
}
}

View File

@@ -0,0 +1,88 @@
package org.springframework.data.couchbase.repository;
import java.util.Date;
import org.springframework.data.annotation.Id;
import org.springframework.data.geo.Point;
import com.couchbase.client.java.repository.annotation.Field;
/**
* An entity used to test conversion of parameters in query derivations.
*
* @author Simon Baslé
*/
public class Party {
@Id
private final String key;
private final String name;
@Field("desc")
private final String description;
private final Date eventDate;
private final long attendees;
private final Point location;
public Party(String key, String name, String description, Date eventDate, long attendees, Point location) {
this.key = key;
this.name = name;
this.description = description;
this.eventDate = eventDate;
this.attendees = attendees;
this.location = location;
}
public String getKey() {
return key;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public Date getEventDate() {
return eventDate;
}
public long getAttendees() {
return attendees;
}
public Point getLocation() {
return location;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Party party = (Party) o;
return key.equals(party.key);
}
@Override
public int hashCode() {
return key.hashCode();
}
@Override
public String toString() {
return "Party{" +
"name='" + name + '\'' +
", eventDate=" + eventDate +
", location=" + location +
'}';
}
}

View File

@@ -0,0 +1,87 @@
package org.springframework.data.couchbase.repository;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.PersistTo;
import com.couchbase.client.java.ReplicateTo;
import com.couchbase.client.java.cluster.ClusterInfo;
import com.couchbase.client.java.view.DefaultView;
import com.couchbase.client.java.view.DesignDocument;
import com.couchbase.client.java.view.SpatialView;
import com.couchbase.client.java.view.View;
import org.springframework.data.couchbase.config.BeanNames;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.data.geo.Point;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
/**
* @author Simon Baslé
*/
public class PartyPopulatorListener extends DependencyInjectionTestExecutionListener {
@Override
public void beforeTestClass(final TestContext testContext) throws Exception {
Bucket client = (Bucket) testContext.getApplicationContext().getBean(BeanNames.COUCHBASE_BUCKET);
ClusterInfo clusterInfo = (ClusterInfo) testContext.getApplicationContext().getBean(BeanNames.COUCHBASE_CLUSTER_INFO);
populateTestData(client, clusterInfo);
createAndWaitForDesignDocs(client);
}
private void populateTestData(Bucket client, ClusterInfo clusterInfo) {
CouchbaseTemplate template = new CouchbaseTemplate(clusterInfo, client);
Calendar cal = Calendar.getInstance();
cal.clear();
cal.set(Calendar.YEAR, 2015);
cal.set(Calendar.DAY_OF_MONTH, 10);
cal.set(Calendar.MONTH, Calendar.JANUARY);
for (int i = 0; i < 12; i++) {
Party p = new Party("testparty-" + i, "party like it's 199" + i,
"An awesome party, 90's themed, every 10 of the month",
cal.getTime(), 100 + i * 10,
new Point(i, -i));
template.save(p, PersistTo.MASTER, ReplicateTo.NONE);
cal.roll(Calendar.MONTH, true);
}
cal.clear();
cal.set(Calendar.YEAR, 1990);
cal.set(Calendar.MONTH, Calendar.JANUARY);
cal.set(Calendar.DAY_OF_MONTH, 01);
template.save(new Party("aTestParty", "New Year's Eve 90", "Happy New Year", cal.getTime(), 1230000, new Point(100, 100)));
template.save(new Party("lowercaseParty", "lowercase party", "lowercase party", cal.getTime(), 1000, new Point(100, 100)));
template.save(new Party("uppercaseParty", "Uppercase party", "Uppercase party", cal.getTime(), 1000, new Point(100, 100)));
}
private void createAndWaitForDesignDocs(Bucket client) {
//standard views
List<View> views = new ArrayList<View>();
String mapFunction = "function (doc, meta) { if(doc._class == \"" + Party.class.getName() + "\") " +
"{ emit(doc.eventDate, null); } }";
views.add(DefaultView.create("byDate", mapFunction, "_count"));
//create the view design document
DesignDocument designDoc = DesignDocument.create("party", views);
client.bucketManager().upsertDesignDocument(designDoc);
//geo views
List<View> geoViews = new ArrayList<View>();
mapFunction = "function (doc, meta) { if(doc._class == \"" + Party.class.getName() + "\") " +
"{ emit([doc.location.x, doc.location.y], null); } }";
geoViews.add(SpatialView.create("byLocation", mapFunction));
mapFunction = "function (doc, meta) { if(doc._class == \"" + Party.class.getName() + "\") " +
"{ emit([doc.location.x, doc.location.y, doc.attendees], null); } }";
geoViews.add(SpatialView.create("byLocationAndAttendees", mapFunction));
//create the geo views design document
DesignDocument geoDesignDoc = DesignDocument.create("partyGeo", geoViews);
client.bucketManager().upsertDesignDocument(geoDesignDoc);
}
}