diff --git a/.gitignore b/.gitignore index 39794b5..285a9bf 100644 --- a/.gitignore +++ b/.gitignore @@ -256,9 +256,13 @@ nbdist/ # Avoid ignoring PolySqlParser jar file (.jar files are usually ignored) !/libs/PolySqlParser-1.0.jar +# vectorbench CLI path /results.csv +/results-pg.csv +/recall-groundtruth.csv +/recall.csv # humble video libraries libhumblevideo-0.dll libhumblevideo.dylib -libhumblevideo.so \ No newline at end of file +libhumblevideo.so diff --git a/src/main/java/org/polypheny/simpleclient/cli/Main.java b/src/main/java/org/polypheny/simpleclient/cli/Main.java index 988ac96..1a40051 100644 --- a/src/main/java/org/polypheny/simpleclient/cli/Main.java +++ b/src/main/java/org/polypheny/simpleclient/cli/Main.java @@ -42,6 +42,7 @@ public static void main( String[] args ) throws SQLException { builder.withCommands( ComsCommand.class ); builder.withCommands( GavelCommand.class ); builder.withCommands( KnnCommand.class ); + builder.withCommands( VectorCommand.class ); builder.withCommands( MultimediaCommand.class ); builder.withCommands( GraphCommand.class ); builder.withCommands( DocBenchCommand.class ); diff --git a/src/main/java/org/polypheny/simpleclient/cli/VectorCommand.java b/src/main/java/org/polypheny/simpleclient/cli/VectorCommand.java new file mode 100644 index 0000000..d733082 --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/cli/VectorCommand.java @@ -0,0 +1,123 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.cli; + +import com.github.rvesse.airline.HelpOption; +import com.github.rvesse.airline.annotations.AirlineModule; +import com.github.rvesse.airline.annotations.Arguments; +import com.github.rvesse.airline.annotations.Command; +import com.github.rvesse.airline.annotations.Option; +import lombok.extern.slf4j.Slf4j; +import org.polypheny.simpleclient.executor.Executor.ExecutorFactory; +import org.polypheny.simpleclient.executor.PolyphenyDbJdbcExecutor.PolyphenyDbJdbcExecutorFactory; +import org.polypheny.simpleclient.main.VectorBenchScenario; +import java.sql.SQLException; +import java.util.List; + +@Slf4j +@Command(name = "vector", description = "Mode for quick testing of Polypheny-DB using the vector benchmark.") +public class VectorCommand implements CliRunnable { + + @AirlineModule + private HelpOption help; + + @Arguments(description = "Task { schema | data | groundtruth | index | workload | warmup | recall } and multiplier.") + private List args; + + + @Option(name = { "-pdb", "--polyphenydb" }, title = "IP or Hostname", arity = 1, description = "IP or Hostname of the Polypheny-DB server (default: 127.0.0.1).") + public static String polyphenyDbHost = "127.0.0.1"; + + + @Option(name = { "--writeCSV" }, arity = 0, description = "Write a CSV file containing execution times for all executed queries (default: false).") + public boolean writeCsv = false; + + + @Option(name = { "--queryList" }, arity = 0, description = "Dump all queries into a file (default: false).") + public boolean dumpQueryList = false; + + + @Override + public int run() throws SQLException { + + if ( args == null || args.size() < 1 ) { + System.err.println( "Missing task" ); + System.exit( 1 ); + } + + int multiplier = 1; + if ( args.size() > 1 ) { + multiplier = Integer.parseInt( args.get( 1 ) ); + if ( multiplier < 1 ) { + System.err.println( "Multiplier needs to be a integer > 0!" ); + System.exit( 1 ); + } + } + + ExecutorFactory executorFactory; + executorFactory = new PolyphenyDbJdbcExecutorFactory( polyphenyDbHost, false ); + boolean usePostgres = VectorBenchScenario.isPostgresMode(); + String task = args.getFirst(); + + try { + if ( task.equalsIgnoreCase( "data" ) ) { + if ( usePostgres ) VectorBenchScenario.pgData( multiplier, true ); + else VectorBenchScenario.data( executorFactory, multiplier, true ); + } else if ( task.equalsIgnoreCase( "workload" ) ) { + if ( usePostgres ) VectorBenchScenario.pgWorkload( multiplier, true, writeCsv, dumpQueryList ); + else VectorBenchScenario.workload( executorFactory, multiplier, true, writeCsv, dumpQueryList ); + } else if ( args.getFirst().equalsIgnoreCase( "schema" ) ) { + if ( usePostgres ) VectorBenchScenario.pgSchema( true ); + else VectorBenchScenario.schema( executorFactory, true ); + } else if ( task.equalsIgnoreCase( "index" ) ) { + if ( usePostgres ) VectorBenchScenario.pgIndex( true ); + else VectorBenchScenario.index( executorFactory, true ); + } else if ( task.equalsIgnoreCase( "groundtruth" ) ) { + if ( usePostgres ) VectorBenchScenario.pgGroundTruth(); + else VectorBenchScenario.groundTruth( executorFactory ); + } else if ( task.equalsIgnoreCase( "recall" ) ) { + if ( usePostgres ) VectorBenchScenario.pgRecall(); + else VectorBenchScenario.recall( executorFactory ); + } else if ( args.getFirst().equalsIgnoreCase( "warmup" ) ) { + if ( usePostgres ) VectorBenchScenario.pgWarmup( multiplier, true, dumpQueryList ); + else VectorBenchScenario.warmup( executorFactory, multiplier, true, dumpQueryList ); + } else { + System.err.println( "Unknown task: " + args.getFirst() ); + } + } catch ( Throwable t ) { + log.error( "Exception while executing VectorBench!", t ); + System.exit( 1 ); + } + + try { + Thread.sleep( 2000 ); + } catch ( InterruptedException e ) { + throw new RuntimeException( "Unexpected interrupt", e ); + } + + return 0; + } + +} diff --git a/src/main/java/org/polypheny/simpleclient/executor/JdbcExecutor.java b/src/main/java/org/polypheny/simpleclient/executor/JdbcExecutor.java index 7440a5d..cca0482 100644 --- a/src/main/java/org/polypheny/simpleclient/executor/JdbcExecutor.java +++ b/src/main/java/org/polypheny/simpleclient/executor/JdbcExecutor.java @@ -108,6 +108,9 @@ public long executeQuery( Query query ) throws ExecutorException { case ARRAY_REAL: preparedStatement.setArray( entry.getKey(), connection.createArrayOf( "REAL", (Object[]) entry.getValue().right ) ); break; + case ARRAY_BOOLEAN: + preparedStatement.setArray( entry.getKey(), connection.createArrayOf( "BOOLEAN", (Object[]) entry.getValue().right ) ); + break; case BYTE_ARRAY: preparedStatement.setBytes( entry.getKey(), (byte[]) entry.getValue().right ); break; @@ -156,6 +159,23 @@ public long executeQuery( Query query ) throws ExecutorException { } + /** + * Executes the given query and returns the values of the first column of the result set (e.g. the ids of a + * top-k nearest-neighbor query). Used for recall measurement where the actual returned rows are needed. + */ + public List executeQueryAndGetIds( Query query ) throws ExecutorException { + List ids = new ArrayList<>(); + try ( ResultSet resultSet = executeStatement.executeQuery( query.getSql() ) ) { + while ( resultSet.next() ) { + ids.add( resultSet.getLong( 1 ) ); + } + } catch ( SQLException e ) { + throw new ExecutorException( e ); + } + return ids; + } + + @Override public long executeQueryAndGetNumber( Query query ) throws ExecutorException { try { @@ -254,6 +274,9 @@ protected void executeInsertListAsPreparedBatch( List queryList case ARRAY_REAL: preparedStatement.setArray( entry.getKey(), connection.createArrayOf( "REAL", (Object[]) entry.getValue().right ) ); break; + case ARRAY_BOOLEAN: + preparedStatement.setArray( entry.getKey(), connection.createArrayOf( "BOOLEAN", (Object[]) entry.getValue().right) ); + break; case BYTE_ARRAY: preparedStatement.setBytes( entry.getKey(), (byte[]) entry.getValue().right ); break; diff --git a/src/main/java/org/polypheny/simpleclient/executor/PolyphenyDbExecutor.java b/src/main/java/org/polypheny/simpleclient/executor/PolyphenyDbExecutor.java index b4ff1ae..419619e 100644 --- a/src/main/java/org/polypheny/simpleclient/executor/PolyphenyDbExecutor.java +++ b/src/main/java/org/polypheny/simpleclient/executor/PolyphenyDbExecutor.java @@ -122,13 +122,18 @@ default String deployMonetDb( boolean deployStoresUsingDocker ) throws ExecutorE default String deployPostgres( boolean deployStoresUsingDocker ) throws ExecutorException { + return deployPostgres( deployStoresUsingDocker, "pgvector & PostGIS" ); + } + + + default String deployPostgres( boolean deployStoresUsingDocker, String imageVariant ) throws ExecutorException { String config; String name; if ( deployStoresUsingDocker ) { name = "postgres" + storeCounter.getAndIncrement(); if ( PolyphenyVersionSwitch.getInstance().useNewAdapterDeployParameters ) { int dockerInstanceId = getDockerInstanceId(); - config = "{\"mode\":\"docker\",\"instanceId\":\"" + dockerInstanceId + "\",\"maxConnections\":\"25\"}"; + config = "{\"mode\":\"docker\",\"instanceId\":\"" + dockerInstanceId + "\",\"maxConnections\":\"25\",\"imageVariant\":\"" + imageVariant + "\"}"; } else { config = "{\"port\":\"" + nextPort.getAndIncrement() + "\",\"maxConnections\":\"25\",\"password\":\"postgres\",\"mode\":\"docker\",\"instanceId\":\"0\"}"; } @@ -345,7 +350,7 @@ public PolyphenyDbInstance( PolyphenyControlConnector polyphenyControlConnector, if ( !config.deployStoresUsingDocker ) { PostgresInstance.reset(); } - executor.deployPostgres( config.deployStoresUsingDocker ); + executor.deployPostgres( config.deployStoresUsingDocker, config.postgresImageVariant ); break; case "monetdb": if ( !config.deployStoresUsingDocker ) { diff --git a/src/main/java/org/polypheny/simpleclient/main/ChronosAgent.java b/src/main/java/org/polypheny/simpleclient/main/ChronosAgent.java index e9ace38..8c9a991 100644 --- a/src/main/java/org/polypheny/simpleclient/main/ChronosAgent.java +++ b/src/main/java/org/polypheny/simpleclient/main/ChronosAgent.java @@ -83,6 +83,9 @@ import org.polypheny.simpleclient.scenario.graph.GraphBenchConfig; import org.polypheny.simpleclient.scenario.knnbench.KnnBench; import org.polypheny.simpleclient.scenario.knnbench.KnnBenchConfig; +import org.polypheny.simpleclient.scenario.vectorbench.PgVectorBench; +import org.polypheny.simpleclient.scenario.vectorbench.VectorBench; +import org.polypheny.simpleclient.scenario.vectorbench.VectorBenchConfig; import org.polypheny.simpleclient.scenario.multibench.MultiBench; import org.polypheny.simpleclient.scenario.multibench.MultiBenchConfig; import org.polypheny.simpleclient.scenario.multimedia.MultimediaBench; @@ -218,7 +221,7 @@ protected Object prepare( ChronosJob chronosJob, final File inputDirectory, fina executorFactory = new SurrealDBExecutorFactory( ChronosCommand.hostname, "8989", true ); break; case "postgres": - dockerContainerName = DockerLauncher.launch( "postgres", "polypheny/postgres:latest", Map.of( "POSTGRES_PASSWORD", "postgres" ), List.of( 5432 ), () -> PostgresInstance.tryConnect( ChronosCommand.hostname ) ); + dockerContainerName = DockerLauncher.launch( "postgres", "polypheny/postgres-pgvector-postgis:17-debian", Map.of( "POSTGRES_PASSWORD", "postgres" ), List.of( 5432 ), () -> PostgresInstance.tryConnect( ChronosCommand.hostname ) ); executorFactory = new PostgresExecutorFactory( ChronosCommand.hostname, Boolean.parseBoolean( parsedConfig.get( "prepareStatements" ) ) ); break; case "monetdb": @@ -253,6 +256,14 @@ protected Object prepare( ChronosJob chronosJob, final File inputDirectory, fina config = new KnnBenchConfig( parsedConfig ); scenario = new KnnBench( executorFactory, (KnnBenchConfig) config, true, dumpQueryList ); break; + case "vectorBench": + config = new VectorBenchConfig( parsedConfig ); + if ( config.system.equals( "postgres" ) ) { + scenario = new PgVectorBench( executorFactory, (VectorBenchConfig) config, true, dumpQueryList ); + } else { + scenario = new VectorBench( executorFactory, (VectorBenchConfig) config, true, dumpQueryList ); + } + break; case "multimedia": config = new MultimediaConfig( parsedConfig ); scenario = new MultimediaBench( executorFactory, (MultimediaConfig) config, true, dumpQueryList ); diff --git a/src/main/java/org/polypheny/simpleclient/main/CsvWriter.java b/src/main/java/org/polypheny/simpleclient/main/CsvWriter.java index d702dd2..b1e53fb 100644 --- a/src/main/java/org/polypheny/simpleclient/main/CsvWriter.java +++ b/src/main/java/org/polypheny/simpleclient/main/CsvWriter.java @@ -55,7 +55,7 @@ public void appendToCsv( String query, long measuredTime ) { } - private void writeLine( String[] entries ) { + private synchronized void writeLine( String[] entries ) { try { String line = String.join( ",", entries ); writer.write( line ); diff --git a/src/main/java/org/polypheny/simpleclient/main/VectorBenchScenario.java b/src/main/java/org/polypheny/simpleclient/main/VectorBenchScenario.java new file mode 100644 index 0000000..5ad0f81 --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/main/VectorBenchScenario.java @@ -0,0 +1,223 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.main; + +import lombok.extern.slf4j.Slf4j; +import org.polypheny.simpleclient.executor.Executor.ExecutorFactory; +import org.polypheny.simpleclient.executor.PostgresExecutor.PostgresExecutorFactory; +import org.polypheny.simpleclient.executor.ExecutorException; +import org.polypheny.simpleclient.executor.JdbcExecutor; +import org.polypheny.simpleclient.query.QueryBuilder; +import org.polypheny.simpleclient.scenario.vectorbench.PgVectorBench; +import org.polypheny.simpleclient.scenario.vectorbench.RecallEvaluator; +import org.polypheny.simpleclient.scenario.vectorbench.VectorBench; +import org.polypheny.simpleclient.scenario.vectorbench.VectorBenchConfig; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dql.SimpleKnnRealFeature; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.postgres.dql.PgSimpleKnnRealFeature; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Objects; +import java.util.Properties; + +@Slf4j +public class VectorBenchScenario { + + public static void schema( ExecutorFactory executorFactory, boolean commitAfterEveryQuery ) { + VectorBenchConfig config = new VectorBenchConfig( getProperties(), 1 ); + VectorBench vectorBench = new VectorBench( executorFactory, config, commitAfterEveryQuery, false ); + vectorBench.createSchema( null, true ); + } + + + public static void pgSchema( boolean commitAfterEveryQuery ) { + VectorBenchConfig config = new VectorBenchConfig( getProperties(), 1 ); + ExecutorFactory factory = new PostgresExecutorFactory( config.postgresHost, false ); + PgVectorBench vectorBench = new PgVectorBench( factory, config, commitAfterEveryQuery, false ); + vectorBench.createSchema( null, true ); + } + + + public static void index( ExecutorFactory executorFactory, boolean commitAfterEveryQuery ) { + VectorBenchConfig config = new VectorBenchConfig( getProperties(), 1 ); + VectorBench vectorBench = new VectorBench( executorFactory, config, commitAfterEveryQuery, false ); + vectorBench.createIndex(); + } + + + public static void pgIndex( boolean commitAfterEveryQuery ) { + VectorBenchConfig config = new VectorBenchConfig( getProperties(), 1 ); + ExecutorFactory factory = new PostgresExecutorFactory( config.postgresHost, false ); + PgVectorBench vectorBench = new PgVectorBench( factory, config, commitAfterEveryQuery, false ); + vectorBench.createIndex(); + } + + + public static void groundTruth( ExecutorFactory executorFactory ) { + VectorBenchConfig config = new VectorBenchConfig( getProperties(), 1 ); + runRecall( executorFactory, config, polyphenyKnnBuilder( config ), true ); + } + + + public static void recall( ExecutorFactory executorFactory ) { + VectorBenchConfig config = new VectorBenchConfig( getProperties(), 1 ); + runRecall( executorFactory, config, polyphenyKnnBuilder( config ), false ); + } + + + public static void pgGroundTruth() { + VectorBenchConfig config = new VectorBenchConfig( getProperties(), 1 ); + ExecutorFactory factory = new PostgresExecutorFactory( config.postgresHost, false ); + runRecall( factory, config, pgKnnBuilder( config ), true ); + } + + + public static void pgRecall() { + VectorBenchConfig config = new VectorBenchConfig( getProperties(), 1 ); + ExecutorFactory factory = new PostgresExecutorFactory( config.postgresHost, false ); + runRecall( factory, config, pgKnnBuilder( config ), false ); + } + + + private static QueryBuilder polyphenyKnnBuilder( VectorBenchConfig config ) { + return new SimpleKnnRealFeature( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.distanceNorm ); + } + + + private static QueryBuilder pgKnnBuilder( VectorBenchConfig config ) { + return new PgSimpleKnnRealFeature( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.distanceNorm ); + } + + + private static void runRecall( ExecutorFactory executorFactory, VectorBenchConfig config, QueryBuilder knnBuilder, boolean capture ) { + JdbcExecutor executor = (JdbcExecutor) executorFactory.createExecutorInstance(); + RecallEvaluator evaluator = new RecallEvaluator( config, executor, knnBuilder, RecallEvaluator.DEFAULT_GROUND_TRUTH_FILE ); + try { + if ( capture ) { + evaluator.captureGroundTruth(); + } else { + double recall = evaluator.evaluate(); + writeRecall( config, recall ); + } + } finally { + try { + executor.closeConnection(); + } catch ( ExecutorException e ) { + log.error( "Error while closing connection", e ); + } + } + } + + + /** Writes the recall@k of the last `recall` run to recall.csv in the working directory. */ + private static void writeRecall( VectorBenchConfig config, double recall ) { + try ( FileWriter fw = new FileWriter( "recall.csv" ) ) { + fw.write( "k,recall\n" ); + fw.write( config.limitKnnQueries + "," + recall + "\n" ); + } catch ( IOException e ) { + log.error( "Could not write recall.csv", e ); + } + } + + + public static void data( ExecutorFactory executorFactory, int multiplier, boolean commitAfterEveryQuery ) { + VectorBenchConfig config = new VectorBenchConfig( getProperties(), multiplier ); + VectorBench vectorBench = new VectorBench( executorFactory, config, commitAfterEveryQuery, false ); + + ProgressReporter progressReporter = new ProgressBar( config.numberOfThreads, config.progressReportBase ); + vectorBench.generateData( null, progressReporter ); + } + + + public static void pgData( int multiplier, boolean commitAfterEveryQuery ) { + VectorBenchConfig config = new VectorBenchConfig( getProperties(), multiplier ); + ExecutorFactory factory = new PostgresExecutorFactory( config.postgresHost, false ); + PgVectorBench bench = new PgVectorBench( factory, config, commitAfterEveryQuery, false ); + ProgressReporter progressReporter = new ProgressBar( config.numberOfThreads, config.progressReportBase ); + bench.generateData( null, progressReporter ); + } + + + public static void workload( ExecutorFactory executorFactory, int multiplier, boolean commitAfterEveryQuery, boolean writeCsv, boolean dumpQueryList ) { + VectorBenchConfig config = new VectorBenchConfig( getProperties(), multiplier ); + VectorBench vectorBench = new VectorBench( executorFactory, config, commitAfterEveryQuery, dumpQueryList ); + + final CsvWriter csvWriter; + if ( writeCsv ) { + csvWriter = new CsvWriter( "results.csv" ); + } else { + csvWriter = null; + } + + ProgressReporter progressReporter = new ProgressBar( config.numberOfThreads, config.progressReportBase ); + vectorBench.execute( progressReporter, csvWriter, new File( "." ), config.numberOfThreads ); + } + + + public static void pgWorkload( int multiplier, boolean commitAfterEveryQuery, boolean writeCsv, boolean dumpQueryList ) { + VectorBenchConfig config = new VectorBenchConfig( getProperties(), multiplier ); + ExecutorFactory factory = new PostgresExecutorFactory( config.postgresHost, false ); + PgVectorBench bench = new PgVectorBench( factory, config, commitAfterEveryQuery, dumpQueryList ); + CsvWriter csvWriter = writeCsv ? new CsvWriter( "results-pg.csv" ) : null; + ProgressReporter progressReporter = new ProgressBar( config.numberOfThreads, config.progressReportBase ); + bench.execute( progressReporter, csvWriter, new File( "." ), config.numberOfThreads ); + } + + + + public static void warmup( ExecutorFactory executorFactory, int multiplier, boolean commitAfterEveryQuery, boolean dumpQueryList ) { + VectorBenchConfig config = new VectorBenchConfig( getProperties(), multiplier ); + VectorBench vectorBench = new VectorBench( executorFactory, config, commitAfterEveryQuery, dumpQueryList ); + + ProgressReporter progressReporter = new ProgressBar( config.numberOfThreads, config.progressReportBase ); + vectorBench.warmUp( progressReporter ); + } + + + public static void pgWarmup( int multiplier, boolean commitAfterEveryQuery, boolean dumpQueryList ) { + VectorBenchConfig config = new VectorBenchConfig( getProperties(), multiplier ); + ExecutorFactory factory = new PostgresExecutorFactory( config.postgresHost, false ); + PgVectorBench bench = new PgVectorBench( factory, config, commitAfterEveryQuery, dumpQueryList ); + ProgressReporter progressReporter = new ProgressBar( config.numberOfThreads, config.progressReportBase ); + bench.warmUp( progressReporter ); + } + + + public static boolean isPostgresMode() { + return new VectorBenchConfig( getProperties(), 1 ).mode.equalsIgnoreCase( "postgres" ); + } + + + private static Properties getProperties() { + Properties props = new Properties(); + try { + props.load( Objects.requireNonNull( ClassLoader.getSystemResourceAsStream( "org/polypheny/simpleclient/scenario/vectorbench/vector.properties" ) ) ); + } catch ( IOException e ) { + log.error( "Exception while reading properties file", e ); + } + return props; + } + +} diff --git a/src/main/java/org/polypheny/simpleclient/query/Query.java b/src/main/java/org/polypheny/simpleclient/query/Query.java index 99f578f..bcd9b83 100644 --- a/src/main/java/org/polypheny/simpleclient/query/Query.java +++ b/src/main/java/org/polypheny/simpleclient/query/Query.java @@ -52,7 +52,7 @@ public Query( boolean expectResultSet ) { } - public enum DataTypes {INTEGER, VARCHAR, TIMESTAMP, DATE, ARRAY_INT, ARRAY_REAL, BYTE_ARRAY, FILE} + public enum DataTypes {INTEGER, VARCHAR, TIMESTAMP, DATE, ARRAY_INT, ARRAY_REAL, ARRAY_BOOLEAN, BYTE_ARRAY, FILE} public abstract String getSql(); diff --git a/src/main/java/org/polypheny/simpleclient/scenario/AbstractConfig.java b/src/main/java/org/polypheny/simpleclient/scenario/AbstractConfig.java index fc3575e..cc44cff 100644 --- a/src/main/java/org/polypheny/simpleclient/scenario/AbstractConfig.java +++ b/src/main/java/org/polypheny/simpleclient/scenario/AbstractConfig.java @@ -70,6 +70,7 @@ public abstract class AbstractConfig { public final boolean workloadMonitoringWarmup; public final int progressReportBase = 100; + public final String postgresImageVariant; protected AbstractConfig( String scenario, String system, Properties properties ) { @@ -103,6 +104,7 @@ protected AbstractConfig( String scenario, String system, Properties properties workloadMonitoringExecutingWorkload = false; workloadMonitoringLoadingData = true; workloadMonitoringWarmup = true; + postgresImageVariant = getStringPropertyOrDefault( properties, "postgresImageVariant", "pgvector & PostGIS" ); // This is hacky but ensures that VersionSwitch is initialized when running tasks from CLI. PolyphenyVersionSwitch.initialize( this ); @@ -150,6 +152,7 @@ protected AbstractConfig( String scenario, String system, Map cd workloadMonitoringExecutingWorkload = Boolean.parseBoolean( cdlGetOrDefault( cdl, "workloadMonitoring", "false" ) ); workloadMonitoringLoadingData = Boolean.parseBoolean( cdlGetOrDefault( cdl, "workloadMonitoringLoadingData", "false" ) ); workloadMonitoringWarmup = Boolean.parseBoolean( cdlGetOrDefault( cdl, "workloadMonitoringWarmup", "true" ) ); + postgresImageVariant = cdlGetOrDefault( cdl, "postgresImageVariant", "pgvector & PostGIS" ); } @@ -171,6 +174,12 @@ protected String getStringProperty( Properties properties, String name ) { } + protected String getStringPropertyOrDefault( Properties properties, String name, String defaultName ) { + String str = getProperty( properties, name ); + return str == null ? defaultName : str; + } + + protected int getIntProperty( Properties properties, String name ) { String str = getProperty( properties, name ); if ( str == null ) { diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/DataGenerator.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/DataGenerator.java new file mode 100644 index 0000000..4271dc2 --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/DataGenerator.java @@ -0,0 +1,135 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package org.polypheny.simpleclient.scenario.vectorbench; + +import java.util.LinkedList; +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.polypheny.simpleclient.executor.Executor; +import org.polypheny.simpleclient.executor.ExecutorException; +import org.polypheny.simpleclient.main.ProgressReporter; +import org.polypheny.simpleclient.query.BatchableInsert; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dml.InsertBooleanFeature; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dml.InsertIntFeature; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dml.InsertMetadata; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dml.InsertRealFeature; + + +@Slf4j +public class DataGenerator { + + private final Executor theExecutor; + private final VectorBenchConfig config; + private final ProgressReporter progressReporter; + + private final List batchList; + + private boolean aborted; + + + DataGenerator(Executor executor, VectorBenchConfig config, ProgressReporter progressReporter ) { + theExecutor = executor; + this.config = config; + this.progressReporter = progressReporter; + batchList = new LinkedList<>(); + + aborted = false; + } + + + void generateMetadata() throws ExecutorException { + InsertMetadata queryBuilder = new InsertMetadata(); + for ( int i = 0; i < config.numberOfEntries; i++ ) { + if ( aborted ) { + break; + } + + addToInsertList( queryBuilder.getNewQuery() ); + } + executeInsertList(); + } + + + void generateIntFeatures() throws ExecutorException { + InsertIntFeature queryBuilder = new InsertIntFeature( config.randomSeedInsert, config.dimensionFeatureVectors ); + for ( int i = 0; i < config.numberOfEntries; i++ ) { + if ( aborted ) { + break; + } + + addToInsertList( queryBuilder.getNewQuery() ); + } + executeInsertList(); + } + + + void generateRealFeatures() throws ExecutorException { + InsertRealFeature queryBuilder = new InsertRealFeature( config.randomSeedInsert, config.dimensionFeatureVectors ); + for ( int i = 0; i < config.numberOfEntries; i++ ) { + if ( aborted ) { + break; + } + + addToInsertList( queryBuilder.getNewQuery() ); + } + executeInsertList(); + } + + + void generateBooleanFeatures() throws ExecutorException { + InsertBooleanFeature queryBuilder = new InsertBooleanFeature( config.randomSeedInsert, config.dimensionFeatureVectors ); + for ( int i = 0; i < config.numberOfEntries; i++ ) { + if ( aborted ) { + break; + } + + addToInsertList( queryBuilder.getNewQuery() ); + } + + executeInsertList(); + } + + + private void addToInsertList( BatchableInsert query ) throws ExecutorException { + batchList.add( query ); + if ( batchList.size() >= config.batchSizeInserts ) { + executeInsertList(); + } + } + + + private void executeInsertList() throws ExecutorException { + theExecutor.executeInsertList( batchList, config ); + theExecutor.executeCommit(); + batchList.clear(); + } + + + public void abort() { + aborted = true; + } + +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/PgDataGenerator.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/PgDataGenerator.java new file mode 100644 index 0000000..816d742 --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/PgDataGenerator.java @@ -0,0 +1,111 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench; + +import lombok.extern.slf4j.Slf4j; +import org.polypheny.simpleclient.executor.Executor; +import org.polypheny.simpleclient.executor.ExecutorException; +import org.polypheny.simpleclient.main.ProgressReporter; +import org.polypheny.simpleclient.query.BatchableInsert; +import org.polypheny.simpleclient.query.RawQuery; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.postgres.dml.PgInsertBooleanFeature; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.postgres.dml.PgInsertRealFeature; +import java.util.LinkedList; +import java.util.List; + + +@Slf4j +public class PgDataGenerator { + + private final Executor executor; + private final VectorBenchConfig config; + private final ProgressReporter progressReporter; + private final List batch; + private boolean aborted; + + + PgDataGenerator( Executor executor, VectorBenchConfig config, ProgressReporter progressReporter ) { + this.executor = executor; + this.config = config; + this.progressReporter = progressReporter; + this.batch = new LinkedList<>(); + this.aborted = false; + } + + + void generateRealFeatures() throws ExecutorException { + PgInsertRealFeature builder = new PgInsertRealFeature( config.randomSeedInsert, config.dimensionFeatureVectors ); + for ( int i = 0; i < config.numberOfEntries; i++ ) { + if ( aborted ) break; + addToBatch( builder.getNewQuery() ); + progressReporter.update( 1 ); + } + flushBatch(); + } + + + void generateBooleanFeatures() throws ExecutorException { + PgInsertBooleanFeature builder = new PgInsertBooleanFeature( config.randomSeedInsert, config.dimensionFeatureVectors ); + for ( int i = 0; i < config.numberOfEntries; i++ ) { + if ( aborted ) break; + addToBatch( builder.getNewQuery() ); + progressReporter.update( 1 ); + } + flushBatch(); + } + + + private void addToBatch( BatchableInsert query ) throws ExecutorException { + batch.add( query ); + if ( batch.size() >= config.batchSizeInserts ) { + flushBatch(); + } + } + + + private void flushBatch() throws ExecutorException { + if ( batch.isEmpty() ) return; + StringBuilder sb = new StringBuilder(); + boolean first = true; + for ( BatchableInsert insert : batch ) { + if ( first ) { + sb.append( insert.getSql() ); + first = false; + } else { + sb.append( "," ).append( insert.getSqlRowExpression() ); + } + } + executor.executeQuery( RawQuery.builder().sql( sb.toString() ).expectResultSet( false ).build() ); + executor.executeCommit(); + batch.clear(); + } + + + + public void abort() { + aborted = true; + } +} + diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/PgVectorBench.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/PgVectorBench.java new file mode 100644 index 0000000..c94e378 --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/PgVectorBench.java @@ -0,0 +1,231 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench; + +import lombok.extern.slf4j.Slf4j; +import org.polypheny.simpleclient.QueryMode; +import org.polypheny.simpleclient.executor.Executor; +import org.polypheny.simpleclient.executor.Executor.DatabaseInstance; +import org.polypheny.simpleclient.executor.ExecutorException; +import org.polypheny.simpleclient.main.CsvWriter; +import org.polypheny.simpleclient.main.ProgressReporter; +import org.polypheny.simpleclient.query.Query; +import org.polypheny.simpleclient.query.QueryBuilder; +import org.polypheny.simpleclient.query.QueryListEntry; +import org.polypheny.simpleclient.query.RawQuery; +import org.polypheny.simpleclient.scenario.PolyphenyScenario; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.postgres.ddl.PgCreateBooleanFeature; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.postgres.ddl.PgCreateBooleanFeatureIndex; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.postgres.ddl.PgCreateRealFeature; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.postgres.ddl.PgCreateRealFeatureIndex; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.postgres.dql.PgSimpleKnnBooleanFeature; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.postgres.dql.PgSimpleKnnBooleanFeatureFiltered; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.postgres.dql.PgSimpleKnnIdRealFeature; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.postgres.dql.PgSimpleKnnRealCrossJoin; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.postgres.dql.PgSimpleKnnRealFeature; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.postgres.dql.PgSimpleKnnRealFeatureFiltered; +import java.io.File; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; +import java.util.Vector; + + +@Slf4j +public class PgVectorBench extends PolyphenyScenario { + + private final VectorBenchConfig config; + + + public PgVectorBench( Executor.ExecutorFactory executorFactory, VectorBenchConfig config, + boolean commitAfterEveryQuery, boolean dumpQueryList ) { + super( executorFactory, commitAfterEveryQuery, dumpQueryList, QueryMode.TABLE ); + this.config = config; + } + + + @Override + public void createSchema( DatabaseInstance databaseInstance, boolean includingKeys ) { + log.info( "Creating schema..." ); + Executor executor = null; + try { + executor = executorFactory.createExecutorInstance(); + executor.executeQuery( new RawQuery( "CREATE EXTENSION IF NOT EXISTS vector", null, false ) ); + executor.executeQuery( new PgCreateRealFeature( config.dimensionFeatureVectors ).getNewQuery() ); + executor.executeQuery( new PgCreateBooleanFeature( config.dimensionFeatureVectors ).getNewQuery() ); + } catch ( ExecutorException e ) { + throw new RuntimeException( "Exception while creating schema", e ); + } finally { + commitAndCloseExecutor( executor ); + } + } + + + public void createIndex() { + if ( !config.useIndex ) { + return; + } + Executor executor = null; + try { + executor = executorFactory.createExecutorInstance(); + long start = System.nanoTime(); + if ( indexSupportsMetric( config.indexMethod, config.distanceNorm ) ) { + executor.executeQuery( new PgCreateRealFeatureIndex( config.indexMethod, config.distanceNorm, config.indexM, config.indexEfConstruction, config.indexLists ).getNewQuery() ); + } else { + log.info( "Skipping real index: {} does not support metric '{}'.", config.indexMethod, config.distanceNorm ); + } + if ( indexSupportsMetric( config.indexMethod, config.booleanDistanceNorm ) ) { + executor.executeQuery( new PgCreateBooleanFeatureIndex( config.indexMethod, config.booleanDistanceNorm, config.indexM, config.indexEfConstruction, config.indexLists ).getNewQuery() ); + } else { + log.info( "Skipping boolean index: {} does not support metric '{}'.", config.indexMethod, config.booleanDistanceNorm ); + } + executor.executeCommit(); + long durationMillis = ( System.nanoTime() - start ) / 1_000_000L; + log.info( "Vector indexes built in {} ms", durationMillis ); + + String conf = config.indexMethod.equals( "hnsw" ) + ? "hnsw.ef_search = " + config.queryEfSearch + : "ivfflat.probes = " + config.queryProbes; + executor.executeQuery( new RawQuery( "ALTER DATABASE postgres SET " + conf, null, false ) ); + executor.executeCommit(); + log.info( "Query-time index parameter set: {}", conf ); + } catch ( ExecutorException e ) { + throw new RuntimeException( "Exception while creating vector index", e ); + } finally { + commitAndCloseExecutor( executor ); + } + } + + + @Override + public void generateData( DatabaseInstance databaseInstance, ProgressReporter progressReporter ) { + log.info( "Generating data..." ); + Executor executor = executorFactory.createExecutorInstance(); + PgDataGenerator dataGenerator = new PgDataGenerator( executor, config, progressReporter ); + try { + dataGenerator.generateRealFeatures(); + dataGenerator.generateBooleanFeatures(); + } catch ( ExecutorException e ) { + throw new RuntimeException( "Exception while generating data", e ); + } finally { + commitAndCloseExecutor( executor ); + } + + // Build the index for the benchmark run (Chronos has no separate index task). + if ( databaseInstance != null && config.useIndex ) { + createIndex(); + } + } + + + @Override + public long execute( ProgressReporter progressReporter, CsvWriter csvWriter, File outputDirectory, int numberOfThreads ) { + log.info( "Preparing query list..." ); + List queryList = new Vector<>(); + addNumberOfTimes( queryList, new PgSimpleKnnRealFeature( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.distanceNorm ), config.numberOfSimpleKnnRealFeatureQueries ); + addNumberOfTimes( queryList, new PgSimpleKnnRealFeatureFiltered( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.distanceNorm, "cat_A" ), config.numberOfSimpleKnnRealFeatureFilteredQueries ); + addNumberOfTimes( queryList, new PgSimpleKnnIdRealFeature( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.distanceNorm ), config.numberOfSimpleKnnIdRealFeatureQueries ); + addNumberOfTimes( queryList, new PgSimpleKnnRealCrossJoin( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.distanceNorm ), config.numberOfSimpleKnnRealCrossJoinQueries ); + addNumberOfTimes( queryList, new PgSimpleKnnBooleanFeature( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.booleanDistanceNorm ), config.numberOfSimpleKnnBooleanFeatureQueries ); + addNumberOfTimes( queryList, new PgSimpleKnnBooleanFeatureFiltered( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.booleanDistanceNorm, "cat_A" ), config.numberOfSimpleKnnBooleanFeatureFilteredQueries ); + + return commonExecute( queryList, progressReporter, outputDirectory, numberOfThreads, + Query::getSql, () -> executorFactory.createExecutorInstance( csvWriter ), new Random() ); + } + + + @Override + public void warmUp( ProgressReporter progressReporter ) { + log.info( "Warm-up..." ); + Executor executor = null; + PgSimpleKnnRealFeature pgSimpleKnnRealFeature = new PgSimpleKnnRealFeature( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.distanceNorm ); + PgSimpleKnnRealFeatureFiltered pgSimpleKnnRealFeatureFiltered = new PgSimpleKnnRealFeatureFiltered( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.distanceNorm, "cat_A" ); + PgSimpleKnnRealCrossJoin pgSimpleKnnRealCrossJoin = new PgSimpleKnnRealCrossJoin( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.distanceNorm ); + PgSimpleKnnIdRealFeature pgSimpleKnnIdRealFeature = new PgSimpleKnnIdRealFeature( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.distanceNorm ); + PgSimpleKnnBooleanFeature pgSimpleKnnBooleanFeature = new PgSimpleKnnBooleanFeature( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.booleanDistanceNorm ); + PgSimpleKnnBooleanFeatureFiltered pgSimpleKnnBooleanFeatureFiltered = new PgSimpleKnnBooleanFeatureFiltered( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.booleanDistanceNorm, "cat_A" ); + + for ( int i = 0; i < config.numberOfWarmUpIterations; i++ ) { + try { + executor = executorFactory.createExecutorInstance(); + if ( config.numberOfSimpleKnnRealFeatureQueries > 0 ) { + executor.executeQuery( pgSimpleKnnRealFeature.getNewQuery() ); + } + if ( config.numberOfSimpleKnnRealFeatureFilteredQueries > 0 ) { + executor.executeQuery( pgSimpleKnnRealFeatureFiltered.getNewQuery() ); + } + if ( config.numberOfSimpleKnnRealCrossJoinQueries > 0 ) { + executor.executeQuery( pgSimpleKnnRealCrossJoin.getNewQuery() ); + } + if ( config.numberOfSimpleKnnIdRealFeatureQueries > 0 ) { + executor.executeQuery( pgSimpleKnnIdRealFeature.getNewQuery() ); + } + if ( config.numberOfSimpleKnnBooleanFeatureQueries > 0 ) { + executor.executeQuery( pgSimpleKnnBooleanFeature.getNewQuery() ); + } + if ( config.numberOfSimpleKnnBooleanFeatureFilteredQueries > 0 ) { + executor.executeQuery( pgSimpleKnnBooleanFeatureFiltered.getNewQuery() ); + } + + } catch ( ExecutorException e ) { + throw new RuntimeException( "Error during warm-up", e ); + } finally { + commitAndCloseExecutor( executor ); + } + try { + Thread.sleep( 10000 ); + } catch ( InterruptedException e ) { + throw new RuntimeException( "Interrupted during warm-up", e ); + } + } + } + + + @Override + public int getNumberOfInsertThreads() { + return 1; + } + + + private static boolean indexSupportsMetric( String method, String metric ) { + // HNSW supports all metrics; IVFFlat does not support L1 or JACCARD. + if ( method.equalsIgnoreCase( "ivfflat" ) ) { + return !( metric.equalsIgnoreCase( "L1" ) || metric.equalsIgnoreCase( "JACCARD" ) ); + } + return true; + } + + + private void addNumberOfTimes( List list, QueryBuilder builder, int count ) { + int id = queryTypes.size() + 1; + queryTypes.put( id, builder.getNewQuery().getSql() ); + measuredTimePerQueryType.put( id, Collections.synchronizedList( new LinkedList<>() ) ); + for ( int i = 0; i < count; i++ ) { + list.add( new QueryListEntry( builder.getNewQuery(), id ) ); + } + } +} + diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/RecallEvaluator.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/RecallEvaluator.java new file mode 100644 index 0000000..e17f817 --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/RecallEvaluator.java @@ -0,0 +1,181 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; +import org.polypheny.simpleclient.executor.ExecutorException; +import org.polypheny.simpleclient.executor.JdbcExecutor; +import org.polypheny.simpleclient.query.QueryBuilder; + + +/** + * Measures recall@k for an approximate vector index. + * + *
    + *
  1. {@link #captureGroundTruth()} is run while no vector index exists, so the regular query path + * performs an exact nearest-neighbor search. The true top-k ids of every query are + * written to the ground-truth file.
  2. + *
  3. The index is created.
  4. + *
  5. {@link #evaluate()} reruns the exact same queries, now answered approximately via the + * index, and computes recall = |returned \intersect ground_truth| / k averaged over all queries.
  6. + *
+ * + *

Determinism relies on the supplied {@link QueryBuilder} being freshly seeded with the same query seed + * in both phases, so {@code getNewQuery()} yields the identical sequence of query vectors. + */ +@Slf4j +public class RecallEvaluator { + + public static final String DEFAULT_GROUND_TRUTH_FILE = "recall-groundtruth.csv"; + + private final VectorBenchConfig config; + private final JdbcExecutor executor; + private final QueryBuilder knnBuilder; + private final String groundTruthFile; + + + public RecallEvaluator( VectorBenchConfig config, JdbcExecutor executor, QueryBuilder knnBuilder, String groundTruthFile ) { + this.config = config; + this.executor = executor; + this.knnBuilder = knnBuilder; + this.groundTruthFile = groundTruthFile; + } + + + /** + * Runs the query set against the table and records the exact top-k ids of each query, returning + * the result in memory. + */ + public List> captureGroundTruthInMemory() { + int n = config.numberOfRecallQueries; + List> groundTruth = new ArrayList<>( n ); + try { + for ( int i = 0; i < n; i++ ) { + groundTruth.add( new HashSet<>( executor.executeQueryAndGetIds( knnBuilder.getNewQuery() ) ) ); + if ( ( i + 1 ) % 100 == 0 ) { + log.info( "Ground truth progress: {}/{}", i + 1, n ); + } + } + } catch ( ExecutorException e ) { + throw new RuntimeException( "Exception while capturing ground truth", e ); + } + return groundTruth; + } + + + /** + * Runs the query set against the table and records the exact top-k ids of each query to the + * ground-truth file (used by the standalone CLI tasks). + */ + public void captureGroundTruth() { + List> groundTruth = captureGroundTruthInMemory(); + try ( BufferedWriter writer = new BufferedWriter( new FileWriter( groundTruthFile ) ) ) { + for ( Set ids : groundTruth ) { + writer.write( ids.stream().map( String::valueOf ).collect( Collectors.joining( "," ) ) ); + writer.newLine(); + } + } catch ( IOException e ) { + throw new RuntimeException( "Could not write ground-truth file " + groundTruthFile, e ); + } + log.info( "Ground truth written to {}", groundTruthFile ); + } + + + /** + * Reruns the query set against the indexed table and compares each result to the stored ground truth. + * + * @return the mean recall@k over all queries. + */ + public double evaluate() { + return evaluate( readGroundTruth() ); + } + + + /** + * Reruns the query set against the indexed table and compares each result to the supplied ground truth. + * + * @return the mean recall@k over all queries. + */ + public double evaluate( List> groundTruth ) { + int n = Math.min( groundTruth.size(), config.numberOfRecallQueries ); + log.info( "Evaluating recall@{} over {} queries...", config.limitKnnQueries, n ); + + double recallSum = 0.0; + int counted = 0; + try { + for ( int i = 0; i < n; i++ ) { + Set expected = groundTruth.get( i ); + List returned = executor.executeQueryAndGetIds( knnBuilder.getNewQuery() ); + if ( expected.isEmpty() ) { + continue; + } + long hits = returned.stream().filter( expected::contains ).distinct().count(); + recallSum += (double) hits / expected.size(); + counted++; + if ( counted % 100 == 0 ) { + log.info( "Recall progress: {}/{}", counted, n ); + } + } + } catch ( ExecutorException e ) { + throw new RuntimeException( "Exception while evaluating recall", e ); + } + + double meanRecall = counted == 0 ? 0.0 : recallSum / counted; + log.info( "Mean recall@{} over {} queries: {}", config.limitKnnQueries, counted, meanRecall ); + return meanRecall; + } + + + private List> readGroundTruth() { + List> groundTruth = new ArrayList<>(); + try ( BufferedReader reader = new BufferedReader( new FileReader( groundTruthFile ) ) ) { + String line; + while ( ( line = reader.readLine() ) != null ) { + Set ids = new HashSet<>(); + if ( !line.isBlank() ) { + for ( String part : line.split( "," ) ) { + ids.add( Long.parseLong( part.trim() ) ); + } + } + groundTruth.add( ids ); + } + } catch ( IOException e ) { + throw new RuntimeException( "Could not read ground-truth file " + groundTruthFile + + ". Run the 'groundtruth' task (before creating the index) first.", e ); + } + return groundTruth; + } + +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/VectorBench.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/VectorBench.java new file mode 100644 index 0000000..7cd7133 --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/VectorBench.java @@ -0,0 +1,300 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench; + +import java.io.File; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; +import java.util.Vector; +import lombok.extern.slf4j.Slf4j; +import org.polypheny.simpleclient.QueryMode; +import org.polypheny.simpleclient.executor.Executor; +import org.polypheny.simpleclient.executor.Executor.DatabaseInstance; +import org.polypheny.simpleclient.executor.ExecutorException; +import org.polypheny.simpleclient.main.CsvWriter; +import org.polypheny.simpleclient.main.ProgressReporter; +import org.polypheny.simpleclient.query.Query; +import org.polypheny.simpleclient.query.QueryBuilder; +import org.polypheny.simpleclient.query.QueryListEntry; +import org.polypheny.simpleclient.scenario.PolyphenyScenario; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dql.SimpleKnnBooleanFeature; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dql.SimpleKnnBooleanFeatureFiltered; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dql.SimpleKnnIdIntFeature; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dql.SimpleKnnRealFeatureFiltered; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.ddl.CreateBooleanFeature; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.ddl.CreateIntFeature; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.ddl.CreateBooleanFeatureIndex; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.ddl.CreateMetadata; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.ddl.CreateRealFeature; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.ddl.CreateRealFeatureIndex; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dql.MetadataKnnIntFeature; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dql.MetadataKnnRealCrossJoin; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dql.MetadataKnnRealFeature; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dql.SimpleKnnIdRealFeature; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dql.SimpleKnnIntFeature; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dql.SimpleKnnRealCrossJoin; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dql.SimpleKnnRealFeature; +import org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dql.SimpleMetadata; + + +@Slf4j +public class VectorBench extends PolyphenyScenario { + + private final VectorBenchConfig config; + private String featureStore; + private String metadataStore; + + public VectorBench(Executor.ExecutorFactory executorFactory, VectorBenchConfig config, boolean commitAfterEveryQuery, boolean dumpQueryList ) { + super( executorFactory, commitAfterEveryQuery, dumpQueryList, QueryMode.TABLE ); + this.config = config; + this.featureStore = config.dataStoreFeature; + this.metadataStore = config.dataStoreMetadata; + + } + + + @Override + public void createSchema( DatabaseInstance databaseInstance, boolean includingKeys ) { + if ( queryMode != QueryMode.TABLE ) { + throw new UnsupportedOperationException( "Unsupported query mode: " + queryMode.name() ); + } + + if ( config.newTablePlacementStrategy.equalsIgnoreCase( "Optimized" ) && config.dataStores.size() > 1 ) { + if ( config.dataStoreMetadata == null ) { + throw new RuntimeException( "Optimized placements is selected but 'dataStoreMetadata' is null!" ); + } + if ( config.dataStoreFeature == null ) { + throw new RuntimeException( "Optimized placements is selected but 'dataStoreFeature' is null!" ); + } + } + + resolveStores( databaseInstance ); + + log.info( "Creating schema..." ); + Executor executor = null; + try { + executor = executorFactory.createExecutorInstance(); + executor.executeQuery( (new CreateMetadata( metadataStore )).getNewQuery() ); + executor.executeQuery( (new CreateIntFeature( featureStore , config.dimensionFeatureVectors, config.supportsNotNullArray )).getNewQuery() ); + executor.executeQuery( (new CreateRealFeature( featureStore , config.dimensionFeatureVectors, config.supportsNotNullArray )).getNewQuery() ); + executor.executeQuery( (new CreateBooleanFeature( featureStore, config.dimensionFeatureVectors, config.supportsNotNullArray )).getNewQuery() ); + } catch (ExecutorException e ) { + throw new RuntimeException( "Exception while creating schema", e ); + } finally { + commitAndCloseExecutor( executor ); + } + } + + + public void createIndex() { + if ( !config.useIndex ) { + return; + } + // Polypheny vector-maps array columns only when declared with element NOT NULL (REAL NOT NULL ARRAY). + // A plain ARRAY column is not vector-mapped, so no vector index can be built. + if ( !config.supportsNotNullArray ) { + log.info( "Skipping index creation: plain ARRAY columns are not vector-mapped in Polypheny (requires NOT NULL ARRAY)." ); + return; + } + Executor executor = null; + try { + executor = executorFactory.createExecutorInstance(); + long start = System.nanoTime(); + if ( indexSupportsMetric( config.indexMethod, config.distanceNorm ) ) { + executor.executeQuery( new CreateRealFeatureIndex( featureStore, config.indexMethod, config.distanceNorm, config.indexM, config.indexEfConstruction, config.indexLists ).getNewQuery() ); + } else { + log.info( "Skipping real index: {} does not support metric '{}'.", config.indexMethod, config.distanceNorm ); + } + if ( indexSupportsMetric( config.indexMethod, config.booleanDistanceNorm ) ) { + executor.executeQuery( new CreateBooleanFeatureIndex( featureStore, config.indexMethod, config.booleanDistanceNorm, config.indexM, config.indexEfConstruction, config.indexLists ).getNewQuery() ); + } else { + log.info( "Skipping boolean index: {} does not support metric '{}'.", config.indexMethod, config.booleanDistanceNorm ); + } + executor.executeCommit(); + long durationMillis = ( System.nanoTime() - start ) / 1_000_000L; + log.info( "Vector indexes built in {} ms", durationMillis ); + } catch ( ExecutorException e ) { + throw new RuntimeException( "Exception while creating vector index", e ); + } finally { + commitAndCloseExecutor( executor ); + } + } + + + private static boolean indexSupportsMetric( String method, String metric ) { + // HNSW supports all metrics, IVFFlat does not support L1 or JACCARD. + if ( method.equalsIgnoreCase( "ivfflat" ) ) { + return !( metric.equalsIgnoreCase( "L1" ) || metric.equalsIgnoreCase( "JACCARD" ) ); + } + return true; + } + + + @Override + public void generateData( DatabaseInstance databaseInstance, ProgressReporter progressReporter ) { + log.info( "Generating data..." ); + resolveStores( databaseInstance ); + Executor executor1 = executorFactory.createExecutorInstance(); + DataGenerator dataGenerator = new DataGenerator( executor1, config, progressReporter ); + + try { + dataGenerator.generateMetadata(); + dataGenerator.generateIntFeatures(); + dataGenerator.generateRealFeatures(); + dataGenerator.generateBooleanFeatures(); + } catch ( ExecutorException e ) { + throw new RuntimeException( "Exception while generating data", e ); + } finally { + commitAndCloseExecutor( executor1 ); + } + + // Build the index for the benchmark run (Chronos has no separate index task). + if ( databaseInstance != null && config.useIndex ) { + createIndex(); + } + } + + + @Override + public long execute( ProgressReporter progressReporter, CsvWriter csvWriter, File outputDirectory, int numberOfThreads ) { + log.info( "Preparing query list for the benchmark..." ); + List queryList = new Vector<>(); + addNumberOfTimes( queryList, new SimpleKnnIntFeature( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.distanceNorm ), config.numberOfSimpleKnnIntFeatureQueries ); + addNumberOfTimes( queryList, new SimpleKnnRealFeature( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.distanceNorm ), config.numberOfSimpleKnnRealFeatureQueries ); + addNumberOfTimes( queryList, new SimpleMetadata( config.randomSeedQuery, config.numberOfEntries ), config.numberOfSimpleMetadataQueries ); + addNumberOfTimes( queryList, new SimpleKnnIdRealFeature( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.distanceNorm ), config.numberOfSimpleKnnIdRealFeatureQueries ); + addNumberOfTimes( queryList, new MetadataKnnIntFeature( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.distanceNorm ), config.numberOfMetadataKnnIntFeatureQueries ); + addNumberOfTimes( queryList, new MetadataKnnRealFeature( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.distanceNorm ), config.numberOfMetadataKnnRealFeatureQueries ); + addNumberOfTimes( queryList, new SimpleKnnRealCrossJoin( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.distanceNorm ), config.numberOfSimpleKnnRealCrossJoinQueries ); + addNumberOfTimes( queryList, new SimpleKnnRealFeatureFiltered( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.distanceNorm, "cat_A" ), config.numberOfSimpleKnnRealFeatureFilteredQueries ); + addNumberOfTimes( queryList, new SimpleKnnBooleanFeature( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.booleanDistanceNorm ), config.numberOfSimpleKnnBooleanFeatureQueries ); + addNumberOfTimes( queryList, new SimpleKnnBooleanFeatureFiltered( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.booleanDistanceNorm, "cat_A" ), config.numberOfSimpleKnnBooleanFeatureFilteredQueries ); + addNumberOfTimes( queryList, new MetadataKnnRealCrossJoin( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.distanceNorm ), config.numberOfMetadataKnnRealCrossJoinQueries ); + addNumberOfTimes( queryList, new SimpleKnnIdIntFeature( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.distanceNorm ), config.numberOfSimpleKnnIdIntFeatureQueries ); + + + + return commonExecute( queryList, progressReporter, outputDirectory, numberOfThreads, Query::getSql, () -> executorFactory.createExecutorInstance( csvWriter ), new Random() ); + } + + + @Override + public void warmUp( ProgressReporter progressReporter ) { + log.info( "Warm-up..." ); + + Executor executor = null; + SimpleKnnIntFeature simpleKnnIntFeatureBuilder = new SimpleKnnIntFeature( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.distanceNorm ); + SimpleKnnRealFeature simpleKnnRealFeatureBuilder = new SimpleKnnRealFeature( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.distanceNorm ); + SimpleMetadata simpleMetadataBuilder = new SimpleMetadata( config.randomSeedQuery, config.numberOfEntries ); + SimpleKnnIdRealFeature simpleKnnIdRealFeatureBuilder = new SimpleKnnIdRealFeature( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.distanceNorm ); + MetadataKnnIntFeature metadataKnnIntFeature = new MetadataKnnIntFeature( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.distanceNorm ); + MetadataKnnRealFeature metadataKnnRealFeature = new MetadataKnnRealFeature( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.distanceNorm ); + MetadataKnnRealCrossJoin metadataKnnCrossJoin = new MetadataKnnRealCrossJoin( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.distanceNorm ); + SimpleKnnRealCrossJoin simpleKnnCrossJoin = new SimpleKnnRealCrossJoin( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.distanceNorm ); + SimpleKnnRealFeatureFiltered simpleKnnRealFeatureFiltered = new SimpleKnnRealFeatureFiltered( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.distanceNorm, "cat_A" ); + SimpleKnnBooleanFeature simpleKnnBooleanFeature = new SimpleKnnBooleanFeature( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.booleanDistanceNorm ); + SimpleKnnBooleanFeatureFiltered simpleKnnBooleanFeatureFiltered = new SimpleKnnBooleanFeatureFiltered( config.randomSeedQuery, config.dimensionFeatureVectors, config.limitKnnQueries, config.booleanDistanceNorm, "cat_A" ); + SimpleKnnIdIntFeature simpleKnnIdIntFeature = new SimpleKnnIdIntFeature( config.randomSeedQuery, config.dimensionFeatureVectors,config.limitKnnQueries, config.distanceNorm ); + + for ( int i = 0; i < config.numberOfWarmUpIterations; i++ ) { + try { + executor = executorFactory.createExecutorInstance(); + if ( config.numberOfSimpleKnnIntFeatureQueries > 0 ) { + executor.executeQuery( simpleKnnIntFeatureBuilder.getNewQuery() ); + } + if ( config.numberOfSimpleKnnRealFeatureQueries > 0 ) { + executor.executeQuery( simpleKnnRealFeatureBuilder.getNewQuery() ); + } + if ( config.numberOfSimpleMetadataQueries > 0 ) { + executor.executeQuery( simpleMetadataBuilder.getNewQuery() ); + } + if ( config.numberOfSimpleKnnIdRealFeatureQueries > 0 ) { + executor.executeQuery( simpleKnnIdRealFeatureBuilder.getNewQuery() ); + } + if ( config.numberOfMetadataKnnIntFeatureQueries > 0 ) { + executor.executeQuery( metadataKnnIntFeature.getNewQuery() ); + } + if ( config.numberOfSimpleKnnIdIntFeatureQueries > 0 ) { + executor.executeQuery( simpleKnnIdIntFeature.getNewQuery() ); + } + if ( config.numberOfMetadataKnnRealFeatureQueries > 0 ) { + executor.executeQuery( metadataKnnRealFeature.getNewQuery() ); + } + if ( config.numberOfMetadataKnnRealCrossJoinQueries > 0 ) { + executor.executeQuery( metadataKnnCrossJoin.getNewQuery() ); + } + if ( config.numberOfSimpleKnnRealCrossJoinQueries > 0 ) { + executor.executeQuery( simpleKnnCrossJoin.getNewQuery() ); + } + if ( config.numberOfSimpleKnnRealFeatureFilteredQueries > 0 ) { + executor.executeQuery( simpleKnnRealFeatureFiltered.getNewQuery() ); + } + if ( config.numberOfSimpleKnnBooleanFeatureQueries > 0 ) { + executor.executeQuery( simpleKnnBooleanFeature.getNewQuery() ); + } + if ( config.numberOfSimpleKnnBooleanFeatureFilteredQueries > 0 ) { + executor.executeQuery( simpleKnnBooleanFeatureFiltered.getNewQuery() ); + } + } catch ( ExecutorException e ) { + throw new RuntimeException( "Error while executing warm-up queries", e ); + } finally { + commitAndCloseExecutor( executor ); + } + try { + Thread.sleep( 10000 ); + } catch ( InterruptedException e ) { + throw new RuntimeException( "Unexpected interrupt", e ); + } + } + } + + + @Override + public int getNumberOfInsertThreads() { + return 1; + } + + + private void addNumberOfTimes( List list, QueryBuilder queryBuilder, int numberOfTimes ) { + int id = queryTypes.size() + 1; + queryTypes.put( id, queryBuilder.getNewQuery().getSql() ); + measuredTimePerQueryType.put( id, Collections.synchronizedList( new LinkedList<>() ) ); + for ( int i = 0; i < numberOfTimes; i++ ) { + list.add( new QueryListEntry( queryBuilder.getNewQuery(), id ) ); + } + } + + + private void resolveStores( DatabaseInstance databaseInstance ) { + if ( databaseInstance != null ) { + featureStore = findMatchingDataStoreName( config.dataStoreFeature ); + metadataStore = findMatchingDataStoreName( config.dataStoreMetadata ); + } + } + + +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/VectorBenchConfig.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/VectorBenchConfig.java new file mode 100644 index 0000000..f6f835d --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/VectorBenchConfig.java @@ -0,0 +1,224 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package org.polypheny.simpleclient.scenario.vectorbench; + +import java.util.Map; +import java.util.Properties; +import java.util.Random; +import lombok.extern.slf4j.Slf4j; +import org.polypheny.simpleclient.scenario.AbstractConfig; + + +@Slf4j +public class VectorBenchConfig extends AbstractConfig { + + public String mode; + + public String dataStoreFeature; + public String dataStoreMetadata; + + public String postgresHost; + + public long randomSeedInsert; + public long randomSeedQuery; + + public int dimensionFeatureVectors; + public int batchSizeInserts; + public int batchSizeQueries; + + public int numberOfEntries; + public int numberOfSimpleKnnIntFeatureQueries; + public int numberOfSimpleKnnRealFeatureQueries; + public int numberOfSimpleMetadataQueries; + public int numberOfSimpleKnnIdIntFeatureQueries; + public int numberOfSimpleKnnIdRealFeatureQueries; + public int numberOfMetadataKnnIntFeatureQueries; + public int numberOfMetadataKnnRealFeatureQueries; + public int numberOfSimpleKnnRealCrossJoinQueries; + public int numberOfMetadataKnnRealCrossJoinQueries; + public int numberOfSimpleKnnRealFeatureFilteredQueries; + public int numberOfSimpleKnnBooleanFeatureQueries; + public int numberOfSimpleKnnBooleanFeatureFilteredQueries; + +// public final int numberOfCombinedQueries; + + public int limitKnnQueries; + public String distanceNorm; + public String booleanDistanceNorm; + + public boolean useIndex; + public String indexMethod; + public int indexM; + public int indexEfConstruction; + public int indexLists; + + // Query-time index parameters (pgvector, applied on the direct-postgres path only) + public int queryEfSearch; + public int queryProbes; + + public int numberOfRecallQueries; + public boolean supportsNotNullArray; + + + public VectorBenchConfig(Properties properties, int multiplier ) { + super( "knnBench", "polypheny-jdbc", properties ); + + mode = getStringProperty( properties, "mode" ); + dataStoreFeature = getStringProperty( properties,"dataStoreFeature" ); + dataStoreMetadata = getStringProperty( properties, "dataStoreMeta" ); + supportsNotNullArray = getBooleanProperty( properties, "supportsNotNullArray" ); + + if ( dataStoreFeature.equals( dataStoreMetadata ) ) { + dataStores.add( dataStoreFeature ); + } else { + dataStores.add( dataStoreFeature ); + dataStores.add( dataStoreMetadata ); + } + //dataStores.add( "cottontail" ); + + postgresHost = getStringProperty( properties, "postgresHost" ); + + if ( getBooleanProperty( properties, "useRandomSeeds" ) ) { + Random tempRand = new Random(); + randomSeedInsert = tempRand.nextLong(); + randomSeedQuery = tempRand.nextLong(); + } else { + randomSeedInsert = getLongProperty( properties, "randomSeedInsert" ); + randomSeedQuery = getLongProperty( properties, "randomSeedQuery" ); + } + + dimensionFeatureVectors = getIntProperty( properties, "dimensionFeatureVectors" ); + batchSizeInserts = getIntProperty( properties, "batchSizeInserts" ); + numberOfEntries = getIntProperty( properties, "numberOfEntries" ) * multiplier; + + batchSizeQueries = getIntProperty( properties, "batchSizeQueries" ); + numberOfSimpleKnnIntFeatureQueries = getIntProperty( properties, "numberOfSimpleKnnIntFeatureQueries" ) * multiplier; + numberOfSimpleKnnRealFeatureQueries = getIntProperty( properties, "numberOfSimpleKnnRealFeatureQueries" ) * multiplier; + numberOfSimpleMetadataQueries = getIntProperty( properties, "numberOfSimpleMetadataQueries" ) * multiplier; + numberOfSimpleKnnIdIntFeatureQueries = getIntProperty( properties, "numberOfSimpleKnnIdIntFeatureQueries" ) * multiplier; + numberOfSimpleKnnIdRealFeatureQueries = getIntProperty( properties, "numberOfSimpleKnnIdRealFeatureQueries" ) * multiplier; + numberOfMetadataKnnIntFeatureQueries = getIntProperty( properties, "numberOfMetadataKnnIntFeatureQueries" ) * multiplier; + numberOfMetadataKnnRealFeatureQueries = getIntProperty( properties, "numberOfMetadataKnnRealFeatureQueries" ) * multiplier; + numberOfSimpleKnnRealCrossJoinQueries = getIntProperty( properties, "numberOfSimpleKnnRealCrossJoinQueries" ) * multiplier; + numberOfMetadataKnnRealCrossJoinQueries = getIntProperty( properties, "numberOfMetadataKnnRealCrossJoinQueries" ) * multiplier; + numberOfSimpleKnnRealFeatureFilteredQueries = getIntProperty( properties, "numberOfSimpleKnnRealFeatureFilteredQueries" ) * multiplier; + numberOfSimpleKnnBooleanFeatureQueries = getIntProperty( properties, "numberOfSimpleKnnBooleanFeatureQueries" ) * multiplier; + numberOfSimpleKnnBooleanFeatureFilteredQueries = getIntProperty( properties, "numberOfSimpleKnnBooleanFeatureFilteredQueries" ) * multiplier; + + limitKnnQueries = getIntProperty( properties, "limitKnnQueries" ); + distanceNorm = getStringProperty( properties, "distanceNorm" ); + booleanDistanceNorm = getStringProperty( properties, "booleanDistanceNorm" ); + + useIndex = getBooleanProperty( properties, "useIndex" ); + indexMethod = getStringProperty( properties, "indexMethod" ); + indexM = getIntProperty( properties, "indexM" ); + indexEfConstruction = getIntProperty( properties, "indexEfConstruction" ); + indexLists = getIntProperty( properties, "indexLists" ); + queryEfSearch = getIntProperty( properties, "queryEfSearch" ); + queryProbes = getIntProperty( properties, "queryProbes" ); + numberOfRecallQueries = getIntProperty( properties, "numberOfRecallQueries" ); + } + + + public VectorBenchConfig(Map cdl ) { + super( "gavel", cdl.get( "store" ), cdl ); + + mode = cdlGetOrDefault( cdl, "mode", "polypheny" ); + dataStoreFeature = cdl.get( "dataStoreFeature" ); + dataStoreMetadata = cdl.get( "dataStoreMetadata" ); + + supportsNotNullArray = cdl.get( "supportsNotNullArray" ) == null || Boolean.parseBoolean( cdl.get( "supportsNotNullArray" ) ); + + if ( dataStoreFeature.equals( dataStoreMetadata ) ) { + dataStores.add( dataStoreFeature ); + } else { + dataStores.add( dataStoreFeature ); + dataStores.add( dataStoreMetadata ); + } + + if ( Boolean.parseBoolean( cdl.get( "useRandomSeeds" ) ) ) { + Random tempRand = new Random(); + randomSeedInsert = tempRand.nextLong(); + randomSeedQuery = tempRand.nextLong(); + } else { + randomSeedInsert = Long.parseLong( cdl.get( "randomSeedInsert" ) ); + randomSeedQuery = Long.parseLong( cdl.get( "randomSeedQuery" ) ); + } + + dimensionFeatureVectors = Integer.parseInt( cdl.get( "dimensionFeatureVectors" ) ); + batchSizeInserts = Integer.parseInt( cdl.get( "batchSizeInserts" ) ); + numberOfEntries = Integer.parseInt( cdl.get( "numberOfEntries" ) ); + + batchSizeQueries = Integer.parseInt( cdl.get( "batchSizeQueries" ) ); + numberOfSimpleKnnIntFeatureQueries = Integer.parseInt( cdl.get( "numberOfSimpleKnnIntFeatureQueries" ) ); + numberOfSimpleKnnRealFeatureQueries = Integer.parseInt( cdl.get( "numberOfSimpleKnnRealFeatureQueries" ) ); + numberOfSimpleMetadataQueries = Integer.parseInt( cdl.get( "numberOfSimpleMetadataQueries" ) ); + numberOfSimpleKnnIdIntFeatureQueries = Integer.parseInt( cdl.get( "numberOfSimpleKnnIdIntFeatureQueries" ) ); + numberOfSimpleKnnIdRealFeatureQueries = Integer.parseInt( cdl.get( "numberOfSimpleKnnIdRealFeatureQueries" ) ); + numberOfMetadataKnnIntFeatureQueries = Integer.parseInt( cdl.get( "numberOfMetadataKnnIntFeatureQueries" ) ); + numberOfMetadataKnnRealFeatureQueries = Integer.parseInt( cdl.get( "numberOfMetadataKnnRealFeatureQueries" ) ); + numberOfSimpleKnnRealCrossJoinQueries = Integer.parseInt( cdl.get( "numberOfSimpleKnnRealCrossJoinQueries" ) ); + numberOfMetadataKnnRealCrossJoinQueries = Integer.parseInt( cdl.get( "numberOfMetadataKnnRealCrossJoinQueries" ) ); + numberOfSimpleKnnRealFeatureFilteredQueries = Integer.parseInt( cdl.get( "numberOfSimpleKnnRealFeatureFilteredQueries") ); + numberOfSimpleKnnBooleanFeatureQueries = Integer.parseInt( cdl.get( "numberOfSimpleKnnBooleanFeatureQueries" ) ); + numberOfSimpleKnnBooleanFeatureFilteredQueries = Integer.parseInt( cdl.get( "numberOfSimpleKnnBooleanFeatureFilteredQueries" ) ); + //TODO: insert missing +// numberOfCombinedQueries = getIntProperty( properties, "numberOfCombinedQueries" ) * multiplier; + limitKnnQueries = Integer.parseInt( cdl.get( "limitKnnQueries" ) ); + distanceNorm = cdl.get( "distanceNorm" ).trim(); + booleanDistanceNorm = cdl.get( "booleanDistanceNorm" ).trim(); + postgresHost = cdlGetOrDefault( cdl, "postgresHost", "127.0.0.1" ); + + useIndex = Boolean.parseBoolean( cdlGetOrDefault( cdl, "useIndex", "false" ) ); + indexMethod = cdlGetOrDefault( cdl, "indexMethod", "hnsw" ); + indexM = Integer.parseInt( cdlGetOrDefault( cdl, "indexM", "16" ) ); + indexEfConstruction = Integer.parseInt( cdlGetOrDefault( cdl, "indexEfConstruction", "64" ) ); + indexLists = Integer.parseInt( cdlGetOrDefault( cdl, "indexLists", "100" ) ); + queryEfSearch = Integer.parseInt( cdlGetOrDefault( cdl, "queryEfSearch", "40" ) ); + queryProbes = Integer.parseInt( cdlGetOrDefault( cdl, "queryProbes", "1" ) ); + + numberOfRecallQueries = Integer.parseInt( cdlGetOrDefault( cdl, "numberOfRecallQueries", "100" ) ); + } + + + // For MultiBench + protected VectorBenchConfig(String scenario, String system, Map cdl ) { + super( scenario, system, cdl ); + } + + + // For MultiBench + protected VectorBenchConfig(String scenario, String system, Properties properties ) { + super( scenario, system, properties ); + } + + + @Override + public boolean usePreparedBatchForDataInsertion() { + return true; + } + +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/ddl/CreateBooleanFeature.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/ddl/CreateBooleanFeature.java new file mode 100644 index 0000000..9dca802 --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/ddl/CreateBooleanFeature.java @@ -0,0 +1,108 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.ddl; + +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.Query; +import org.polypheny.simpleclient.query.QueryBuilder; +import java.util.Map; + +public class CreateBooleanFeature extends QueryBuilder { + + private final String store; + private final int dimension; + private final boolean supportsNotNullArray; + + + public CreateBooleanFeature( String store, int dimension, boolean supportsNotNullArray ) { + this.store = store; + this.dimension = dimension; + this.supportsNotNullArray = supportsNotNullArray; + } + + + @Override + public Query getNewQuery() { + return new CreateBooleanFeatureQuery( store, dimension, supportsNotNullArray ); + } + + + private static class CreateBooleanFeatureQuery extends Query { + + private final String store; + private final int dimension; + private final boolean supportsNotNullArray; + + + CreateBooleanFeatureQuery( String store, int dimension, boolean supportsNotNullArray ) { + super( false ); + this.store = store; + this.dimension = dimension; + this.supportsNotNullArray = supportsNotNullArray; + } + + + @Override + public String getSql() { + String elementsNullable = supportsNotNullArray ? " NOT NULL " : " "; + String sql = "CREATE TABLE knn_booleanfeature (" + + "id INTEGER NOT NULL, " + + "feature BOOLEAN" + elementsNullable + "ARRAY(1, " + this.dimension + "), " + + "category VARCHAR(50), " + + "PRIMARY KEY(id))"; + if ( this.store != null ) { + sql += " ON STORE \"" + this.store + "\""; + } + return sql; + } + + + @Override + public String getParameterizedSqlQuery() { + return null; + } + + + @Override + public Map> getParameterValues() { + return null; + } + + + @Override + public HttpRequest getRest() { + return null; + } + + + @Override + public String getMongoQl() { + return null; + } + + } + +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/ddl/CreateBooleanFeatureIndex.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/ddl/CreateBooleanFeatureIndex.java new file mode 100644 index 0000000..1d910dc --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/ddl/CreateBooleanFeatureIndex.java @@ -0,0 +1,123 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.ddl; + +import java.util.Map; +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.Query; +import org.polypheny.simpleclient.query.QueryBuilder; + + +public class CreateBooleanFeatureIndex extends QueryBuilder { + + private final String store; + private final String method; + private final String metric; + private final int m; + private final int efConstruction; + private final int lists; + + + public CreateBooleanFeatureIndex( String store, String method, String metric, int m, int efConstruction, int lists ) { + this.store = store; + this.method = method; + this.metric = metric; + this.m = m; + this.efConstruction = efConstruction; + this.lists = lists; + } + + + @Override + public Query getNewQuery() { + return new CreateBooleanFeatureIndexQuery( store, method, metric, m, efConstruction, lists ); + } + + + private static class CreateBooleanFeatureIndexQuery extends Query { + + private final String store; + private final String method; + private final String metric; + private final int m; + private final int efConstruction; + private final int lists; + private final boolean isHnsw; + + + CreateBooleanFeatureIndexQuery( String store, String method, String metric, int m, int efConstruction, int lists ) { + super( false ); + this.store = store; + this.method = method; + this.metric = metric; + this.m = m; + this.efConstruction = efConstruction; + this.lists = lists; + this.isHnsw = method.equals( "hnsw" ); + } + + + @Override + public String getSql() { + String sql = "ALTER TABLE knn_booleanfeature ADD INDEX feature_bool_" + method + + " ON (feature) USING " + method; + if ( store != null ) { + sql += " ON STORE \"" + store + "\""; + } + String params = isHnsw + ? "m=" + m + ", ef_construction=" + efConstruction + : "lists=" + lists; + sql += " WITH (metric='" + metric + "', " + params + ")"; + return sql; + } + + + @Override + public String getParameterizedSqlQuery() { + return null; + } + + + @Override + public Map> getParameterValues() { + return null; + } + + + @Override + public HttpRequest getRest() { + return null; + } + + + @Override + public String getMongoQl() { + return null; + } + + } + +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/ddl/CreateIntFeature.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/ddl/CreateIntFeature.java new file mode 100644 index 0000000..f17320e --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/ddl/CreateIntFeature.java @@ -0,0 +1,106 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.ddl; + +import java.util.Map; +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.Query; +import org.polypheny.simpleclient.query.QueryBuilder; + + +public class CreateIntFeature extends QueryBuilder { + + private final String store; + private final int dimension; + private final boolean supportsNotNullArray; + + + public CreateIntFeature( String store, int dimension, boolean supportsNotNullArray ) { + this.store = store; + this.dimension = dimension; + this.supportsNotNullArray = supportsNotNullArray; + } + + + @Override + public Query getNewQuery() { + return new CreateIntFeatureQuery( store, dimension, supportsNotNullArray ); + } + + + private static class CreateIntFeatureQuery extends Query { + + private final String store; + private final int dimension; + private final boolean supportsNotNullArray; + + + CreateIntFeatureQuery( String store, int dimension, boolean supportsNotNullArray ) { + super( false ); + this.store = store; + this.dimension = dimension; + this.supportsNotNullArray = supportsNotNullArray; + } + + + @Override + public String getSql() { + String elementsNullable = supportsNotNullArray ? " NOT NULL " : " "; + String sql = "CREATE TABLE knn_intfeature (id INTEGER NOT NULL, feature INTEGER" + elementsNullable + "ARRAY(1, " + this.dimension + "), PRIMARY KEY(id))"; + if ( this.store != null ) { + sql += " ON STORE \"" + this.store + "\""; + } + return sql; + } + + + @Override + public String getParameterizedSqlQuery() { + return null; + } + + + @Override + public Map> getParameterValues() { + return null; + } + + + @Override + public HttpRequest getRest() { + return null; + } + + + @Override + public String getMongoQl() { + return null; + } + + + } + +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/ddl/CreateMetadata.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/ddl/CreateMetadata.java new file mode 100644 index 0000000..37ff0a8 --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/ddl/CreateMetadata.java @@ -0,0 +1,97 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.ddl; + +import java.util.Map; +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.Query; +import org.polypheny.simpleclient.query.QueryBuilder; + + + +public class CreateMetadata extends QueryBuilder { + + private final String store; + + + public CreateMetadata( String store ) { + this.store = store; + } + + + @Override + public Query getNewQuery() { + return new CreateMetadataQuery( this.store ); + } + + + private static class CreateMetadataQuery extends Query { + + private final String store; + + + CreateMetadataQuery( String store ) { + super( false ); + this.store = store; + } + + + @Override + public String getSql() { + String sql = "CREATE TABLE knn_metadata (id integer NOT NULL, textdata VARCHAR(100), PRIMARY KEY (id))"; + if ( this.store != null ) { + sql += " ON STORE \"" + this.store + "\""; + } + return sql; + } + + + @Override + public String getParameterizedSqlQuery() { + return null; + } + + + @Override + public Map> getParameterValues() { + return null; + } + + + @Override + public HttpRequest getRest() { + return null; + } + + + @Override + public String getMongoQl() { + return null; + } + + } + +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/ddl/CreateRealFeature.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/ddl/CreateRealFeature.java new file mode 100644 index 0000000..d9290d1 --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/ddl/CreateRealFeature.java @@ -0,0 +1,109 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.ddl; + +import java.util.Map; +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.Query; +import org.polypheny.simpleclient.query.QueryBuilder; + + +public class CreateRealFeature extends QueryBuilder { + + private final String store; + private final int dimension; + private final boolean supportsNotNullArray; + + + public CreateRealFeature( String store, int dimension, boolean supportsNotNullArray ) { + this.store = store; + this.dimension = dimension; + this.supportsNotNullArray = supportsNotNullArray; + } + + + @Override + public Query getNewQuery() { + return new CreateRealFeatureQuery( store, dimension, supportsNotNullArray ); + } + + + private static class CreateRealFeatureQuery extends Query { + + private final String store; + private final int dimension; + private final boolean supportsNotNullArray; + + + CreateRealFeatureQuery( String store, int dimension, boolean supportsNotNullArray ) { + super( false ); + this.store = store; + this.dimension = dimension; + this.supportsNotNullArray = supportsNotNullArray; + } + + + @Override + public String getSql() { + String elementsNullable = supportsNotNullArray ? " NOT NULL " : " "; + String sql = "CREATE TABLE knn_realfeature (" + + "id INTEGER NOT NULL, " + + "category VARCHAR(50), " + + "feature REAL" + elementsNullable + "ARRAY(1, " + this.dimension + "), " + + "PRIMARY KEY(id))"; + if ( this.store != null ) { + sql += " ON STORE \"" + this.store + "\""; + } + return sql; + } + + + @Override + public String getParameterizedSqlQuery() { + return null; + } + + + @Override + public Map> getParameterValues() { + return null; + } + + + @Override + public HttpRequest getRest() { + return null; + } + + + @Override + public String getMongoQl() { + return null; + } + + } + +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/ddl/CreateRealFeatureIndex.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/ddl/CreateRealFeatureIndex.java new file mode 100644 index 0000000..cb556a4 --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/ddl/CreateRealFeatureIndex.java @@ -0,0 +1,123 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.ddl; + +import java.util.Map; +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.Query; +import org.polypheny.simpleclient.query.QueryBuilder; + + +public class CreateRealFeatureIndex extends QueryBuilder { + + private final String store; + private final String method; + private final String metric; + private final int m; + private final int efConstruction; + private final int lists; + + + public CreateRealFeatureIndex( String store, String method, String metric, int m, int efConstruction, int lists ) { + this.store = store; + this.method = method; + this.metric = metric; + this.m = m; + this.efConstruction = efConstruction; + this.lists = lists; + } + + + @Override + public Query getNewQuery() { + return new CreateRealFeatureIndexQuery( store, method, metric, m, efConstruction, lists ); + } + + + private static class CreateRealFeatureIndexQuery extends Query { + + private final String store; + private final String method; + private final String metric; + private final int m; + private final int efConstruction; + private final int lists; + private final boolean isHnsw; + + + CreateRealFeatureIndexQuery( String store, String method, String metric, int m, int efConstruction, int lists ) { + super( false ); + this.store = store; + this.method = method; + this.metric = metric; + this.m = m; + this.efConstruction = efConstruction; + this.lists = lists; + this.isHnsw = method.equals( "hnsw" ); + } + + + @Override + public String getSql() { + String sql = "ALTER TABLE knn_realfeature ADD INDEX feature_" + method + + " ON (feature) USING " + method; + if ( store != null ) { + sql += " ON STORE \"" + store + "\""; + } + String params = isHnsw + ? "m=" + m + ", ef_construction=" + efConstruction + : "lists=" + lists; + sql += " WITH (metric='" + metric + "', " + params + ")"; + return sql; + } + + + @Override + public String getParameterizedSqlQuery() { + return null; + } + + + @Override + public Map> getParameterValues() { + return null; + } + + + @Override + public HttpRequest getRest() { + return null; + } + + + @Override + public String getMongoQl() { + return null; + } + + } + +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dml/InsertBooleanFeature.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dml/InsertBooleanFeature.java new file mode 100644 index 0000000..4759858 --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dml/InsertBooleanFeature.java @@ -0,0 +1,142 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dml; + +import com.google.gson.JsonObject; +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.BatchableInsert; +import org.polypheny.simpleclient.query.QueryBuilder; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.atomic.AtomicInteger; + +public class InsertBooleanFeature extends QueryBuilder { + + private static final boolean EXPECT_RESULT = false; + private static final AtomicInteger nextId = new AtomicInteger( 1 ); + private final long randomSeed; + private final int dimension; + private final Random random; + private static final String[] CATEGORIES = {"cat_A", "cat_B", "cat_C", "cat_D"}; + + public InsertBooleanFeature( long randomSeed, int dimension ) { + this.randomSeed = randomSeed; + this.dimension = dimension; + this.random = new Random( randomSeed ); + } + + + private Boolean[] getRandomVector() { + Boolean[] booleans = new Boolean[this.dimension]; + for ( int i = 0; i < this.dimension; i++ ) { + // Generate random bitvectors (true/false) + booleans[i] = random.nextBoolean(); + } + return booleans; + } + + + @Override + public synchronized BatchableInsert getNewQuery() { + return new InsertBooleanFeatureQuery( + nextId.getAndIncrement(), + getRandomVector(), + CATEGORIES[random.nextInt(CATEGORIES.length)] + ); + } + + + private static class InsertBooleanFeatureQuery extends BatchableInsert { + + private static final String SQL = "INSERT INTO knn_booleanfeature (id, category, feature) VALUES "; + private final int id; + private final Boolean[] feature; + private final String randomCategory; + + private InsertBooleanFeatureQuery( int id, Boolean[] feature, String randomCategory ) { + super( EXPECT_RESULT ); + this.id = id; + this.feature = feature; + this.randomCategory = randomCategory; + } + + + @Override + public String getSqlRowExpression() { + // Arrays.toString on Boolean[] will result in "[true, false, true...]" + return "(" + id + ", '" + randomCategory + "', ARRAY" + Arrays.toString( feature ) + ")"; + } + + + @Override + public String getParameterizedSqlQuery() { + return SQL + "(?, ?, ?)"; + } + + + @Override + public Map> getParameterValues() { + Map> map = new HashMap<>(); + map.put( 1, new ImmutablePair<>( DataTypes.INTEGER, id ) ); + map.put( 2, new ImmutablePair<>( DataTypes.VARCHAR, randomCategory ) ); + map.put( 3, new ImmutablePair<>( DataTypes.ARRAY_BOOLEAN, feature ) ); + return map; + } + + + @Override + public JsonObject getRestRowExpression() { return null; } + + + @Override + public String getEntity() { return "public.knn_booleanfeature"; } + + + @Override + public String getSql() { + return SQL + getSqlRowExpression(); + } + + + @Override + public HttpRequest getRest() { return null; } + + + @Override + public String getMongoQl() { return null; } + + + private String getBitString( Boolean[] feature ) { + StringBuilder sb = new StringBuilder(feature.length); + for ( Boolean b : feature ) { + sb.append( b ? "1" : "0" ); + } + return sb.toString(); + } + } +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dml/InsertIntFeature.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dml/InsertIntFeature.java new file mode 100644 index 0000000..8ed7133 --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dml/InsertIntFeature.java @@ -0,0 +1,143 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dml; + +import com.google.gson.JsonObject; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.atomic.AtomicInteger; +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.BatchableInsert; +import org.polypheny.simpleclient.query.QueryBuilder; + + +public class InsertIntFeature extends QueryBuilder { + + private static final boolean EXPECT_RESULT = false; + + private static final AtomicInteger nextId = new AtomicInteger( 1 ); + private final long randomSeed; + private final int dimension; + + private final Random random; + + + public InsertIntFeature( long randomSeed, int dimension ) { + this.randomSeed = randomSeed; + this.dimension = dimension; + + this.random = new Random( randomSeed ); + } + + + private Integer[] getRandomVector() { + Integer[] integers = new Integer[this.dimension]; + for ( int i = 0; i < this.dimension; i++ ) { + integers[i] = random.nextInt( 500 ); + } + + return integers; + } + + + @Override + public synchronized BatchableInsert getNewQuery() { + return new InsertIntFeatureQuery( + nextId.getAndIncrement(), + getRandomVector() + ); + } + + + private static class InsertIntFeatureQuery extends BatchableInsert { + + private static final String SQL = "INSERT INTO knn_intfeature (id, feature) VALUES "; + private final int id; + private final Integer[] feature; + + + private InsertIntFeatureQuery( int id, Integer[] feature ) { + super( EXPECT_RESULT ); + this.id = id; + this.feature = feature; + } + + + @Override + public String getSqlRowExpression() { + return "(" + id + ", ARRAY" + Arrays.toString( feature ) + ")"; + } + + + @Override + public String getParameterizedSqlQuery() { + return SQL + "(?, ?)"; + } + + + @Override + public Map> getParameterValues() { + Map> map = new HashMap<>(); + map.put( 1, new ImmutablePair<>( DataTypes.INTEGER, id ) ); + map.put( 2, new ImmutablePair<>( DataTypes.ARRAY_INT, feature ) ); + return map; + } + + + @Override + public JsonObject getRestRowExpression() { + return null; + } + + + @Override + public String getEntity() { + return "public.knn_intfeature"; + } + + + @Override + public String getSql() { + return SQL + getSqlRowExpression(); + } + + + @Override + public HttpRequest getRest() { + return null; + } + + + @Override + public String getMongoQl() { + return null; + } + + } + +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dml/InsertMetadata.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dml/InsertMetadata.java new file mode 100644 index 0000000..27856db --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dml/InsertMetadata.java @@ -0,0 +1,117 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dml; + +import com.google.gson.JsonObject; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.BatchableInsert; +import org.polypheny.simpleclient.query.QueryBuilder; + + +public class InsertMetadata extends QueryBuilder { + + private static final boolean EXPECT_RESULT = false; + + private static final AtomicInteger nextId = new AtomicInteger( 1 ); + + + @Override + public BatchableInsert getNewQuery() { + return new InsertMetadataQuery( nextId.getAndIncrement() ); + } + + + private static class InsertMetadataQuery extends BatchableInsert { + + private static final String SQL = "INSERT INTO knn_metadata (id, textdata) VALUES "; + + private final int id; + private final String textdata; + + + private InsertMetadataQuery( int id ) { + super( EXPECT_RESULT ); + this.id = id; + this.textdata = "textdata_" + id + "_blubber"; + } + + + @Override + public String getSqlRowExpression() { + return "(" + id + ", '" + textdata + "')"; + } + + + @Override + public String getParameterizedSqlQuery() { + return SQL + "(?, ?)"; + } + + + @Override + public Map> getParameterValues() { + Map> map = new HashMap<>(); + map.put( 1, new ImmutablePair<>( DataTypes.INTEGER, id ) ); + map.put( 2, new ImmutablePair<>( DataTypes.VARCHAR, textdata ) ); + return map; + } + + + @Override + public JsonObject getRestRowExpression() { + return null; + } + + + @Override + public String getEntity() { + return "public.knn_metadata"; + } + + + @Override + public String getSql() { + return SQL + getSqlRowExpression(); + } + + + @Override + public HttpRequest getRest() { + return null; + } + + + @Override + public String getMongoQl() { + return null; + } + + } + +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dml/InsertRealFeature.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dml/InsertRealFeature.java new file mode 100644 index 0000000..e0d12b9 --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dml/InsertRealFeature.java @@ -0,0 +1,147 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dml; + +import com.google.gson.JsonObject; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.atomic.AtomicInteger; +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.BatchableInsert; +import org.polypheny.simpleclient.query.QueryBuilder; + + +public class InsertRealFeature extends QueryBuilder { + + private static final boolean EXPECT_RESULT = false; + + private static final AtomicInteger nextId = new AtomicInteger( 1 ); + private final long randomSeed; + private final int dimension; + private static final String[] CATEGORIES = {"cat_A", "cat_B", "cat_C", "cat_D"}; + + private final Random random; + + + public InsertRealFeature( long randomSeed, int dimension ) { + this.randomSeed = randomSeed; + this.dimension = dimension; + + this.random = new Random( randomSeed ); + } + + + private Float[] getRandomVector() { + Float[] floats = new Float[this.dimension]; + for ( int i = 0; i < this.dimension; i++ ) { + floats[i] = random.nextInt( 100 ) / 100.0f; + } + + return floats; + } + + + @Override + public synchronized BatchableInsert getNewQuery() { + return new InsertRealFeatureQuery( + nextId.getAndIncrement(), + getRandomVector(), + CATEGORIES[random.nextInt(CATEGORIES.length)] + ); + } + + + private static class InsertRealFeatureQuery extends BatchableInsert { + + private static final String SQL = "INSERT INTO knn_realfeature (id, category, feature) VALUES "; + private final int id; + private final Float[] feature; + private String randomCategory; + + private InsertRealFeatureQuery( int id, Float[] feature, String randomCategory ) { + super( EXPECT_RESULT ); + this.id = id; + this.feature = feature; + this.randomCategory = randomCategory; + } + + + @Override + public String getSqlRowExpression() { + return "(" + id + ", '" + randomCategory + "', ARRAY" + Arrays.toString( feature ) + ")"; + } + + + @Override + public String getParameterizedSqlQuery() { + return SQL + "(?, ?, ?)"; + } + + + @Override + public Map> getParameterValues() { + Map> map = new HashMap<>(); + map.put( 1, new ImmutablePair<>( DataTypes.INTEGER, id ) ); + map.put( 2, new ImmutablePair<>( DataTypes.VARCHAR, randomCategory ) ); + map.put( 3, new ImmutablePair<>( DataTypes.ARRAY_REAL, feature ) ); + return map; + } + + + @Override + public JsonObject getRestRowExpression() { + return null; + } + + + @Override + public String getEntity() { + return "public.knn_realfeature"; + } + + + @Override + public String getSql() { + return SQL + getSqlRowExpression(); + } + + + @Override + public HttpRequest getRest() { + return null; + } + + + @Override + public String getMongoQl() { + return null; + } + + } + +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/MetadataKnnIntFeature.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/MetadataKnnIntFeature.java new file mode 100644 index 0000000..9feb180 --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/MetadataKnnIntFeature.java @@ -0,0 +1,133 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dql; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.Query; +import org.polypheny.simpleclient.query.QueryBuilder; + + +public class MetadataKnnIntFeature extends QueryBuilder { + + private static final boolean EXPECT_RESULT = true; + + private final long randomSeed; + private final int dimension; + private final int limit; + private final String norm; + + private final Random random; + + + public MetadataKnnIntFeature( long randomSeed, int dimension, int limit, String norm ) { + this.randomSeed = randomSeed; + this.dimension = dimension; + this.limit = limit; + this.norm = norm; + + this.random = new Random( randomSeed ); + } + + + private Integer[] getRandomVector() { + Integer[] integers = new Integer[this.dimension]; + for ( int i = 0; i < this.dimension; i++ ) { + integers[i] = random.nextInt( 500 ); + } + + return integers; + } + + + @Override + public synchronized Query getNewQuery() { + return new MetadataKnnIntFeatureQuery( + getRandomVector(), + limit, + norm + ); + } + + + private static class MetadataKnnIntFeatureQuery extends Query { + + private static final String SQL_1 = "SELECT knn_metadata.id, knn_metadata.textdata, closest.dist FROM knn_metadata, ( SELECT id, distance(feature, "; + private static final String SQL_2 = ", "; + private static final String SQL_3 = ") AS dist FROM knn_intfeature ORDER BY dist ASC LIMIT "; + private static final String SQL_4 = ") AS closest WHERE knn_metadata.id = closest.id ORDER BY closest.dist ASC"; + + private final Integer[] target; + private final int limit; + private final String norm; + + + private MetadataKnnIntFeatureQuery( Integer[] target, int limit, String norm ) { + super( EXPECT_RESULT ); + this.target = target; + this.limit = limit; + this.norm = norm; + } + + + @Override + public String getSql() { + return SQL_1 + "ARRAY" + Arrays.toString( target ) + SQL_2 + " '" + norm + "' " + SQL_3 + limit + SQL_4; + } + + + @Override + public String getParameterizedSqlQuery() { + return null; + //return SQL_1 + "?" + SQL_2 + "'" + norm + "'" + SQL_3 + limit + SQL_4; + } + + + @Override + public Map> getParameterValues() { + Map> map = new HashMap<>(); + map.put( 1, new ImmutablePair<>( DataTypes.ARRAY_INT, target ) ); + return map; + } + + + @Override + public HttpRequest getRest() { + return null; + } + + + @Override + public String getMongoQl() { + return null; + } + + } + +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/MetadataKnnRealCrossJoin.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/MetadataKnnRealCrossJoin.java new file mode 100644 index 0000000..a57cf7d --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/MetadataKnnRealCrossJoin.java @@ -0,0 +1,128 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dql; + +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.Query; +import org.polypheny.simpleclient.query.QueryBuilder; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; + +public class MetadataKnnRealCrossJoin extends QueryBuilder { + + private static final boolean EXPECT_RESULT = true; + + private final int dimension; + private final int limit; + private final String norm; + + private final Random random; + + + public MetadataKnnRealCrossJoin( long randomSeed, int dimension, int limit, String norm ) { + this.dimension = dimension; + this.limit = limit; + this.norm = norm; + + this.random = new Random( randomSeed ); + } + + + private Float[] getRandomVector() { + Float[] floats = new Float[this.dimension]; + for ( int i = 0; i < this.dimension; i++ ) { + floats[i] = random.nextInt( 100 ) / 100.0f; + } + + return floats; + } + + + @Override + public synchronized Query getNewQuery() { + return new MetadataKnnRealCrossJoin.MetadataKnnRealCrossJoinQuery( + getRandomVector(), + limit, + norm + ); + } + + + private static class MetadataKnnRealCrossJoinQuery extends Query { + + private static final String SQL_1 = "SELECT knn_metadata.id, knn_metadata.textdata, closest.dist FROM knn_metadata, ( SELECT t1.id, distance(t1.feature, t2.feature, '"; + private static final String SQL_2 = "') AS dist FROM knn_realfeature t1, knn_realfeature t2 WHERE t2.id = 1 ORDER BY dist ASC LIMIT "; + private static final String SQL_3 = ") AS closest WHERE knn_metadata.id = closest.id ORDER BY closest.dist ASC"; + private final Float[] target; + private final int limit; + private final String norm; + + + private MetadataKnnRealCrossJoinQuery( Float[] target, int limit, String norm ) { + super( EXPECT_RESULT ); + this.target = target; + this.limit = limit; + this.norm = norm; + } + + + @Override + public String getSql() { + return SQL_1 + norm + SQL_2 + limit + SQL_3; + } + + + @Override + public String getParameterizedSqlQuery() { + return null; + //return SQL_1 + "?" + SQL_2 + "'" + norm + "'" + SQL_3 + limit + SQL_4; + } + + + @Override + public Map> getParameterValues() { + Map> map = new HashMap<>(); + map.put( 1, new ImmutablePair<>( DataTypes.ARRAY_REAL, target ) ); + return map; + } + + + @Override + public HttpRequest getRest() { + return null; + } + + + @Override + public String getMongoQl() { + return null; + } + + } + +} + diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/MetadataKnnRealFeature.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/MetadataKnnRealFeature.java new file mode 100644 index 0000000..81ed28a --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/MetadataKnnRealFeature.java @@ -0,0 +1,133 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dql; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.Query; +import org.polypheny.simpleclient.query.QueryBuilder; + + +public class MetadataKnnRealFeature extends QueryBuilder { + + private static final boolean EXPECT_RESULT = true; + + private final long randomSeed; + private final int dimension; + private final int limit; + private final String norm; + + private final Random random; + + + public MetadataKnnRealFeature( long randomSeed, int dimension, int limit, String norm ) { + this.randomSeed = randomSeed; + this.dimension = dimension; + this.limit = limit; + this.norm = norm; + + this.random = new Random( randomSeed ); + } + + + private Float[] getRandomVector() { + Float[] floats = new Float[this.dimension]; + for ( int i = 0; i < this.dimension; i++ ) { + floats[i] = random.nextInt( 100 ) / 100.0f; + } + + return floats; + } + + + @Override + public synchronized Query getNewQuery() { + return new MetadataKnnRealFeatureQuery( + getRandomVector(), + limit, + norm + ); + } + + + private static class MetadataKnnRealFeatureQuery extends Query { + + private static final String SQL_1 = "SELECT knn_metadata.id, knn_metadata.textdata, closest.dist FROM knn_metadata, ( SELECT id, distance(feature, "; + private static final String SQL_2 = ", "; + private static final String SQL_3 = ") AS dist FROM knn_realfeature ORDER BY dist ASC LIMIT "; + private static final String SQL_4 = ") AS closest WHERE knn_metadata.id = closest.id ORDER BY closest.dist ASC"; + + private final Float[] target; + private final int limit; + private final String norm; + + + private MetadataKnnRealFeatureQuery( Float[] target, int limit, String norm ) { + super( EXPECT_RESULT ); + this.target = target; + this.limit = limit; + this.norm = norm; + } + + + @Override + public String getSql() { + return SQL_1 + "ARRAY" + Arrays.toString( target ) + SQL_2 + " '" + norm + "' " + SQL_3 + limit + SQL_4; + } + + + @Override + public String getParameterizedSqlQuery() { + return null; + //return SQL_1 + "?" + SQL_2 + "'" + norm + "'" + SQL_3 + limit + SQL_4; + } + + + @Override + public Map> getParameterValues() { + Map> map = new HashMap<>(); + map.put( 1, new ImmutablePair<>( DataTypes.ARRAY_REAL, target ) ); + return map; + } + + + @Override + public HttpRequest getRest() { + return null; + } + + + @Override + public String getMongoQl() { + return null; + } + + } + +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/SimpleKnnBooleanFeature.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/SimpleKnnBooleanFeature.java new file mode 100644 index 0000000..219e6e5 --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/SimpleKnnBooleanFeature.java @@ -0,0 +1,111 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dql; + +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.Query; +import org.polypheny.simpleclient.query.QueryBuilder; +import java.util.Arrays; +import java.util.Map; +import java.util.Random; + +public class SimpleKnnBooleanFeature extends QueryBuilder { + + private static final boolean EXPECT_RESULT = true; + private final int dimension; + private final int limit; + private final String norm; // Should be 'JACCARD' or 'HAMMING' + private final Random random; + + public SimpleKnnBooleanFeature( long randomSeed, int dimension, int limit, String norm ) { + this.dimension = dimension; + this.limit = limit; + this.norm = norm; + this.random = new Random( randomSeed ); + } + + private Boolean[] getRandomVector() { + Boolean[] booleans = new Boolean[this.dimension]; + for ( int i = 0; i < this.dimension; i++ ) { + booleans[i] = random.nextBoolean(); + } + return booleans; + } + + @Override + public synchronized Query getNewQuery() { + return new SimpleKnnBooleanFeatureQuery( getRandomVector(), limit, norm ); + } + + private static class SimpleKnnBooleanFeatureQuery extends Query { + + private final Boolean[] target; + private final int limit; + private final String norm; + + public SimpleKnnBooleanFeatureQuery( Boolean[] target, int limit, String norm ) { + super( EXPECT_RESULT ); + this.target = target; + this.limit = limit; + this.norm = norm; + } + + @Override + public String getSql() { + return "SELECT id, " + norm.toLowerCase() + "_distance(feature, ARRAY" + Arrays.toString( target ) + ") " + + "as dist " + + "FROM knn_booleanfeature " + // Ensure this tableexists in DataGenerator + "ORDER BY dist ASC LIMIT " + limit; + } + + + @Override + public String getParameterizedSqlQuery() { return null; } + + + @Override + public Map> + getParameterValues() { return null; } + + + @Override + public HttpRequest getRest() { return null; } + + + @Override + public String getMongoQl() { + return "db.knn_booleanfeature.aggregate([{" + + " \"$vectorSearch\": {" + + " \"path\": \"feature\"," + + " \"queryVector\": " + Arrays.toString( target ) + + "," + + " \"metric\": \"" + norm + "\"," + + " \"limit\": " + limit + + " }" + + "}])"; + } + } +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/SimpleKnnBooleanFeatureFiltered.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/SimpleKnnBooleanFeatureFiltered.java new file mode 100644 index 0000000..93f9bf7 --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/SimpleKnnBooleanFeatureFiltered.java @@ -0,0 +1,119 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dql; + +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.Query; +import org.polypheny.simpleclient.query.QueryBuilder; +import java.util.Arrays; +import java.util.Map; +import java.util.Random; + +public class SimpleKnnBooleanFeatureFiltered extends QueryBuilder { + + private static final boolean EXPECT_RESULT = true; + private final int dimension; + private final int limit; + private final String norm; + private final String filterCategory; + private final Random random; + + public SimpleKnnBooleanFeatureFiltered( long randomSeed, int dimension, int limit, String norm, String filterCategory ) { + this.dimension = dimension; + this.limit = limit; + this.norm = norm; + this.filterCategory = filterCategory; + this.random = new Random( randomSeed ); + } + + + private Boolean[] getRandomVector() { + Boolean[] booleans = new Boolean[this.dimension]; + for ( int i = 0; i < this.dimension; i++ ) { + booleans[i] = random.nextBoolean(); + } + return booleans; + } + + + @Override + public synchronized Query getNewQuery() { + return new SimpleKnnBooleanFeatureFilteredQuery( getRandomVector(), limit, norm, filterCategory ); + } + + + private static class SimpleKnnBooleanFeatureFilteredQuery extends Query { + + private final Boolean[] target; + private final int limit; + private final String norm; + private final String category; + + public SimpleKnnBooleanFeatureFilteredQuery( Boolean[] target, int limit, String norm, String category ) { + super( EXPECT_RESULT ); + this.target = target; + this.limit = limit; + this.norm = norm; + this.category = category; + } + + + @Override + public String getSql() { + return "SELECT id, " + norm.toLowerCase() + "_distance(feature, ARRAY" + Arrays.toString( target ) + ") " + + "as dist " + + "FROM knn_booleanfeature " + + "WHERE category = '" + category + "' " + + "ORDER BY dist ASC LIMIT " + limit; + } + + + @Override + public String getParameterizedSqlQuery() { return null; } + + + @Override + public Map> getParameterValues() { return null; } + + + @Override + public HttpRequest getRest() { return null; } + + + @Override + public String getMongoQl() { + return "db.knn_booleanfeature.aggregate([{" + + " \"$vectorSearch\": {" + + " \"path\": \"feature\"," + + " \"queryVector\": " + Arrays.toString( target ) + "," + + " \"metric\": \"" + norm + "\"," + + " \"limit\": " + limit + "," + + " \"filter\": { \"category\": \"" + category + "\" }" + + " }" + + "}])"; + } + } +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/SimpleKnnIdIntFeature.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/SimpleKnnIdIntFeature.java new file mode 100644 index 0000000..9749d78 --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/SimpleKnnIdIntFeature.java @@ -0,0 +1,133 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dql; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.Query; +import org.polypheny.simpleclient.query.QueryBuilder; + + +public class SimpleKnnIdIntFeature extends QueryBuilder { + + private static final boolean EXPECT_RESULT = true; + + private final long randomSeed; + private final int dimension; + private final int limit; + private final String norm; + + private final Random random; + + + public SimpleKnnIdIntFeature( long randomSeed, int dimension, int limit, String norm ) { + this.randomSeed = randomSeed; + this.dimension = dimension; + + this.random = new Random( randomSeed ); + this.limit = limit; + this.norm = norm; + } + + + private Integer[] getRandomVector() { + Integer[] integers = new Integer[this.dimension]; + for ( int i = 0; i < this.dimension; i++ ) { + integers[i] = random.nextInt( 500 ); + } + + return integers; + } + + + @Override + public synchronized Query getNewQuery() { + return new SimpleKnnIdIntFeatureQuery( + getRandomVector(), + limit, + norm + ); + } + + + private static class SimpleKnnIdIntFeatureQuery extends Query { + + private static final String SQL_1 = "SELECT closest.dist FROM ( SELECT id, distance(feature, "; + private static final String SQL_2 = ", "; + private static final String SQL_3 = ") AS dist FROM knn_intfeature ORDER BY dist ASC LIMIT "; + private static final String SQL_4 = ") AS closest"; + + private final Integer[] target; + private final int limit; + private final String norm; + + + public SimpleKnnIdIntFeatureQuery( Integer[] target, int limit, String norm ) { + super( EXPECT_RESULT ); + this.target = target; + this.limit = limit; + this.norm = norm; + } + + + @Override + public String getSql() { + return SQL_1 + "ARRAY" + Arrays.toString( target ) + SQL_2 + "'" + norm + "'" + SQL_3 + limit + SQL_4; + } + + + @Override + public String getParameterizedSqlQuery() { + return null; + //return SQL_1 + "?" + SQL_2 + "'" + norm + "'" + SQL_3 + limit + SQL_4; + } + + + @Override + public Map> getParameterValues() { + Map> map = new HashMap<>(); + map.put( 1, new ImmutablePair<>( DataTypes.ARRAY_INT, target ) ); + return map; + } + + + @Override + public HttpRequest getRest() { + return null; + } + + + @Override + public String getMongoQl() { + return null; + } + + } + +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/SimpleKnnIdRealFeature.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/SimpleKnnIdRealFeature.java new file mode 100644 index 0000000..26f1354 --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/SimpleKnnIdRealFeature.java @@ -0,0 +1,133 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dql; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.Query; +import org.polypheny.simpleclient.query.QueryBuilder; + + +public class SimpleKnnIdRealFeature extends QueryBuilder { + + private static final boolean EXPECT_RESULT = true; + + private final long randomSeed; + private final int dimension; + private final int limit; + private final String norm; + + private final Random random; + + + public SimpleKnnIdRealFeature( long randomSeed, int dimension, int limit, String norm ) { + this.randomSeed = randomSeed; + this.dimension = dimension; + + this.random = new Random( randomSeed ); + this.limit = limit; + this.norm = norm; + } + + + private Float[] getRandomVector() { + Float[] integers = new Float[this.dimension]; + for ( int i = 0; i < this.dimension; i++ ) { + integers[i] = random.nextInt( 100 ) / 100.0f; + } + + return integers; + } + + + @Override + public synchronized Query getNewQuery() { + return new SimpleKnnIdRealFeatureQuery( + getRandomVector(), + limit, + norm + ); + } + + + private static class SimpleKnnIdRealFeatureQuery extends Query { + + private static final String SQL_1 = "SELECT closest.dist FROM ( SELECT id, distance(feature, "; + private static final String SQL_2 = ", "; + private static final String SQL_3 = ") AS dist FROM knn_realfeature ORDER BY dist ASC LIMIT "; + private static final String SQL_4 = ") AS closest"; + + private final Float[] target; + private final int limit; + private final String norm; + + + public SimpleKnnIdRealFeatureQuery( Float[] target, int limit, String norm ) { + super( EXPECT_RESULT ); + this.target = target; + this.limit = limit; + this.norm = norm; + } + + + @Override + public String getSql() { + return SQL_1 + "ARRAY" + Arrays.toString( target ) + SQL_2 + "'" + norm + "'" + SQL_3 + limit + SQL_4; + } + + + @Override + public String getParameterizedSqlQuery() { + return null; + //return SQL_1 + "?" + SQL_2 + "'" + norm + "'" + SQL_3 + limit + SQL_4; + } + + + @Override + public Map> getParameterValues() { + Map> map = new HashMap<>(); + map.put( 1, new ImmutablePair<>( DataTypes.ARRAY_REAL, target ) ); + return map; + } + + + @Override + public HttpRequest getRest() { + return null; + } + + + @Override + public String getMongoQl() { + return null; + } + + } + +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/SimpleKnnIntFeature.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/SimpleKnnIntFeature.java new file mode 100644 index 0000000..26448b0 --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/SimpleKnnIntFeature.java @@ -0,0 +1,132 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dql; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.Query; +import org.polypheny.simpleclient.query.QueryBuilder; + + +public class SimpleKnnIntFeature extends QueryBuilder { + + private static final boolean EXPECT_RESULT = true; + + private final long randomSeed; + private final int dimension; + private final int limit; + private final String norm; + + private final Random random; + + + public SimpleKnnIntFeature( long randomSeed, int dimension, int limit, String norm ) { + this.randomSeed = randomSeed; + this.dimension = dimension; + + this.random = new Random( randomSeed ); + this.limit = limit; + this.norm = norm; + } + + + private Integer[] getRandomVector() { + Integer[] integers = new Integer[this.dimension]; + for ( int i = 0; i < this.dimension; i++ ) { + integers[i] = random.nextInt( 500 ); + } + + return integers; + } + + + @Override + public synchronized Query getNewQuery() { + return new SimpleKnnIntFeatureQuery( + getRandomVector(), + limit, + norm + ); + } + + + private static class SimpleKnnIntFeatureQuery extends Query { + + private static final String SQL_1 = "SELECT id, distance(feature, "; + private static final String SQL_2 = ", "; + private static final String SQL_3 = ") as dist FROM knn_intfeature ORDER BY dist ASC LIMIT "; + + private final Integer[] target; + private final int limit; + private final String norm; + + + public SimpleKnnIntFeatureQuery( Integer[] target, int limit, String norm ) { + super( EXPECT_RESULT ); + this.target = target; + this.limit = limit; + this.norm = norm; + } + + + @Override + public String getSql() { + return SQL_1 + "ARRAY" + Arrays.toString( target ) + SQL_2 + " '" + norm + "' " + SQL_3 + limit; + } + + + @Override + public String getParameterizedSqlQuery() { + return null; + //return SQL_1 + "?" + SQL_2 + "'" + norm + "'" + SQL_3 + limit; + } + + + @Override + public Map> getParameterValues() { + Map> map = new HashMap<>(); + map.put( 1, new ImmutablePair<>( DataTypes.ARRAY_INT, target ) ); + return map; + } + + + @Override + public HttpRequest getRest() { + return null; + } + + + @Override + public String getMongoQl() { + return null; + } + + } + +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/SimpleKnnRealCrossJoin.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/SimpleKnnRealCrossJoin.java new file mode 100644 index 0000000..40e8f2e --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/SimpleKnnRealCrossJoin.java @@ -0,0 +1,121 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dql; + +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.Query; +import org.polypheny.simpleclient.query.QueryBuilder; +import java.util.Map; +import java.util.Random; + +public class SimpleKnnRealCrossJoin extends QueryBuilder { + + private static final boolean EXPECT_RESULT = true; + + private final int dimension; + private final int limit; + private final String norm; + + private final Random random; + + + public SimpleKnnRealCrossJoin( long randomSeed, int dimension, int limit, String norm ) { + this.dimension = dimension; + this.limit = limit; + this.norm = norm; + + this.random = new Random( randomSeed ); + } + + + private Float[] getRandomVector() { + Float[] floats = new Float[this.dimension]; + for ( int i = 0; i < this.dimension; i++ ) { + floats[i] = random.nextInt( 100 ) / 100.0f; + } + + return floats; + } + + + @Override + public synchronized Query getNewQuery() { + return new SimpleKnnRealCrossJoin.SimpleKnnRealCrossJoinQuery( + getRandomVector(), + limit, + norm ); + } + + + private static class SimpleKnnRealCrossJoinQuery extends Query { + + private static final String SQL_1 = "SELECT t1.id, distance(t1.feature, t2.feature,"; + private static final String SQL_2 = ") as dist FROM knn_realfeature t1, knn_realfeature t2 WHERE t2.id = 1 ORDER BY dist ASC LIMIT "; + + private final Float[] target; + private final int limit; + private final String norm; + + + public SimpleKnnRealCrossJoinQuery( Float[] target, int limit, String norm ) { + super( EXPECT_RESULT ); + this.target = target; + this.limit = limit; + this.norm = norm; + } + + + @Override + public String getSql() { + return SQL_1 + " '" + norm + "' " + SQL_2 + limit; + } + + + @Override + public String getParameterizedSqlQuery() { + return ""; + } + + + @Override + public Map> getParameterValues() { + return Map.of(); + } + + + @Override + public HttpRequest getRest() { + return null; + } + + + @Override + public String getMongoQl() { + return null; + } + + } +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/SimpleKnnRealFeature.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/SimpleKnnRealFeature.java new file mode 100644 index 0000000..3edc323 --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/SimpleKnnRealFeature.java @@ -0,0 +1,132 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dql; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.Query; +import org.polypheny.simpleclient.query.QueryBuilder; + + + +public class SimpleKnnRealFeature extends QueryBuilder { + + private static final boolean EXPECT_RESULT = true; + + private final long randomSeed; + private final int dimension; + private final int limit; + private final String norm; + + private final Random random; + + + public SimpleKnnRealFeature( long randomSeed, int dimension, int limit, String norm ) { + this.randomSeed = randomSeed; + this.dimension = dimension; + + this.random = new Random( randomSeed ); + this.limit = limit; + this.norm = norm; + } + + + private Float[] getRandomVector() { + Float[] integers = new Float[this.dimension]; + for ( int i = 0; i < this.dimension; i++ ) { + integers[i] = random.nextInt( 100 ) / 100.0f; + } + + return integers; + } + + + @Override + public synchronized Query getNewQuery() { + return new SimpleKnnRealFeatureQuery( + getRandomVector(), + limit, + norm + ); + } + + + private static class SimpleKnnRealFeatureQuery extends Query { + + private static final String SQL_1 = "SELECT id, distance(feature, "; + private static final String SQL_2 = ", "; + private static final String SQL_3 = ") as dist FROM knn_realfeature ORDER BY dist ASC LIMIT "; + + private final Float[] target; + private final int limit; + private final String norm; + + + public SimpleKnnRealFeatureQuery( Float[] target, int limit, String norm ) { + super( EXPECT_RESULT ); + this.target = target; + this.limit = limit; + this.norm = norm; + } + + + @Override + public String getSql() { + return SQL_1 + "ARRAY" + Arrays.toString( target ) + SQL_2 + " '" + norm + "' " + SQL_3 + limit; + } + + + @Override + public String getParameterizedSqlQuery() { + return null; + //return SQL_1 + "?" + SQL_2 + "'" + norm + "'" + SQL_3 + limit; + } + + + @Override + public Map> getParameterValues() { + Map> map = new HashMap<>(); + map.put( 1, new ImmutablePair<>( DataTypes.ARRAY_REAL, target ) ); + return map; + } + + + @Override + public HttpRequest getRest() { + return null; + } + + + @Override + public String getMongoQl() { + return null; + } + + } +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/SimpleKnnRealFeatureFiltered.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/SimpleKnnRealFeatureFiltered.java new file mode 100644 index 0000000..fb93c7f --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/SimpleKnnRealFeatureFiltered.java @@ -0,0 +1,120 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dql; + +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.Query; +import org.polypheny.simpleclient.query.QueryBuilder; +import java.util.Arrays; +import java.util.Map; +import java.util.Random; + +public class SimpleKnnRealFeatureFiltered extends QueryBuilder { + + private static final boolean EXPECT_RESULT = true; + private final int dimension; + private final int limit; + private final String norm; + private final Random random; + private final String filterCategory; + + public SimpleKnnRealFeatureFiltered( long randomSeed, int dimension, int limit, String norm, String filterCategory ) { + this.dimension = dimension; + this.limit = limit; + this.norm = norm; + this.filterCategory = filterCategory; + this.random = new Random( randomSeed ); + } + + private Float[] getRandomVector() { + Float[] floats = new Float[this.dimension]; + for ( int i = 0; i < this.dimension; i++ ) { + floats[i] = random.nextInt( 100 ) / 100.0f; + } + return floats; + } + + @Override + public synchronized Query getNewQuery() { + return new SimpleKnnRealFeatureFilteredQuery( getRandomVector(), limit, norm, filterCategory ); + } + + private static class SimpleKnnRealFeatureFilteredQuery extends Query { + + private final Float[] target; + private final int limit; + private final String norm; + private final String category; + + public SimpleKnnRealFeatureFilteredQuery( Float[] target, int limit, String norm, String category ) { + super( EXPECT_RESULT ); + this.target = target; + this.limit = limit; + this.norm = norm; + this.category = category; + } + + @Override + public String getSql() { + return "SELECT id, distance(feature, ARRAY" + Arrays.toString( + target ) + ", '" + norm + "') as dist " + + "FROM knn_realfeature " + + "WHERE category = '" + category + "' " + + "ORDER BY dist ASC LIMIT " + limit; + } + + @Override + public String getParameterizedSqlQuery() { return null; } + + @Override + public Map> getParameterValues() { + return Map.of(); + } + + + @Override + public HttpRequest getRest() { + return null; + } + + + // Note: This query will currently not enable pushdown, therefore runtime is expected to be high. + @Override + public String getMongoQl() { + return "db.knn_realfeature.aggregate([{" + + " \"$vectorSearch\": {" + + " \"path\": \"feature\"," + + " \"queryVector\": " + Arrays.toString( target ) + + "," + + " \"metric\": \"" + norm + "\"," + + " \"limit\": " + limit + "," + + " \"filter\": { \"category\": \"" + category + + "\" }" + + " }" + + "}])"; + } + } +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/SimpleMetadata.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/SimpleMetadata.java new file mode 100644 index 0000000..d02a5e2 --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/dql/SimpleMetadata.java @@ -0,0 +1,117 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.dql; + +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.Query; +import org.polypheny.simpleclient.query.QueryBuilder; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; + + +public class SimpleMetadata extends QueryBuilder { + + private static final boolean EXPECT_RESULT = true; + + private final long randomSeed; + private final int numOfEntries; + + private final Random random; + + + public SimpleMetadata( long randomSeed, int numOfEntries ) { + this.randomSeed = randomSeed; + this.numOfEntries = numOfEntries; + + this.random = new Random( randomSeed ); + } + + + private int getRandomId() { + return this.random.nextInt( this.numOfEntries ); + } + + + @Override + public Query getNewQuery() { + return new SimpleMetadataQuery( this.getRandomId() ); + } + + + private static class SimpleMetadataQuery extends Query { + + private static final String SQL = "SELECT id, textdata FROM knn_metadata WHERE id = "; + + private final int id; + + + private SimpleMetadataQuery( int id ) { + super( EXPECT_RESULT ); + this.id = id; + } + + + @Override + public String getSql() { + return SQL + id; + } + + + @Override + public String getParameterizedSqlQuery() { + return SQL + "?"; + } + + + @Override + public Map> getParameterValues() { + Map> map = new HashMap<>(); + map.put( 1, new ImmutablePair<>( DataTypes.INTEGER, id ) ); + return map; + } + + + @Override + public HttpRequest getRest() { + throw new UnsupportedOperationException( "kNN benchmarking is not supported for the REST interface." ); + } + + + @Override + public String getMongoQl() { + throw new UnsupportedOperationException( "kNN benchmarking is not supported for the MongoQl interface." ); + } + + + @Override + public String getCypher() { + throw new UnsupportedOperationException( "kNN benchmarking is not supported for the Cypher interface." ); + } + + } + +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/ddl/PgCreateBooleanFeature.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/ddl/PgCreateBooleanFeature.java new file mode 100644 index 0000000..a3c8d0f --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/ddl/PgCreateBooleanFeature.java @@ -0,0 +1,94 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.postgres.ddl; + +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.Query; +import org.polypheny.simpleclient.query.QueryBuilder; +import java.util.Map; + +public class PgCreateBooleanFeature extends QueryBuilder { + private final int dimension; + + + public PgCreateBooleanFeature( int dimension ) { + this.dimension = dimension; + } + + + @Override + public Query getNewQuery() { + return new PgCreateBooleanFeatureQuery( dimension ); + } + + + private static class PgCreateBooleanFeatureQuery extends Query { + + private final int dimension; + + + PgCreateBooleanFeatureQuery( int dimension ) { + super( false ); + this.dimension = dimension; + } + + + @Override + public String getSql() { + return "CREATE TABLE knn_booleanfeature (" + + "id INTEGER NOT NULL, " + + "category VARCHAR(50), " + + "feature bit(" + dimension + ") NOT NULL, " + + "PRIMARY KEY (id))"; + } + + + @Override + public String getParameterizedSqlQuery() { + return null; + } + + + @Override + public Map> getParameterValues() { + return null; + } + + + @Override + public HttpRequest getRest() { + return null; + } + + + @Override + public String getMongoQl() { + return null; + } + + } + +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/ddl/PgCreateBooleanFeatureIndex.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/ddl/PgCreateBooleanFeatureIndex.java new file mode 100644 index 0000000..ff3563d --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/ddl/PgCreateBooleanFeatureIndex.java @@ -0,0 +1,123 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.postgres.ddl; + +import java.util.Map; +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.Query; +import org.polypheny.simpleclient.query.QueryBuilder; + + +public class PgCreateBooleanFeatureIndex extends QueryBuilder { + + private final String method; + private final String opClass; + private final int m; + private final int efConstruction; + private final int lists; + + + public PgCreateBooleanFeatureIndex( String method, String metric, int m, int efConstruction, int lists ) { + this.method = method; + this.opClass = toOpClass( metric ); + this.m = m; + this.efConstruction = efConstruction; + this.lists = lists; + } + + + private static String toOpClass( String metric ) { + return switch ( metric.toLowerCase() ) { + case "hamming" -> "bit_hamming_ops"; + case "jaccard" -> "bit_jaccard_ops"; + default -> throw new IllegalArgumentException( "Provided boolean metric is invalid: " + metric ); + }; + } + + + @Override + public Query getNewQuery() { + return new PgCreateBooleanFeatureIndexQuery( method, opClass, m, efConstruction, lists ); + } + + + private static class PgCreateBooleanFeatureIndexQuery extends Query { + + private final String method; + private final String opClass; + private final int m; + private final int efConstruction; + private final int lists; + private final boolean isHnsw; + + + PgCreateBooleanFeatureIndexQuery( String method, String opClass, int m, int efConstruction, int lists ) { + super( false ); + this.method = method; + this.opClass = opClass; + this.m = m; + this.efConstruction = efConstruction; + this.lists = lists; + this.isHnsw = method.equals( "hnsw" ); + } + + + @Override + public String getSql() { + String withClause = isHnsw + ? "(m=" + m + ", ef_construction=" + efConstruction + ")" + : "(lists=" + lists + ")"; + return "CREATE INDEX ON knn_booleanfeature USING " + method + + " (feature " + opClass + ") WITH " + withClause; + } + + + @Override + public String getParameterizedSqlQuery() { + return null; + } + + + @Override + public Map> getParameterValues() { + return null; + } + + + @Override + public HttpRequest getRest() { + return null; + } + + + @Override + public String getMongoQl() { + return null; + } + + } + +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/ddl/PgCreateRealFeature.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/ddl/PgCreateRealFeature.java new file mode 100644 index 0000000..e3b6266 --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/ddl/PgCreateRealFeature.java @@ -0,0 +1,96 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.postgres.ddl; + +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.Query; +import org.polypheny.simpleclient.query.QueryBuilder; +import java.util.Map; + + +public class PgCreateRealFeature extends QueryBuilder { + + private final int dimension; + + + public PgCreateRealFeature( int dimension ) { + this.dimension = dimension; + } + + + @Override + public Query getNewQuery() { + return new PgCreateRealFeatureQuery( dimension ); + } + + + private static class PgCreateRealFeatureQuery extends Query { + + private final int dimension; + + + PgCreateRealFeatureQuery( int dimension ) { + super( false ); + this.dimension = dimension; + } + + + @Override + public String getSql() { + return "CREATE TABLE knn_realfeature (" + + "id INTEGER NOT NULL, " + + "category VARCHAR(50), " + + "feature vector(" + dimension + ") NOT NULL, " + + "PRIMARY KEY (id))"; + } + + + @Override + public String getParameterizedSqlQuery() { + return null; + } + + + @Override + public Map> getParameterValues() { + return null; + } + + + @Override + public HttpRequest getRest() { + return null; + } + + + @Override + public String getMongoQl() { + return null; + } + + } + +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/ddl/PgCreateRealFeatureIndex.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/ddl/PgCreateRealFeatureIndex.java new file mode 100644 index 0000000..25b7585 --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/ddl/PgCreateRealFeatureIndex.java @@ -0,0 +1,125 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.postgres.ddl; + +import java.util.Map; +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.Query; +import org.polypheny.simpleclient.query.QueryBuilder; + + +public class PgCreateRealFeatureIndex extends QueryBuilder { + + private final String method; + private final String opClass; + private final int m; + private final int efConstruction; + private final int lists; + + + public PgCreateRealFeatureIndex( String method, String metric, int m, int efConstruction, int lists ) { + this.method = method; + this.opClass = toOpClass( metric ); + this.m = m; + this.efConstruction = efConstruction; + this.lists = lists; + } + + + private static String toOpClass( String metric ) { + return switch ( metric.toLowerCase() ) { + case "cosine" -> "vector_cosine_ops"; + case "l2" -> "vector_l2_ops"; + case "l1" -> "vector_l1_ops"; + case "inner_product" -> "vector_ip_ops"; + default -> throw new IllegalArgumentException( "Provided metric is invalid: " + metric ); + }; + } + + + @Override + public Query getNewQuery() { + return new PgCreateRealFeatureIndexQuery( method, opClass, m, efConstruction, lists ); + } + + + private static class PgCreateRealFeatureIndexQuery extends Query { + + private final String method; + private final String opClass; + private final int m; + private final int efConstruction; + private final int lists; + private final boolean isHnsw; + + + PgCreateRealFeatureIndexQuery( String method, String opClass, int m, int efConstruction, int lists ) { + super( false ); + this.method = method; + this.opClass = opClass; + this.m = m; + this.efConstruction = efConstruction; + this.lists = lists; + this.isHnsw = method.equals( "hnsw" ); + } + + + @Override + public String getSql() { + String withClause = isHnsw + ? "(m=" + m + ", ef_construction=" + efConstruction + ")" + : "(lists=" + lists + ")"; + return "CREATE INDEX ON knn_realfeature USING " + method + + " (feature " + opClass + ") WITH " + withClause; + } + + + @Override + public String getParameterizedSqlQuery() { + return null; + } + + + @Override + public Map> getParameterValues() { + return null; + } + + + @Override + public HttpRequest getRest() { + return null; + } + + + @Override + public String getMongoQl() { + return null; + } + + } + +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/dml/PgInsertBooleanFeature.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/dml/PgInsertBooleanFeature.java new file mode 100644 index 0000000..cdf5e3d --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/dml/PgInsertBooleanFeature.java @@ -0,0 +1,138 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.postgres.dml; + +import com.google.gson.JsonObject; +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.BatchableInsert; +import org.polypheny.simpleclient.query.QueryBuilder; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.atomic.AtomicInteger; + +public class PgInsertBooleanFeature extends QueryBuilder { + + private static final boolean EXPECT_RESULT = false; + private static final AtomicInteger nextId = new AtomicInteger( 1 ); + private static final String[] CATEGORIES = { "cat_A", "cat_B", "cat_C", "cat_D" }; + + private final int dimension; + private final Random random; + + + public PgInsertBooleanFeature( long randomSeed, int dimension ) { + this.dimension = dimension; + this.random = new Random( randomSeed ); + } + + + private String getRandomBits() { + StringBuilder sb = new StringBuilder( dimension ); + for ( int i = 0; i < dimension; i++ ) { + sb.append( random.nextBoolean() ? '1' : '0' ); + } + return sb.toString(); + } + + + @Override + public synchronized BatchableInsert getNewQuery() { + return new PgInsertBooleanFeatureQuery( + nextId.getAndIncrement(), + getRandomBits(), + CATEGORIES[random.nextInt( CATEGORIES.length )] + ); + } + + + private static class PgInsertBooleanFeatureQuery extends BatchableInsert { + + private static final String SQL = "INSERT INTO knn_booleanfeature (id, category, feature) VALUES "; + private final int id; + private final String feature; + private final String category; + + + PgInsertBooleanFeatureQuery( int id, String feature, String category ) { + super( EXPECT_RESULT ); + this.id = id; + this.feature = feature; + this.category = category; + } + + + @Override + public String getSqlRowExpression() { + StringBuilder sb = new StringBuilder( "(" ); + sb.append( id ).append( ", '" ).append( category ).append( "', B'" ).append( feature ).append( "')" ); + return sb.toString(); + } + + + @Override + public String getSql() { + return SQL + getSqlRowExpression(); + } + + + @Override + public String getParameterizedSqlQuery() { + return null; + } + + + @Override + public Map> getParameterValues() { + return null; + } + + + @Override + public JsonObject getRestRowExpression() { + return null; + } + + + @Override + public String getEntity() { + return "public.knn_booleanfeature"; + } + + + @Override + public HttpRequest getRest() { + return null; + } + + + @Override + public String getMongoQl() { + return null; + } + + } + +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/dml/PgInsertRealFeature.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/dml/PgInsertRealFeature.java new file mode 100644 index 0000000..3e652e5 --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/dml/PgInsertRealFeature.java @@ -0,0 +1,145 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.postgres.dml; + +import com.google.gson.JsonObject; +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.BatchableInsert; +import org.polypheny.simpleclient.query.QueryBuilder; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.atomic.AtomicInteger; + + +public class PgInsertRealFeature extends QueryBuilder { + + private static final boolean EXPECT_RESULT = false; + private static final AtomicInteger nextId = new AtomicInteger( 1 ); + private static final String[] CATEGORIES = { "cat_A", "cat_B", "cat_C", "cat_D" }; + + private final int dimension; + private final Random random; + + + public PgInsertRealFeature( long randomSeed, int dimension ) { + this.dimension = dimension; + this.random = new Random( randomSeed ); + } + + + private Float[] getRandomVector() { + Float[] floats = new Float[dimension]; + for ( int i = 0; i < dimension; i++ ) { + floats[i] = random.nextInt( 100 ) / 100.0f; + } + return floats; + } + + + @Override + public synchronized BatchableInsert getNewQuery() { + return new PgInsertRealFeatureQuery( + nextId.getAndIncrement(), + getRandomVector(), + CATEGORIES[random.nextInt( CATEGORIES.length )] + ); + } + + + private static class PgInsertRealFeatureQuery extends BatchableInsert { + + private static final String SQL = "INSERT INTO knn_realfeature (id, category, feature) VALUES "; + private final int id; + private final Float[] feature; + private final String category; + + + PgInsertRealFeatureQuery( int id, Float[] feature, String category ) { + super( EXPECT_RESULT ); + this.id = id; + this.feature = feature; + this.category = category; + } + + + @Override + public String getSqlRowExpression() { + StringBuilder sb = new StringBuilder( "(" ); + sb.append( id ).append( ", '" ).append( category ).append( "', '[" ); + for ( int i = 0; i < feature.length; i++ ) { + if ( i > 0 ) + sb.append( "," ); + sb.append( feature[i] ); + } + sb.append( "]')" ); + return sb.toString(); + } + + + @Override + public String getSql() { + return SQL + getSqlRowExpression(); + } + + + @Override + public String getParameterizedSqlQuery() { + return null; + } + + + @Override + public Map> getParameterValues() { + return null; + } + + + @Override + public JsonObject getRestRowExpression() { + return null; + } + + + @Override + public String getEntity() { + return "public.knn_realfeature"; + } + + + @Override + public HttpRequest getRest() { + return null; + } + + + @Override + public String getMongoQl() { + return null; + } + + } + +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/dql/PgSimpleKnnBooleanFeature.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/dql/PgSimpleKnnBooleanFeature.java new file mode 100644 index 0000000..007407d --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/dql/PgSimpleKnnBooleanFeature.java @@ -0,0 +1,116 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.postgres.dql; + +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.Query; +import org.polypheny.simpleclient.query.QueryBuilder; +import java.util.Map; +import java.util.Random; + + +public class PgSimpleKnnBooleanFeature extends QueryBuilder { + + private static final boolean EXPECT_RESULT = true; + + private final int dimension; + private final int limit; + private final String operator; + private final Random random; + + + public PgSimpleKnnBooleanFeature( long randomSeed, int dimension, int limit, String booleanDistanceMetric ) { + this.dimension = dimension; + this.limit = limit; + this.random = new Random( randomSeed ); + this.operator = toOperator( booleanDistanceMetric ); + } + + + private static String toOperator( String metric ) { + return switch ( metric.toLowerCase() ) { + case "hamming" -> "<~>"; + case "jaccard" -> "<%>"; + default -> throw new IllegalArgumentException( "Provided boolean metric is invalid: " + metric ); + }; + } + + + private String getRandomBits() { + StringBuilder sb = new StringBuilder( dimension ); + for ( int i = 0; i < dimension; i++ ) { + sb.append( random.nextBoolean() ? '1' : '0' ); + } + return sb.toString(); + } + + + @Override + public synchronized Query getNewQuery() { + return new PgSimpleKnnBooleanFeatureQuery( getRandomBits(), dimension, limit, operator ); + } + + + private static class PgSimpleKnnBooleanFeatureQuery extends Query { + + private final String bits; + private final int dimension; + private final int limit; + private final String operator; + + + PgSimpleKnnBooleanFeatureQuery( String bits, int dimension, int limit, String operator ) { + super( EXPECT_RESULT ); + this.bits = bits; + this.dimension = dimension; + this.limit = limit; + this.operator = operator; + } + + + @Override + public String getSql() { + return "SELECT id, feature " + operator + " '" + bits + "'::bit(" + dimension + ") AS dist " + + "FROM knn_booleanfeature ORDER BY dist ASC LIMIT " + limit; + } + + + @Override + public String getParameterizedSqlQuery() { return null; } + + + @Override + public Map> getParameterValues() { return null; } + + + @Override + public HttpRequest getRest() { return null; } + + + @Override + public String getMongoQl() { return null; } + } +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/dql/PgSimpleKnnBooleanFeatureFiltered.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/dql/PgSimpleKnnBooleanFeatureFiltered.java new file mode 100644 index 0000000..abcc864 --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/dql/PgSimpleKnnBooleanFeatureFiltered.java @@ -0,0 +1,118 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.postgres.dql; + +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.Query; +import org.polypheny.simpleclient.query.QueryBuilder; +import java.util.Map; +import java.util.Random; + +public class PgSimpleKnnBooleanFeatureFiltered extends QueryBuilder { + private static final boolean EXPECT_RESULT = true; + + private final int dimension; + private final int limit; + private final String operator; + private final String filterCategory; + private final Random random; + + + public PgSimpleKnnBooleanFeatureFiltered( long randomSeed, int dimension, int limit, String booleanDistanceMetric, String filterCategory ) { + this.dimension = dimension; + this.limit = limit; + this.filterCategory = filterCategory; + this.random = new Random( randomSeed ); + this.operator = toOperator( booleanDistanceMetric ); + } + + + private static String toOperator( String metric ) { + return switch ( metric.toLowerCase() ) { + case "hamming" -> "<~>"; + case "jaccard" -> "<%>"; + default -> throw new IllegalArgumentException( "Provided boolean metric is invalid: " + metric ); + }; + } + + + private String getRandomBits() { + StringBuilder sb = new StringBuilder( dimension ); + for ( int i = 0; i < dimension; i++ ) { + sb.append( random.nextBoolean() ? '1' : '0' ); + } + return sb.toString(); + } + + + @Override + public synchronized Query getNewQuery() { + return new PgSimpleKnnBooleanFeatureFilteredQuery( getRandomBits(), dimension, limit, operator, filterCategory ); + } + + + private static class PgSimpleKnnBooleanFeatureFilteredQuery extends Query { + + private final String bits; + private final int dimension; + private final int limit; + private final String operator; + private final String category; + + + PgSimpleKnnBooleanFeatureFilteredQuery( String bits, int dimension, int limit, String operator, String category ) { + super( EXPECT_RESULT ); + this.bits = bits; + this.dimension = dimension; + this.limit = limit; + this.operator = operator; + this.category = category; + } + + + @Override + public String getSql() { + return "SELECT id, feature " + operator + " '" + bits + "'::bit(" + dimension + ") AS dist " + + "FROM knn_booleanfeature WHERE category = '" + category + "' ORDER BY dist ASC LIMIT " + limit; + } + + + @Override + public String getParameterizedSqlQuery() { return null; } + + + @Override + public Map> getParameterValues() { return null; } + + + @Override + public HttpRequest getRest() { return null; } + + + @Override + public String getMongoQl() { return null; } + } +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/dql/PgSimpleKnnIdRealFeature.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/dql/PgSimpleKnnIdRealFeature.java new file mode 100644 index 0000000..ebbbbe9 --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/dql/PgSimpleKnnIdRealFeature.java @@ -0,0 +1,122 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.postgres.dql; + +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.Query; +import org.polypheny.simpleclient.query.QueryBuilder; +import java.util.Map; +import java.util.Random; + + +public class PgSimpleKnnIdRealFeature extends QueryBuilder { + + private static final boolean EXPECT_RESULT = true; + + private final int dimension; + private final int limit; + private final String operator; + private final Random random; + + + public PgSimpleKnnIdRealFeature( long randomSeed, int dimension, int limit, String distanceMetric ) { + this.dimension = dimension; + this.limit = limit; + this.random = new Random( randomSeed ); + this.operator = toOperator( distanceMetric ); + } + + + private static String toOperator( String metric ) { + return switch ( metric.toLowerCase() ) { + case "cosine" -> "<=>"; + case "l2" -> "<->"; + case "l1" -> "<+>"; + case "inner_product" -> "<#>"; + default -> throw new IllegalArgumentException( "Provided metric is invalid: " + metric ); + }; + } + + + private Float[] getRandomVector() { + Float[] floats = new Float[dimension]; + for ( int i = 0; i < dimension; i++ ) { + floats[i] = random.nextInt( 100 ) / 100.0f; + } + return floats; + } + + + @Override + public synchronized Query getNewQuery() { + return new PgSimpleKnnIdRealFeatureQuery( getRandomVector(), limit, operator ); + } + + + private static class PgSimpleKnnIdRealFeatureQuery extends Query { + + private final Float[] target; + private final int limit; + private final String operator; + + + PgSimpleKnnIdRealFeatureQuery( Float[] target, int limit, String operator ) { + super( EXPECT_RESULT ); + this.target = target; + this.limit = limit; + this.operator = operator; + } + + + @Override + public String getSql() { + StringBuilder sb = new StringBuilder( "SELECT closest.dist FROM (SELECT id, feature " ); + sb.append( operator ).append( " '[" ); + for ( int i = 0; i < target.length; i++ ) { + if ( i > 0 ) sb.append( "," ); + sb.append( target[i] ); + } + sb.append( "]' AS dist FROM knn_realfeature ORDER BY dist ASC LIMIT " ).append( limit ).append( ") AS closest" ); + return sb.toString(); + } + + + @Override + public String getParameterizedSqlQuery() { return null; } + + + @Override + public Map> getParameterValues() { return null; } + + + @Override + public HttpRequest getRest() { return null; } + + + @Override + public String getMongoQl() { return null; } + } +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/dql/PgSimpleKnnRealCrossJoin.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/dql/PgSimpleKnnRealCrossJoin.java new file mode 100644 index 0000000..6bfb4b3 --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/dql/PgSimpleKnnRealCrossJoin.java @@ -0,0 +1,104 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.postgres.dql; + +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.Query; +import org.polypheny.simpleclient.query.QueryBuilder; +import java.util.Map; + + +public class PgSimpleKnnRealCrossJoin extends QueryBuilder { + + private static final boolean EXPECT_RESULT = true; + + private final int limit; + private final String operator; + + + // randomSeed and dimension are accepted for signature parity with the Polypheny-path + // SimpleKnnRealCrossJoin; the cross-join compares against the feature of row id = 1, + // so no random query vector is needed. + public PgSimpleKnnRealCrossJoin( long randomSeed, int dimension, int limit, String distanceMetric ) { + this.limit = limit; + this.operator = toOperator( distanceMetric ); + } + + + private static String toOperator( String metric ) { + return switch ( metric.toLowerCase() ) { + case "cosine" -> "<=>"; + case "l2" -> "<->"; + case "l1" -> "<+>"; + case "inner_product" -> "<#>"; + default -> throw new IllegalArgumentException( "Provided metric is invalid: " + metric ); + }; + } + + + @Override + public synchronized Query getNewQuery() { + return new PgSimpleKnnRealCrossJoinQuery( limit, operator ); + } + + + private static class PgSimpleKnnRealCrossJoinQuery extends Query { + + private final int limit; + private final String operator; + + + PgSimpleKnnRealCrossJoinQuery( int limit, String operator ) { + super( EXPECT_RESULT ); + this.limit = limit; + this.operator = operator; + } + + + @Override + public String getSql() { + return "SELECT t1.id, t1.feature " + operator + " t2.feature AS dist " + + "FROM knn_realfeature t1, knn_realfeature t2 WHERE t2.id = 1 " + + "ORDER BY dist ASC LIMIT " + limit; + } + + + @Override + public String getParameterizedSqlQuery() { return null; } + + + @Override + public Map> getParameterValues() { return null; } + + + @Override + public HttpRequest getRest() { return null; } + + + @Override + public String getMongoQl() { return null; } + } +} diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/dql/PgSimpleKnnRealFeature.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/dql/PgSimpleKnnRealFeature.java new file mode 100644 index 0000000..5b60a9d --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/dql/PgSimpleKnnRealFeature.java @@ -0,0 +1,123 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.postgres.dql; + +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.query.Query; +import org.polypheny.simpleclient.query.QueryBuilder; +import java.util.Map; +import java.util.Random; + + +public class PgSimpleKnnRealFeature extends QueryBuilder { + + private static final boolean EXPECT_RESULT = true; + + private final int dimension; + private final int limit; + private final String operator; + private final Random random; + + + public PgSimpleKnnRealFeature( long randomSeed, int dimension, int limit, String distanceMetric ) { + this.dimension = dimension; + this.limit = limit; + this.random = new Random( randomSeed ); + this.operator = toOperator( distanceMetric ); + } + + + private static String toOperator( String metric ) { + return switch ( metric.toLowerCase() ) { + case "cosine" -> "<=>"; + case "l2" -> "<->"; + case "l1" -> "<+>"; + case "inner_product" -> "<#>"; + default -> throw new IllegalArgumentException( "Provided metric is invalid: " + metric ); + }; + } + + + private Float[] getRandomVector() { + Float[] floats = new Float[dimension]; + for ( int i = 0; i < dimension; i++ ) { + floats[i] = random.nextInt( 100 ) / 100.0f; + } + return floats; + } + + + @Override + public synchronized Query getNewQuery() { + return new PgSimpleKnnRealFeatureQuery( getRandomVector(), limit, operator ); + } + + + private static class PgSimpleKnnRealFeatureQuery extends Query { + + private final Float[] target; + private final int limit; + private final String operator; + + + PgSimpleKnnRealFeatureQuery( Float[] target, int limit, String operator ) { + super( EXPECT_RESULT ); + this.target = target; + this.limit = limit; + this.operator = operator; + } + + + @Override + public String getSql() { + StringBuilder sb = new StringBuilder( "SELECT id, feature " ); + sb.append( operator ).append( " '[" ); + for ( int i = 0; i < target.length; i++ ) { + if ( i > 0 ) sb.append( "," ); + sb.append( target[i] ); + } + sb.append( "]' AS dist FROM knn_realfeature ORDER BY dist ASC LIMIT " ).append( limit ); + return sb.toString(); + } + + + @Override + public String getParameterizedSqlQuery() { return null; } + + + @Override + public Map> getParameterValues() { return null; } + + + @Override + public HttpRequest getRest() { return null; } + + + @Override + public String getMongoQl() { return null; } + } +} + diff --git a/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/dql/PgSimpleKnnRealFeatureFiltered.java b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/dql/PgSimpleKnnRealFeatureFiltered.java new file mode 100644 index 0000000..a3ac00c --- /dev/null +++ b/src/main/java/org/polypheny/simpleclient/scenario/vectorbench/queryBuilder/postgres/dql/PgSimpleKnnRealFeatureFiltered.java @@ -0,0 +1,130 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2026 The Polypheny Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.polypheny.simpleclient.scenario.vectorbench.queryBuilder.postgres.dql; + +import kong.unirest.core.HttpRequest; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.polypheny.simpleclient.executor.ExecutorException; +import org.polypheny.simpleclient.query.Query; +import org.polypheny.simpleclient.query.QueryBuilder; +import java.util.Map; +import java.util.Random; + + +public class PgSimpleKnnRealFeatureFiltered extends QueryBuilder { + + private static final boolean EXPECT_RESULT = true; + + private final int dimension; + private final int limit; + private final String operator; + private final String filterCategory; + private final Random random; + + + public PgSimpleKnnRealFeatureFiltered( long randomSeed, int dimension, int limit, String distanceMetric, String filterCategory ) { + this.dimension = dimension; + this.limit = limit; + this.filterCategory = filterCategory; + this.random = new Random( randomSeed ); + this.operator = toOperator( distanceMetric ); + } + + + private static String toOperator( String metric ) { + return switch ( metric.toLowerCase() ) { + case "cosine" -> "<=>"; + case "l2" -> "<->"; + case "l1" -> "<+>"; + case "inner_product" -> "<#>"; + default -> throw new IllegalArgumentException( "Provided metric is invalid: " + metric ); + }; + } + + + private Float[] getRandomVector() { + Float[] floats = new Float[dimension]; + for ( int i = 0; i < dimension; i++ ) { + floats[i] = random.nextInt( 100 ) / 100.0f; + } + return floats; + } + + + @Override + public synchronized Query getNewQuery() { + return new PgSimpleKnnRealFeatureFilteredQuery( getRandomVector(), limit, operator, filterCategory ); + } + + + private static class PgSimpleKnnRealFeatureFilteredQuery extends Query { + + private final Float[] target; + private final int limit; + private final String operator; + private final String category; + + + PgSimpleKnnRealFeatureFilteredQuery( Float[] target, int limit, String operator, String category ) { + super( EXPECT_RESULT ); + this.target = target; + this.limit = limit; + this.operator = operator; + this.category = category; + } + + + @Override + public String getSql() { + StringBuilder sb = new StringBuilder( "SELECT id, feature " ); + sb.append( operator ).append( " '[" ); + for ( int i = 0; i < target.length; i++ ) { + if ( i > 0 ) sb.append( "," ); + sb.append( target[i] ); + } + sb.append( "]' AS dist FROM knn_realfeature WHERE category = '" ) + .append( category ) + .append( "' ORDER BY dist ASC LIMIT " ).append( limit ); + return sb.toString(); + } + + + @Override + public String getParameterizedSqlQuery() { return null; } + + + @Override + public Map> getParameterValues() { return null; } + + + @Override + public HttpRequest getRest() { return null; } + + + @Override + public String getMongoQl() { return null; } + } +} + diff --git a/src/main/resources/org/polypheny/simpleclient/scenario/vectorbench/vector.properties b/src/main/resources/org/polypheny/simpleclient/scenario/vectorbench/vector.properties new file mode 100644 index 0000000..e2a954e --- /dev/null +++ b/src/main/resources/org/polypheny/simpleclient/scenario/vectorbench/vector.properties @@ -0,0 +1,90 @@ +scenario = "vectorBench" + +# Execution of queries on: polypheny | postgres +mode = polypheny + +dataStoreFeature = postgresql1 +dataStoreMeta = hsqldb + +# Default, pgvector, PostGIS, pgvector & PostGIS +postgresImageVariant = pgvector & PostGIS + +# true => DDL: REAL NOT NULL ARRAY(1,3) +# false => DDL: REAL ARRAY(1,3) +# Note: Ignored by direct postgres path +supportsNotNullArray = true + +# False -> Does not create index +# Note: When run in cli, vector index will not create indexes if set to false +useIndex = true + +# Query-time index parameters (direct-postgres path only, ignored on the Polypheny path). +# --------------------------------------------------------------------------------------- +# Applied via ALTER DATABASE when the index is built. Higher = better recall but slower. +# HNSW dynamic candidate list (pgvector default 40) +queryEfSearch = 40 +# IVFFlat number of probes (pgvector default 1) +queryProbes = 1 +# --------------------------------------------------------------------------------------- + +# Number of sampled query vectors used to compute recall@k +# Note: CLI path only +numberOfRecallQueries = 100 + +# Vector index settings, +# Method: hnsw or ivfflat +indexMethod = hnsw +# Only used for HNSW index +indexM = 16 +indexEfConstruction = 64 +# Only used for IVFFlat index +indexLists = 100 + +# PostgreSQL direct connect settings +# Used when mode = postgres +postgresHost = localhost + +numberOfThreads = 4 +progressReportBase = 100 +numberOfWarmUpIterations = 4 + +# Seeds +# Note: When running using CLI and doing an index recall this should be turned off +useRandomSeeds = false +randomSeedInsert = 46891971806236 +randomSeedQuery = 196033374268 + +dimensionFeatureVectors = 100 +batchSizeInserts = 2500 +batchSizeQueries = 10 + +# Numbers of queries +numberOfEntries = 100000 + +# Int (no vector mapping) +numberOfSimpleKnnIntFeatureQueries = 0 +numberOfSimpleKnnIdIntFeatureQueries = 0 +numberOfMetadataKnnIntFeatureQueries = 0 + +numberOfSimpleMetadataQueries = 0 + +# Real (float vector mapping) +numberOfSimpleKnnRealFeatureQueries = 0 +numberOfSimpleKnnIdRealFeatureQueries = 0 +numberOfMetadataKnnRealFeatureQueries = 0 +numberOfSimpleKnnRealCrossJoinQueries = 0 +numberOfMetadataKnnRealCrossJoinQueries = 0 +numberOfSimpleKnnRealFeatureFilteredQueries = 0 + +# Boolean (bit vector mapping) +numberOfSimpleKnnBooleanFeatureFilteredQueries = 10 +numberOfSimpleKnnBooleanFeatureQueries = 0 + + +limitKnnQueries = 10 +# L1, L2, COSINE, INNER_PRODUCT +distanceNorm = L2 +# HAMMING, JACCARD +booleanDistanceNorm = JACCARD +# Note: When ivfflat is selected as indexMethod, JACCARD and L1 indexes are not created and the step is skipped. +