/* * 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 de.lmu.ifi.dbs.elki.data.NumberVector; import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery; import de.lmu.ifi.dbs.elki.database.query.distance.PrimitiveDistanceQuery; import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery; import de.lmu.ifi.dbs.elki.database.query.knn.LinearScanDistanceKNNQuery; import de.lmu.ifi.dbs.elki.database.query.knn.LinearScanEuclideanDistanceKNNQuery; import de.lmu.ifi.dbs.elki.database.query.knn.LinearScanPrimitiveDistanceKNNQuery; import de.lmu.ifi.dbs.elki.database.query.range.LinearScanDistanceRangeQuery; import de.lmu.ifi.dbs.elki.database.query.range.LinearScanEuclideanDistanceRangeQuery; import de.lmu.ifi.dbs.elki.database.query.range.LinearScanPrimitiveDistanceRangeQuery; import de.lmu.ifi.dbs.elki.database.query.range.LinearScanPrimitiveSimilarityRangeQuery; import de.lmu.ifi.dbs.elki.database.query.range.LinearScanSimilarityRangeQuery; 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.PrimitiveSimilarityQuery; 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.distance.distancefunction.DistanceFunction; import de.lmu.ifi.dbs.elki.distance.distancefunction.minkowski.EuclideanDistanceFunction; import de.lmu.ifi.dbs.elki.distance.similarityfunction.SimilarityFunction; /** * Static class with utilities related to querying a database. * * @author Erich Schubert * @since 0.4.0 * * @apiviz.landmark * * @apiviz.uses Database * @apiviz.uses Relation * @apiviz.has DistanceQuery * @apiviz.has SimilarityQuery * @apiviz.has KNNQuery * @apiviz.has RangeQuery * @apiviz.has RKNNQuery */ public final class QueryUtil { /** * Private constructor. Static methods only. */ private QueryUtil() { // Do not use. } /** * Get a distance query for a given distance function, automatically choosing * a relation. * * @param <O> Object type * @param database Database * @param distanceFunction Distance function * @param hints Optimizer hints * @return Distance query */ public static <O> DistanceQuery<O> getDistanceQuery(Database database, DistanceFunction<? super O> distanceFunction, Object... hints) { final Relation<O> objectQuery = database.getRelation(distanceFunction.getInputTypeRestriction(), hints); return database.getDistanceQuery(objectQuery, distanceFunction, hints); } /** * Get a similarity query, automatically choosing a relation. * * @param <O> Object type * @param database Database * @param similarityFunction Similarity function * @param hints Optimizer hints * @return Similarity Query */ public static <O> SimilarityQuery<O> getSimilarityQuery(Database database, SimilarityFunction<? super O> similarityFunction, Object... hints) { final Relation<O> objectQuery = database.getRelation(similarityFunction.getInputTypeRestriction(), hints); return database.getSimilarityQuery(objectQuery, similarityFunction, hints); } /** * Get a KNN query object for the given distance function. * * An index is used when possible, but it may fall back to a linear scan. * * Hints include: * <ul> * <li>Integer: maximum value for k needed</li> * <li>{@link de.lmu.ifi.dbs.elki.database.query.DatabaseQuery#HINT_BULK} bulk * query needed</li> * </ul> * * @param <O> Object type * @param database Database * @param distanceFunction Distance function * @param hints Optimizer hints * @return KNN Query object */ public static <O> KNNQuery<O> getKNNQuery(Database database, DistanceFunction<? super O> distanceFunction, Object... hints) { final Relation<O> relation = database.getRelation(distanceFunction.getInputTypeRestriction(), hints); final DistanceQuery<O> distanceQuery = relation.getDistanceQuery(distanceFunction, hints); return relation.getKNNQuery(distanceQuery, hints); } /** * Get a KNN query object for the given distance function. * * An index is used when possible, but it may fall back to a linear scan. * * Hints include: * <ul> * <li>Integer: maximum value for k needed</li> * <li>{@link de.lmu.ifi.dbs.elki.database.query.DatabaseQuery#HINT_BULK} bulk * query needed</li> * </ul> * * @param relation Relation used * @param distanceFunction Distance function * @param hints Optimizer hints * * @param <O> Object type * @return KNN Query object */ public static <O> KNNQuery<O> getKNNQuery(Relation<O> relation, DistanceFunction<? super O> distanceFunction, Object... hints) { final DistanceQuery<O> distanceQuery = relation.getDistanceQuery(distanceFunction, hints); return relation.getKNNQuery(distanceQuery, hints); } /** * Get a range query object for the given distance function for radius-based * neighbor search. (Range queries in ELKI refers to radius-based ranges, not * rectangular query windows.) * * An index is used when possible, but it may fall back to a linear scan. * * Hints include: * <ul> * <li>Range: maximum range requested</li> * <li>{@link de.lmu.ifi.dbs.elki.database.query.DatabaseQuery#HINT_BULK} bulk * query needed</li> * </ul> * * @param <O> Object type * @param database Database * @param distanceFunction Distance function * @param hints Optimizer hints * @return KNN Query object */ public static <O> RangeQuery<O> getRangeQuery(Database database, DistanceFunction<? super O> distanceFunction, Object... hints) { final Relation<O> relation = database.getRelation(distanceFunction.getInputTypeRestriction(), hints); final DistanceQuery<O> distanceQuery = relation.getDistanceQuery(distanceFunction, hints); return relation.getRangeQuery(distanceQuery, hints); } /** * Get a range query object for the given distance function for radius-based * neighbor search. (Range queries in ELKI refers to radius-based ranges, not * rectangular query windows.) * * An index is used when possible, but it may fall back to a linear scan. * * Hints include: * <ul> * <li>Range: maximum range requested</li> * <li>{@link de.lmu.ifi.dbs.elki.database.query.DatabaseQuery#HINT_BULK} bulk * query needed</li> * </ul> * * @param relation Relation used * @param distanceFunction Distance function * @param hints Optimizer hints * * @param <O> Object type * @return KNN Query object */ public static <O> RangeQuery<O> getRangeQuery(Relation<O> relation, DistanceFunction<? super O> distanceFunction, Object... hints) { final DistanceQuery<O> distanceQuery = relation.getDistanceQuery(distanceFunction, hints); return relation.getRangeQuery(distanceQuery, hints); } /** * Get a rKNN query object for the given distance function. * * When possible, this will use an index, but it may default to an expensive * linear scan. * * Hints include: * <ul> * <li>Integer: maximum value for k needed</li> * <li>{@link de.lmu.ifi.dbs.elki.database.query.DatabaseQuery#HINT_BULK} bulk * query needed</li> * </ul> * * @param relation Relation used * @param distanceFunction Distance function * @param hints Optimizer hints * * @param <O> Object type * @return RKNN Query object */ public static <O> RKNNQuery<O> getRKNNQuery(Relation<O> relation, DistanceFunction<? super O> distanceFunction, Object... hints) { final DistanceQuery<O> distanceQuery = relation.getDistanceQuery(distanceFunction, hints); return relation.getRKNNQuery(distanceQuery, hints); } /** * Get a linear scan query for the given distance query. * * @param <O> Object type * @param distanceQuery distance query * @return KNN query */ @SuppressWarnings("unchecked") public static <O> KNNQuery<O> getLinearScanKNNQuery(DistanceQuery<O> distanceQuery) { // Slight optimizations of linear scans if(distanceQuery instanceof PrimitiveDistanceQuery) { final PrimitiveDistanceQuery<O> pdq = (PrimitiveDistanceQuery<O>) distanceQuery; if(EuclideanDistanceFunction.STATIC.equals(pdq.getDistanceFunction())) { final PrimitiveDistanceQuery<NumberVector> ndq = (PrimitiveDistanceQuery<NumberVector>) pdq; return (KNNQuery<O>) new LinearScanEuclideanDistanceKNNQuery<>(ndq); } return new LinearScanPrimitiveDistanceKNNQuery<>(pdq); } return new LinearScanDistanceKNNQuery<>(distanceQuery); } /** * Get a linear scan query for the given distance query. * * @param <O> Object type * @param distanceQuery distance query * @return Range query */ @SuppressWarnings("unchecked") public static <O> RangeQuery<O> getLinearScanRangeQuery(DistanceQuery<O> distanceQuery) { // Slight optimizations of linear scans if(distanceQuery instanceof PrimitiveDistanceQuery) { final PrimitiveDistanceQuery<O> pdq = (PrimitiveDistanceQuery<O>) distanceQuery; if(EuclideanDistanceFunction.STATIC.equals(pdq.getDistanceFunction())) { final PrimitiveDistanceQuery<NumberVector> ndq = (PrimitiveDistanceQuery<NumberVector>) pdq; return (RangeQuery<O>) new LinearScanEuclideanDistanceRangeQuery<>(ndq); } return new LinearScanPrimitiveDistanceRangeQuery<>(pdq); } return new LinearScanDistanceRangeQuery<>(distanceQuery); } /** * Get a linear scan query for the given similarity query. * * @param <O> Object type * @param simQuery similarity query * @return Range query */ public static <O> RangeQuery<O> getLinearScanSimilarityRangeQuery(SimilarityQuery<O> simQuery) { // Slight optimizations of linear scans if(simQuery instanceof PrimitiveSimilarityQuery) { final PrimitiveSimilarityQuery<O> pdq = (PrimitiveSimilarityQuery<O>) simQuery; return new LinearScanPrimitiveSimilarityRangeQuery<>(pdq); } return new LinearScanSimilarityRangeQuery<>(simQuery); } }