/**
* Copyright (c) 2016, All Contributors (see CONTRIBUTORS file)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package com.eventsourcing.index;
import com.eventsourcing.Entity;
import com.eventsourcing.EntityHandle;
import com.eventsourcing.ResolvedEntityHandle;
import com.eventsourcing.StandardEntity;
import com.googlecode.cqengine.ConcurrentIndexedCollection;
import com.googlecode.cqengine.IndexedCollection;
import com.googlecode.cqengine.index.AttributeIndex;
import com.googlecode.cqengine.index.hash.HashIndex;
import com.googlecode.cqengine.query.Query;
import com.googlecode.cqengine.query.option.QueryOptions;
import com.googlecode.cqengine.resultset.ResultSet;
import lombok.Getter;
import org.testng.annotations.Test;
import java.util.Arrays;
import java.util.List;
import static com.googlecode.cqengine.query.QueryFactory.equal;
import static com.googlecode.cqengine.query.QueryFactory.noQueryOptions;
import static org.testng.Assert.assertEquals;
// This test was originally copied from CQEngine in order to test
// indices the same way CQEngine does.
public abstract class UniqueIndexTest<UniqueIndex extends AttributeIndex> {
public abstract <A, O extends Entity> UniqueIndex onAttribute(Attribute<O, A> attribute);
public static class Car extends StandardEntity {
@Getter
public int carId;
@Getter
public String name;
@Getter
public String description;
@Getter
public List<String> features;
public Car() {}
public Car(int carId, String name, String description, List<String> features) {
this.carId = carId;
this.name = name;
this.description = description;
this.features = features;
}
@Override
public String toString() {
return "Car{carId=" + carId + ", name='" + name + "', description='" + description + "', features=" + features + "}";
}
// -------------------------- Attributes --------------------------
public static final Attribute<Car, Integer> CAR_ID = new SimpleAttribute<Car, Integer>(
"carId") {
public Integer getValue(Car car, QueryOptions queryOptions) { return car.carId; }
};
public static final Attribute<Car, String> NAME = new SimpleAttribute<Car, String>(
"name") {
public String getValue(Car car, QueryOptions queryOptions) { return car.name; }
};
public static final Attribute<Car, String> DESCRIPTION = new SimpleAttribute<Car, String>(
"description") {
public String getValue(Car car, QueryOptions queryOptions) { return car.description; }
};
public static final Attribute<Car, String> FEATURES = new MultiValueAttribute<Car, String>("features") {
public List<String> getValues(Car car, QueryOptions queryOptions) { return car.features; }
};
}
@Test
public void uniqueIndex() {
IndexedCollection<EntityHandle<Car>> cars = new ConcurrentIndexedCollection<>();
// Add some indexes...
UniqueIndex index = onAttribute(Car.CAR_ID);
cars.addIndex(index);
HashIndex<Integer, EntityHandle<Car>> index1 = HashIndex.onAttribute(Car.CAR_ID);
cars.addIndex(index1);
index.clear(noQueryOptions());
index1.clear(noQueryOptions());
// Add some objects to the collection...
cars.add(new ResolvedEntityHandle<>(new Car(1, "ford focus", "great condition, low mileage", Arrays.asList("spare tyre",
"sunroof"))));
cars.add(new ResolvedEntityHandle<>(new Car(2, "ford taurus", "dirty and unreliable, flat tyre", Arrays.asList("spare tyre",
"radio"))));
cars.add(new ResolvedEntityHandle<>(new Car(3, "honda civic", "has a flat tyre and high mileage", Arrays
.asList("radio"))));
Query<EntityHandle<Car>> query = equal(Car.CAR_ID, 2);
ResultSet<EntityHandle<Car>> rs = cars.retrieve(query);
assertEquals(rs.getRetrievalCost(), index.retrieve(query, noQueryOptions()).getRetrievalCost(),
"should prefer unique index over hash index");
assertEquals(rs.uniqueResult().get().carId, 2, "should retrieve car 2");
index.clear(noQueryOptions());
index1.clear(noQueryOptions());
}
@Test(expectedExceptions = com.googlecode.cqengine.index.unique.UniqueIndex.UniqueConstraintViolatedException.class)
public void duplicateObjectDetection_SimpleAttribute() {
IndexedCollection<EntityHandle<Car>> cars = new ConcurrentIndexedCollection<>();
// Add some indexes...
UniqueIndex index = onAttribute(Car.CAR_ID);
cars.addIndex(index);
index.clear(noQueryOptions());
// Add some objects to the collection...
cars.add(new ResolvedEntityHandle<>(new Car(1, "ford focus", "great condition, low mileage", Arrays.asList("spare tyre",
"sunroof"))));
cars.add(new ResolvedEntityHandle<>(new Car(2, "ford taurus", "dirty and unreliable, flat tyre", Arrays.asList("spare tyre",
"radio"))));
cars.add(new ResolvedEntityHandle<>(new Car(3, "honda civic", "has a flat tyre and high mileage", Arrays
.asList("radio"))));
cars.add(new ResolvedEntityHandle<>(new Car(2, "some other car", "foo", Arrays.asList("bar"))));
index.clear(noQueryOptions());
}
@Test(expectedExceptions = com.googlecode.cqengine.index.unique.UniqueIndex.UniqueConstraintViolatedException.class)
public void duplicateObjectDetection_MultiValueAttribute() {
IndexedCollection<EntityHandle<Car>> cars = new ConcurrentIndexedCollection<>();
// Add some indexes...
UniqueIndex index = onAttribute(Car.FEATURES);
cars.addIndex(index);
index.clear(noQueryOptions());
// Add some objects to the collection...
cars.add(new ResolvedEntityHandle<>(new Car(1, "ford focus", "foo", Arrays.asList("spare tyre", "sunroof"))));
cars.add(new ResolvedEntityHandle<>(new Car(2, "ford taurus", "bar", Arrays.asList("radio", "cd player"))));
// Try to add another car which has a cd player, when one car already has a cd player...
cars.add(new ResolvedEntityHandle<>(new Car(3, "honda civic", "baz", Arrays.asList("cd player", "bluetooth"))));
index.clear(noQueryOptions());
}
@Test
public void retrieve() {
IndexedCollection<EntityHandle<Car>> cars = new ConcurrentIndexedCollection<>();
// Add some indexes...
UniqueIndex index = onAttribute(Car.FEATURES);
cars.addIndex(index);
index.clear(noQueryOptions());
// Add some objects to the collection...
cars.add(new ResolvedEntityHandle<>(new Car(1, "ford focus", "foo", Arrays.asList("spare tyre", "sunroof"))));
cars.add(new ResolvedEntityHandle<>(new Car(2, "ford taurus", "bar", Arrays.asList("radio", "cd player"))));
ResultSet<EntityHandle<Car>> radio = cars.retrieve(equal(Car.FEATURES, "radio"));
assertEquals(radio.size(), 1);
radio.close();
ResultSet<EntityHandle<Car>> unknown = cars.retrieve(equal(Car.FEATURES, "unknown"));
assertEquals(unknown.size(), 0);
unknown.close();
index.clear(noQueryOptions());
}
@Test
public void indexingExistingData() {
IndexedCollection<EntityHandle<Car>> cars = new ConcurrentIndexedCollection<>();
UniqueIndex index = onAttribute(Car.FEATURES);
index.clear(noQueryOptions());
// Add some objects to the collection...
cars.add(new ResolvedEntityHandle<>(new Car(1, "ford focus", "foo", Arrays.asList("spare tyre", "sunroof"))));
cars.add(new ResolvedEntityHandle<>(new Car(2, "ford taurus", "bar", Arrays.asList("radio", "cd player"))));
// Add some indexes...
cars.addIndex(index);
ResultSet<EntityHandle<Car>> radio = cars.retrieve(equal(Car.FEATURES, "radio"));
assertEquals(radio.size(), 1);
radio.close();
ResultSet<EntityHandle<Car>> unknown = cars.retrieve(equal(Car.FEATURES, "unknown"));
assertEquals(unknown.size(), 0);
unknown.close();
index.clear(noQueryOptions());
}
@Test
public void reindexData() {
IndexedCollection<EntityHandle<Car>> cars = new ConcurrentIndexedCollection<>();
UniqueIndex index = onAttribute(Car.FEATURES);
index.clear(noQueryOptions());
// Add some objects to the collection...
cars.add(new ResolvedEntityHandle<>(new Car(1, "ford focus", "foo", Arrays.asList("spare tyre", "sunroof"))));
cars.add(new ResolvedEntityHandle<>(new Car(2, "ford taurus", "bar", Arrays.asList("radio", "cd player"))));
cars.addIndex(index);
IndexedCollection<EntityHandle<Car>> cars1 = new ConcurrentIndexedCollection<>();
UniqueIndex index1 = onAttribute(Car.FEATURES);
cars1.addAll(cars);
cars1.addIndex(index1);
ResultSet<EntityHandle<Car>> radio = cars.retrieve(equal(Car.FEATURES, "radio"));
assertEquals(radio.size(), 1);
radio.close();
index.clear(noQueryOptions());
index1.clear(noQueryOptions());
}
}