/* * This file is part of ELKI: * Environment for Developing KDD-Applications Supported by Index-Structures * * Copyright (C) 2017 * ELKI Development Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package de.lmu.ifi.dbs.elki.database; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import de.lmu.ifi.dbs.elki.data.type.NoSupportedDataTypeException; import de.lmu.ifi.dbs.elki.data.type.TypeInformation; import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener; import de.lmu.ifi.dbs.elki.database.ids.DBIDRef; import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery; import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery; import de.lmu.ifi.dbs.elki.database.query.range.RangeQuery; import de.lmu.ifi.dbs.elki.database.query.rknn.RKNNQuery; import de.lmu.ifi.dbs.elki.database.query.similarity.SimilarityQuery; import de.lmu.ifi.dbs.elki.database.relation.Relation; import de.lmu.ifi.dbs.elki.datasource.bundle.SingleObjectBundle; import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction; import de.lmu.ifi.dbs.elki.distance.similarityfunction.SimilarityFunction; import de.lmu.ifi.dbs.elki.index.IndexFactory; import de.lmu.ifi.dbs.elki.logging.Logging; import de.lmu.ifi.dbs.elki.result.AbstractHierarchicalResult; import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer; import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID; /** * Abstract base class for database API implementations. Provides default * management of relations, indexes and events as well as default query * matching. * * Note: when debugging index usage, set logging for this package to FINEST via * <tt>-enableDebug de.lmu.ifi.dbs.elki.database=FINEST</tt> * * @author Erich Schubert * @since 0.4.0 * * @apiviz.composedOf DatabaseEventManager * @apiviz.has IndexFactory */ public abstract class AbstractDatabase extends AbstractHierarchicalResult implements Database { /** * The event manager, collects events and fires them on demand. */ protected final DatabaseEventManager eventManager = new DatabaseEventManager(); /** * The relations we manage. */ protected final List<Relation<?>> relations = new ArrayList<>(); /** * Index factories. */ protected final Collection<IndexFactory<?, ?>> indexFactories = new ArrayList<>(); /** * Constructor. */ public AbstractDatabase() { super(); } @Override public SingleObjectBundle getBundle(DBIDRef id) { assert (id != null); // TODO: ensure that the ID actually exists in the database? try { // Build an object package SingleObjectBundle ret = new SingleObjectBundle(); for(Relation<?> relation : relations) { ret.append(relation.getDataTypeInformation(), relation.get(id)); } return ret; } catch(RuntimeException e) { if(id == null) { throw new UnsupportedOperationException("AbstractDatabase.getPackage(null) called!"); } // throw e upwards. throw e; } } @Override public Collection<Relation<?>> getRelations() { return Collections.unmodifiableCollection(relations); } @SuppressWarnings({ "unchecked" }) @Override public <O> Relation<O> getRelation(TypeInformation restriction, Object... hints) throws NoSupportedDataTypeException { // Get first match for(Relation<?> relation : relations) { if(restriction.isAssignableFromType(relation.getDataTypeInformation())) { return (Relation<O>) relation; } } List<TypeInformation> types = new ArrayList<>(relations.size()); for(Relation<?> relation : relations) { types.add(relation.getDataTypeInformation()); } throw new NoSupportedDataTypeException(restriction, types); } @Override public <O> DistanceQuery<O> getDistanceQuery(Relation<O> objQuery, DistanceFunction<? super O> distanceFunction, Object... hints) { return objQuery.getDistanceQuery(distanceFunction, hints); } @Override public <O> SimilarityQuery<O> getSimilarityQuery(Relation<O> objQuery, SimilarityFunction<? super O> similarityFunction, Object... hints) { return objQuery.getSimilarityQuery(similarityFunction, hints); } @Override public <O> KNNQuery<O> getKNNQuery(DistanceQuery<O> distanceQuery, Object... hints) { @SuppressWarnings("unchecked") final Relation<O> relation = (Relation<O>) distanceQuery.getRelation(); return relation.getKNNQuery(distanceQuery, hints); } @Override public <O> RangeQuery<O> getRangeQuery(DistanceQuery<O> distanceQuery, Object... hints) { @SuppressWarnings("unchecked") final Relation<O> relation = (Relation<O>) distanceQuery.getRelation(); return relation.getRangeQuery(distanceQuery, hints); } @Override public <O> RangeQuery<O> getSimilarityRangeQuery(SimilarityQuery<O> simQuery, Object... hints) { @SuppressWarnings("unchecked") final Relation<O> relation = (Relation<O>) simQuery.getRelation(); return relation.getSimilarityRangeQuery(simQuery, hints); } @Override public <O> RKNNQuery<O> getRKNNQuery(DistanceQuery<O> distanceQuery, Object... hints) { @SuppressWarnings("unchecked") final Relation<O> relation = (Relation<O>) distanceQuery.getRelation(); return relation.getRKNNQuery(distanceQuery, hints); } @Override public void addDataStoreListener(DataStoreListener l) { eventManager.addListener(l); } @Override public void removeDataStoreListener(DataStoreListener l) { eventManager.removeListener(l); } @Override public void accumulateDataStoreEvents() { eventManager.accumulateDataStoreEvents(); } @Override public void flushDataStoreEvents() { eventManager.flushDataStoreEvents(); } @Override public String getLongName() { return "Database"; } @Override public String getShortName() { return "database"; } /** * Get the class logger. * * @return Class logger */ protected abstract Logging getLogger(); /** * Parameterization class. * * @author Erich Schubert * * @apiviz.exclude */ public abstract static class Parameterizer extends AbstractParameterizer { /** * Option to specify the data source for the database. * * Key: * <p> * {@code -dbc} * </p> */ public static final OptionID DATABASE_CONNECTION_ID = new OptionID("dbc", "Database connection class."); /** * Parameter to specify the indexes to use. * <p> * Key: {@code -db.index} * </p> */ public static final OptionID INDEX_ID = new OptionID("db.index", "Database indexes to add."); @Override protected abstract Database makeInstance(); } }