package org.gbif.occurrence.search;
import org.gbif.api.model.common.search.Facet;
import org.gbif.api.model.common.search.SearchResponse;
import org.gbif.api.model.occurrence.Occurrence;
import org.gbif.api.model.occurrence.search.OccurrenceSearchParameter;
import org.gbif.api.model.occurrence.search.OccurrenceSearchRequest;
import org.gbif.api.service.checklistbank.NameUsageMatchingService;
import org.gbif.api.service.occurrence.OccurrenceSearchService;
import org.gbif.api.service.occurrence.OccurrenceService;
import org.gbif.api.vocabulary.BasisOfRecord;
import org.gbif.api.vocabulary.Continent;
import org.gbif.api.vocabulary.Country;
import org.gbif.api.vocabulary.MediaType;
import org.gbif.api.vocabulary.OccurrenceIssue;
import org.gbif.api.vocabulary.TypeStatus;
import org.gbif.common.search.solr.SolrConfig;
import org.gbif.common.search.solr.SolrModule;
import org.gbif.occurrence.common.config.OccHBaseConfiguration;
import org.gbif.occurrence.persistence.OccurrencePersistenceServiceImpl;
import org.gbif.occurrence.search.writer.SolrOccurrenceWriter;
import org.gbif.occurrence.search.writers.HBasePredicateWriter;
import org.gbif.occurrence.search.writers.SolrPredicateWriter;
import org.gbif.service.guice.PrivateServiceModule;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Properties;
import java.util.UUID;
import com.google.common.io.Resources;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Provides;
import com.google.inject.Singleton;
import org.apache.commons.io.IOUtils;
import org.apache.hadoop.hbase.HBaseTestingUtility;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import org.apache.hadoop.hbase.client.Table;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrServerException;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.Mockito;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Test class for integration tests for the OccurrenceSearchService class.
* Internally loads data from a csv file into a HBase minicluster and an embedded Solr server; both server instances are
* shared among test cases.
*/
public class OccurrenceSearchTestIT {
/**
* Test guice module.
* Exposes SolrServer and OccurrenceSearchService instances.
*/
public static class OccurrenceSearchTestModule extends PrivateServiceModule {
private static final String PREFIX = "occurrence.search.";
private final SolrConfig solrConfig;
/**
* Default constructor.
*/
public OccurrenceSearchTestModule(Properties properties) {
super(PREFIX, properties);
solrConfig = SolrConfig.fromProperties(properties, PREFIX + "solr.");
}
/**
* Provides instances of OccurrenceService, this is required by the OccurrenceSearchService.
*/
@Provides
@Singleton
public OccurrenceService provideOccurrenceService() {
OccurrenceService occurrenceService = new OccurrencePersistenceServiceImpl(CFG, hbaseConnection);
return occurrenceService;
}
@Override
protected void configureService() {
install(new SolrModule(solrConfig));
bind(NameUsageMatchingService.class).toInstance(Mockito.mock(NameUsageMatchingService.class));
bind(OccurrenceSearchService.class).to(OccurrenceSearchImpl.class);
expose(OccurrenceSearchService.class);
// Exposes the SolrClient because it is required to create the index.
expose(SolrClient.class);
}
}
private static final Injector injector = getInjector();
// Test file
private static final String CSV_TEST_FILE = "occurrences-test.csv";
// HBase mini-cluster variables
private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
private static final OccHBaseConfiguration CFG = new OccHBaseConfiguration();
static {
CFG.setEnvironment("test");
}
private static final TableName OCCURRENCE_TABLE = TableName.valueOf(CFG.occTable);
private static final String CF_NAME = "o";
private static final byte[] CF = Bytes.toBytes(CF_NAME);
private static Connection hbaseConnection;
// Solr server
private static SolrClient solrClient;
// Search service
private static OccurrenceSearchService occurrenceSearchService;
private static final Logger LOG = LoggerFactory.getLogger(OccurrenceSearchTestIT.class);
@AfterClass
public static void afterClass() throws Exception {
TEST_UTIL.shutdownMiniCluster();
solrClient.close();
}
@BeforeClass
public static void beforeClass() throws Exception {
TEST_UTIL.startMiniCluster(1);
TEST_UTIL.createTable(OCCURRENCE_TABLE, CF);
hbaseConnection = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration());
occurrenceSearchService = injector.getInstance(OccurrenceSearchService.class);
solrClient = injector.getInstance(SolrClient.class);
// deletes all previous data
solrClient.deleteByQuery("*:*");
loadOccurrences();
}
/**
* Commits changes to the Solr server.
* Exceptions {@link SolrServerException} and {@link IOException} are swallowed.
*/
private static void commitToSolrQuietly() {
try {
solrClient.commit();
} catch (SolrServerException e) {
LOG.error("Solr error commiting on Solr server", e);
} catch (IOException e) {
LOG.error("I/O error commiting on Solr server", e);
}
}
/**
* Creates an instance of a guice injector using the OccurrenceSearchTestModule.
*
* @return
*/
private static Injector getInjector() {
InputStream inputStream = null;
try {
Properties properties = new Properties();
inputStream = Resources.newInputStreamSupplier(Resources.getResource("occurrence.properties")).getInput();
properties.load(inputStream);
return Guice.createInjector(new OccurrenceSearchTestModule(properties));
} catch (IllegalArgumentException e) {
throw e;
} catch (IOException e) {
throw new IllegalStateException(e);
} finally {
IOUtils.closeQuietly(inputStream);
}
}
/**
* Loads test data from the CSV file and store it into Solr and HBase.
*/
private static void loadOccurrences() throws IOException {
try (Table hTable = hbaseConnection.getTable(OCCURRENCE_TABLE)) {
OccurrenceDataLoader.processOccurrences(CSV_TEST_FILE, new HBasePredicateWriter(hTable),
new SolrPredicateWriter(new SolrOccurrenceWriter(solrClient)));
} catch (Exception e) {
LOG.error("Error processing occurrence objects from file", e);
} finally {
commitToSolrQuietly();
}
}
/**
* Performs a search without parameters.
*/
@Test
public void testSearchAll() {
SearchResponse<Occurrence, OccurrenceSearchParameter> response =
occurrenceSearchService.search(new OccurrenceSearchRequest());
Assert.assertTrue(response.getCount() > 0);
}
/**
* Performs a search by BasisOfRecord.
*/
@Test
public void testSearchByBasisOfRecord() {
OccurrenceSearchRequest occurrenceSearchRequest = new OccurrenceSearchRequest();
occurrenceSearchRequest.addBasisOfRecordFilter(BasisOfRecord.PRESERVED_SPECIMEN);
SearchResponse<Occurrence, OccurrenceSearchParameter> response =
occurrenceSearchService.search(occurrenceSearchRequest);
Assert.assertTrue(response.getCount() > 0);
}
/**
* Performs a search by BoundingBox.
*/
@Test
public void testSearchByBBox() {
OccurrenceSearchRequest occurrenceSearchRequest = new OccurrenceSearchRequest();
occurrenceSearchRequest
.addGeometryFilter("POLYGON ((-125.156 57.326,-125.156 -49.382,138.515 -49.382,138.515 57.326,-125.156 57.326))");
SearchResponse<Occurrence, OccurrenceSearchParameter> response =
occurrenceSearchService.search(occurrenceSearchRequest);
Assert.assertTrue(response.getCount() > 0);
}
/**
* Performs a search by CatalogNumber.
*/
@Test
public void testSearchByCatalogNumber() {
OccurrenceSearchRequest occurrenceSearchRequest = new OccurrenceSearchRequest();
occurrenceSearchRequest.addCatalogNumberFilter("1198");
SearchResponse<Occurrence, OccurrenceSearchParameter> response =
occurrenceSearchService.search(occurrenceSearchRequest);
Assert.assertTrue(response.getCount() > 0);
}
/**
* Performs a search by RecordedBy.
*/
@Test
public void testSearchByRecordedBy() {
OccurrenceSearchRequest occurrenceSearchRequest = new OccurrenceSearchRequest();
occurrenceSearchRequest.addRecordedByFilter("Kupke");
SearchResponse<Occurrence, OccurrenceSearchParameter> response =
occurrenceSearchService.search(occurrenceSearchRequest);
Assert.assertTrue(response.getCount() > 0);
}
/**
* Performs a search by RecordNumber.
*/
@Test
public void testSearchByRecordNumber() {
OccurrenceSearchRequest occurrenceSearchRequest = new OccurrenceSearchRequest();
occurrenceSearchRequest.addRecordNumberFilter("c1");
SearchResponse<Occurrence, OccurrenceSearchParameter> response =
occurrenceSearchService.search(occurrenceSearchRequest);
Assert.assertTrue(response.getCount() > 0);
}
/**
* Performs a search by country.
*/
@Test
public void testSearchByCountry() {
OccurrenceSearchRequest occurrenceSearchRequest = new OccurrenceSearchRequest();
occurrenceSearchRequest.addParameter(OccurrenceSearchParameter.COUNTRY, Country.UNITED_STATES.getIso2LetterCode());
SearchResponse<Occurrence, OccurrenceSearchParameter> response =
occurrenceSearchService.search(occurrenceSearchRequest);
Assert.assertTrue(response.getCount() > 0);
}
/**
* Performs a search by country.
*/
@Test
public void testSearchByContinent() {
OccurrenceSearchRequest occurrenceSearchRequest = new OccurrenceSearchRequest();
occurrenceSearchRequest.addParameter(OccurrenceSearchParameter.CONTINENT, Continent.NORTH_AMERICA);
SearchResponse<Occurrence, OccurrenceSearchParameter> response =
occurrenceSearchService.search(occurrenceSearchRequest);
Assert.assertEquals(response.getCount().intValue(), 19);
}
/**
* Performs a search by month.
*/
@Test
public void testSearchByDataset() {
OccurrenceSearchRequest occurrenceSearchRequest = new OccurrenceSearchRequest();
occurrenceSearchRequest.addDatasetKeyFilter(UUID.fromString("85685a84-f762-11e1-a439-00145eb45e9a"));
SearchResponse<Occurrence, OccurrenceSearchParameter> response =
occurrenceSearchService.search(occurrenceSearchRequest);
Assert.assertTrue(response.getCount() > 0);
}
/**
* Performs a search by year.
*/
@Test
@Ignore("Commit/revision 1985 removed the capacity of interpreting 'DATE is not null' queries")
public void testSearchByDateAll() throws SolrServerException {
OccurrenceSearchRequest occurrenceSearchRequest = new OccurrenceSearchRequest();
occurrenceSearchRequest.addParameter(OccurrenceSearchParameter.EVENT_DATE, "*");
SearchResponse<Occurrence, OccurrenceSearchParameter> response =
occurrenceSearchService.search(occurrenceSearchRequest);
Assert.assertTrue(response.getCount() == 19);
}
/**
* Performs a search by date range using years only.
*/
@Test
public void testSearchByDateRangeYear() throws SolrServerException {
OccurrenceSearchRequest occurrenceSearchRequest = new OccurrenceSearchRequest();
occurrenceSearchRequest.addParameter(OccurrenceSearchParameter.EVENT_DATE, "1955,1956");
SearchResponse<Occurrence, OccurrenceSearchParameter> response =
occurrenceSearchService.search(occurrenceSearchRequest);
Assert.assertTrue(response.getCount() == 19);
}
/**
* Performs a search by date range using year and month pattern.
*/
@Test
public void testSearchByDateRangeYearAndMonth() throws SolrServerException {
OccurrenceSearchRequest occurrenceSearchRequest = new OccurrenceSearchRequest();
occurrenceSearchRequest.addParameter(OccurrenceSearchParameter.EVENT_DATE, "1955-07,1955-08");
SearchResponse<Occurrence, OccurrenceSearchParameter> response =
occurrenceSearchService.search(occurrenceSearchRequest);
Assert.assertTrue(response.getCount() == 17);
}
/**
* Performs a search by year.
*/
@Test
public void testSearchByDateYear() throws SolrServerException {
OccurrenceSearchRequest occurrenceSearchRequest = new OccurrenceSearchRequest();
occurrenceSearchRequest.addParameter(OccurrenceSearchParameter.EVENT_DATE, "1955");
SearchResponse<Occurrence, OccurrenceSearchParameter> response =
occurrenceSearchService.search(occurrenceSearchRequest);
Assert.assertTrue(response.getCount() == 17);
}
/**
* Performs a search year and month.
*/
@Test
public void testSearchByDateYearAndMonth() throws SolrServerException {
OccurrenceSearchRequest occurrenceSearchRequest = new OccurrenceSearchRequest();
occurrenceSearchRequest.addParameter(OccurrenceSearchParameter.EVENT_DATE, "1955-07");
SearchResponse<Occurrence, OccurrenceSearchParameter> response =
occurrenceSearchService.search(occurrenceSearchRequest);
Assert.assertTrue(response.getCount() == 16);
}
/**
* Performs a search by single date.
*/
@Test
public void testSearchByFullDate() throws SolrServerException {
OccurrenceSearchRequest occurrenceSearchRequest = new OccurrenceSearchRequest();
occurrenceSearchRequest.addParameter(OccurrenceSearchParameter.EVENT_DATE, "1955-7-31");
SearchResponse<Occurrence, OccurrenceSearchParameter> response =
occurrenceSearchService.search(occurrenceSearchRequest);
Assert.assertTrue(response.getCount() == 1);
}
/**
* Performs a search by date range using full date format.
*/
@Test
public void testSearchByFullDateRange() throws SolrServerException {
OccurrenceSearchRequest occurrenceSearchRequest = new OccurrenceSearchRequest();
occurrenceSearchRequest.addParameter(OccurrenceSearchParameter.EVENT_DATE, "1955-07-1,1955-7-14");
SearchResponse<Occurrence, OccurrenceSearchParameter> response =
occurrenceSearchService.search(occurrenceSearchRequest);
Assert.assertTrue(response.getCount() == 14);
}
/**
* Performs a search by InstitutionCode.
*/
@Test
public void testSearchByInstitutionCode() {
OccurrenceSearchRequest occurrenceSearchRequest = new OccurrenceSearchRequest();
occurrenceSearchRequest.addParameter(OccurrenceSearchParameter.INSTITUTION_CODE, "BGBM");
SearchResponse<Occurrence, OccurrenceSearchParameter> response =
occurrenceSearchService.search(occurrenceSearchRequest);
Assert.assertTrue(response.getCount() > 0);
}
/**
* Performs a search by date range using full date format.
*/
@Test
public void testSearchByMixDateRangesFullAndYear() throws SolrServerException {
OccurrenceSearchRequest occurrenceSearchRequest = new OccurrenceSearchRequest();
occurrenceSearchRequest.addParameter(OccurrenceSearchParameter.EVENT_DATE, "1955-7-14,1956");
SearchResponse<Occurrence, OccurrenceSearchParameter> response =
occurrenceSearchService.search(occurrenceSearchRequest);
Assert.assertTrue(response.getCount() == 6);
}
/**
* Performs a search by date range using full date format.
*/
@Test
public void testSearchByMixDateRangesYearAndFull() throws SolrServerException {
OccurrenceSearchRequest occurrenceSearchRequest = new OccurrenceSearchRequest();
occurrenceSearchRequest.addParameter(OccurrenceSearchParameter.EVENT_DATE, "1955,1956-10-10");
SearchResponse<Occurrence, OccurrenceSearchParameter> response =
occurrenceSearchService.search(occurrenceSearchRequest);
Assert.assertTrue(response.getCount() == 19);
}
/**
* Performs a search by month.
*/
@Test
public void testSearchByMonth() {
OccurrenceSearchRequest occurrenceSearchRequest = new OccurrenceSearchRequest();
occurrenceSearchRequest.addParameter(OccurrenceSearchParameter.MONTH, 7);
SearchResponse<Occurrence, OccurrenceSearchParameter> response =
occurrenceSearchService.search(occurrenceSearchRequest);
Assert.assertTrue(response.getCount() > 0);
}
/**
* Performs a search by date range using year and month pattern.
*/
@Test
public void testSearchByOpenDateRange() throws SolrServerException {
OccurrenceSearchRequest occurrenceSearchRequest = new OccurrenceSearchRequest();
occurrenceSearchRequest.addParameter(OccurrenceSearchParameter.EVENT_DATE, "*,1958-8");
SearchResponse<Occurrence, OccurrenceSearchParameter> response =
occurrenceSearchService.search(occurrenceSearchRequest);
Assert.assertTrue(response.getCount() == 19);
}
/**
* Performs a search by polygon.
*/
@Test
public void testSearchByPolygon() {
OccurrenceSearchRequest occurrenceSearchRequest = new OccurrenceSearchRequest();
occurrenceSearchRequest
.addGeometryFilter("POLYGON((-109.336 -49.465,-124.804 -29.074,-118.476 57.409,126.913 57.409,138.163 -38.918,119.882 -49.4655,-108.633 -49.4655,-109.336 -49.465))");
SearchResponse<Occurrence, OccurrenceSearchParameter> response =
occurrenceSearchService.search(occurrenceSearchRequest);
Assert.assertTrue(response.getCount() > 0);
}
/**
* Performs a search by TypeStatus.
*/
@Test
public void testSearchByTypeStatus() {
OccurrenceSearchRequest occurrenceSearchRequest = new OccurrenceSearchRequest();
occurrenceSearchRequest.addTypeStatusFilter(TypeStatus.TYPE);
SearchResponse<Occurrence, OccurrenceSearchParameter> response =
occurrenceSearchService.search(occurrenceSearchRequest);
Assert.assertTrue(response.getCount() > 0);
}
/**
* Performs a search by MediaType.
*/
@Test
public void testSearchByMediaType() {
// There are 3 occurrences with media objects: 1 still image, 1 video and 1 sound
OccurrenceSearchRequest occurrenceSearchRequest = new OccurrenceSearchRequest();
occurrenceSearchRequest.addMediaTypeFilter(MediaType.StillImage);
SearchResponse<Occurrence, OccurrenceSearchParameter> response =
occurrenceSearchService.search(occurrenceSearchRequest);
Assert.assertTrue(response.getCount() == 1);
occurrenceSearchRequest.addMediaTypeFilter(MediaType.MovingImage);
response = occurrenceSearchService.search(occurrenceSearchRequest);
Assert.assertTrue(response.getCount() == 2);
occurrenceSearchRequest.addMediaTypeFilter(MediaType.Sound);
response = occurrenceSearchService.search(occurrenceSearchRequest);
Assert.assertTrue(response.getCount() == 3);
}
/**
* Perform a search by OccurrenceIssue
*/
@Test
public void testSearchByIssue() {
// There is 1 occurrence with with issue COUNTRY_INVALID
OccurrenceSearchRequest occurrenceSearchRequest = new OccurrenceSearchRequest();
occurrenceSearchRequest.addIssueFilter(OccurrenceIssue.COUNTRY_INVALID);
SearchResponse<Occurrence, OccurrenceSearchParameter> response =
occurrenceSearchService.search(occurrenceSearchRequest);
Assert.assertTrue(response.getCount() == 1);
}
@Test
public void testFacetByIssue() {
// There is 1 occurrence with with issue COUNTRY_INVALID
OccurrenceSearchRequest occurrenceSearchRequest = new OccurrenceSearchRequest();
occurrenceSearchRequest.addFacets(OccurrenceSearchParameter.ISSUE);
SearchResponse<Occurrence, OccurrenceSearchParameter> response =
occurrenceSearchService.search(occurrenceSearchRequest);
List<Facet.Count> count = response.getFacets().iterator().next().getCounts();
Assert.assertTrue(count.iterator().next().getCount() == 1l);
}
}