diff --git a/java/23ai-jdbc-vector-search/README.md b/java/23ai-jdbc-vector-search/README.md new file mode 100644 index 00000000..c12c9d59 --- /dev/null +++ b/java/23ai-jdbc-vector-search/README.md @@ -0,0 +1 @@ +[Oracle AI Vector Search for Java Developers with the Oracle Database 23ai](https://juarezjunior.medium.com/oracle-ai-vector-search-for-java-developers-with-the-oracle-database-23ai-e29321d23fa0) \ No newline at end of file diff --git a/java/23ai-jdbc-vector-search/pom.xml b/java/23ai-jdbc-vector-search/pom.xml new file mode 100644 index 00000000..a6dfb830 --- /dev/null +++ b/java/23ai-jdbc-vector-search/pom.xml @@ -0,0 +1,82 @@ + + + + 4.0.0 + + com.oracle.dev.jdbc + 23ai-jdbc-vector-search + 1.0-SNAPSHOT + + 23ai-jdbc-vector-search + Oracle AI Vector Search for Java Developers with the Oracle + Database 23ai + + https://juarezjunior.medium.com/oracle-ai-vector-search-for-java-developers-with-the-oracle-database-23ai-e29321d23fa0 + + + UTF-8 + 21 + 21 + + + + + com.oracle.database.jdbc + ojdbc11 + 23.4.0.24.05 + + + com.oracle.database.jdbc + ucp + 23.4.0.24.05 + + + + + + + maven-clean-plugin + 3.1.0 + + + maven-site-plugin + 3.7.1 + + + maven-project-info-reports-plugin + 3.0.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.8.0 + + + maven-surefire-plugin + 2.22.1 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + + + diff --git a/java/23ai-jdbc-vector-search/sql/23ai-jdbc-vector-search.sql b/java/23ai-jdbc-vector-search/sql/23ai-jdbc-vector-search.sql new file mode 100644 index 00000000..01092479 --- /dev/null +++ b/java/23ai-jdbc-vector-search/sql/23ai-jdbc-vector-search.sql @@ -0,0 +1,29 @@ +SELECT * FROM V$VERSION; + +CREATE USER VECTOR_USER IDENTIFIED BY QUOTA UNLIMITED ON USERS; +GRANT DB_DEVELOPER_ROLE TO VECTOR_USER; +GRANT CREATE SESSION TO VECTOR_USER; + +GRANT SELECT ANY TABLE ON SCHEMA VECTOR_USER TO VECTOR_USER; +GRANT SELECT ANY TABLE ON SCHEMA VECTOR_USER TO VECTOR_USER; +GRANT SELECT ANY TABLE ON SCHEMA VECTOR_USER TO VECTOR_USER; +GRANT SELECT ANY TABLE ON SCHEMA VECTOR_USER TO VECTOR_USER; + +ALTER SESSION SET CURRENT_SCHEMA = VECTOR_USER; + +CREATE TABLE VECTOR_USER.ORACLE_AI_VECTOR_SEARCH_DEMO (ID NUMBER GENERATED ALWAYS as IDENTITY(START with 1 INCREMENT by 1) PRIMARY KEY, VECTOR_DATA VECTOR(3, FLOAT64)); +COMMIT; + +DESCRIBE VECTOR_USER.ORACLE_AI_VECTOR_SEARCH_DEMO; + +SELECT * FROM VECTOR_USER.ORACLE_AI_VECTOR_SEARCH_DEMO; + +DELETE FROM VECTOR_USER.ORACLE_AI_VECTOR_SEARCH_DEMO; +COMMIT; + +DROP TABLE VECTOR_USER.ORACLE_AI_VECTOR_SEARCH_DEMO PURGE; + + + + + diff --git a/java/23ai-jdbc-vector-search/src/main/java/com/oracle/dev/jdbc/OracleAIVectorSearchWithJava.java b/java/23ai-jdbc-vector-search/src/main/java/com/oracle/dev/jdbc/OracleAIVectorSearchWithJava.java new file mode 100644 index 00000000..d2106e18 --- /dev/null +++ b/java/23ai-jdbc-vector-search/src/main/java/com/oracle/dev/jdbc/OracleAIVectorSearchWithJava.java @@ -0,0 +1,181 @@ +/* + Copyright (c) 2024, Oracle and/or its affiliates. + This software is dual-licensed to you under the Universal Permissive License + (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License + 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose + either license. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + https://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.oracle.dev.jdbc; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.Properties; + +import oracle.jdbc.OracleType; +import oracle.ucp.jdbc.PoolDataSource; +import oracle.ucp.jdbc.PoolDataSourceFactory; + +public class OracleAIVectorSearchWithJava { + + private final static String URL = "jdbc:oracle:thin:@localhost:1521/FREEPDB1"; + private final static String USERNAME = System.getenv("DB_23AI_USERNAME"); + private final static String PASSWORD = System.getenv("DB_23AI_PASSWORD"); + + private String insertSql = "INSERT INTO ORACLE_AI_VECTOR_SEARCH_DEMO (VECTOR_DATA) VALUES (?)"; + private String querySql = "SELECT ID, VECTOR_DATA FROM ORACLE_AI_VECTOR_SEARCH_DEMO"; + private String querySqlWithBind = "SELECT ID, VECTOR_DATA FROM ORACLE_AI_VECTOR_SEARCH_DEMO ORDER BY VECTOR_DISTANCE(VECTOR_DATA, ?, COSINE)"; + + public static void main(String[] args) throws SQLException { + OracleAIVectorSearchWithJava oracleAIVectorSearch = new OracleAIVectorSearchWithJava(); + oracleAIVectorSearch.execute(); + } + + private void execute() throws SQLException { + + System.out.println("Starting JDBC connection with PooledDataSource..."); + try (Connection conn = getConnectionFromPooledDataSource()) { + System.out.println("Connected to Oracle Database 23ai! " + "\n"); + + System.out + .println("Inserting VECTOR with oracle.jdbc.OracleType.VECTOR..."); + insertVector(conn); + System.out.println( + "VECTOR with oracle.jdbc.OracleType.VECTOR inserted!" + "\n"); + + System.out + .println("Inserting VECTOR with oracle.jdbc.OracleType.VARCHAR2..."); + insertVectorWithVarChar2(conn); + System.out.println( + "VECTOR with oracle.jdbc.OracleType.VARCHAR2 inserted!" + "\n"); + + System.out.println("Inserting VECTOR with Batch API..."); + insertVectorWithBatchAPI(conn); + System.out.println("VECTOR with Batch API inserted!" + "\n"); + + System.out.println("Retrieving VECTOR as double array..."); + retrieveVectorAsArray(conn); + System.out.println("VECTOR retrieved!" + "\n"); + + System.out.println("Retrieving VECTOR as String..."); + retrieveVectorAsString(conn); + System.out.println("VECTOR retrieved!" + "\n"); + + System.out.println( + "Retrieving VECTOR with bound VECTOR (L2 - Euclidean distance)..."); + retrieveVectorWithBoundVector(conn); + System.out.println("VECTOR retrieved!"); + + } + } + + private void insertVector(Connection connection) throws SQLException { + PreparedStatement insertStatement = connection.prepareStatement(insertSql, + new String[]{"ID"}); + float[] vector = {1.1f, 2.2f, 3.3f}; + System.out.println("SQL DML: " + insertSql); + System.out.println("VECTOR to be inserted: " + Arrays.toString(vector)); + insertStatement.setObject(1, vector, OracleType.VECTOR_FLOAT64); + insertStatement.executeUpdate(); + } + + private void insertVectorWithVarChar2(Connection connection) + throws SQLException { + PreparedStatement insertStatement = connection.prepareStatement(insertSql, + new String[]{"ID"}); + float[] vector = {1.1f, 2.2f, 3.3f}; + System.out.println("SQL DML: " + insertSql); + System.out.println("VECTOR to be inserted: " + Arrays.toString(vector)); + insertStatement.setObject(1, Arrays.toString(vector), OracleType.VARCHAR2); + insertStatement.executeUpdate(); + } + + private void insertVectorWithBatchAPI(Connection connection) + throws SQLException { + + float[][] vectors = {{1.1f, 2.2f, 3.3f}, {1.3f, 7.2f, 4.3f}, + {5.9f, 5.2f, 7.3f}}; + System.out.println("SQL DML: " + insertSql); + System.out.println("VECTORs to be inserted as a batch: " + + Arrays.toString(vectors[0]) + ", " + Arrays.toString(vectors[1]) + + ", " + Arrays.toString(vectors[2])); + try (PreparedStatement insertStatement = connection + .prepareStatement(insertSql, new String[]{"ID"})) { + for (float[] vector : vectors) { + insertStatement.setObject(1, vector, OracleType.VECTOR_FLOAT64); + insertStatement.addBatch(); + } + insertStatement.executeBatch(); + } + } + + private void retrieveVectorAsArray(Connection connection) + throws SQLException { + PreparedStatement queryStatement = connection.prepareStatement(querySql); + System.out.println("SQL DML: " + querySql); + ResultSet resultSet = queryStatement.executeQuery(); + float[] vector = null; + while (resultSet.next()) { + vector = resultSet.getObject(2, float[].class); + } + System.out.println("Retrieved VECTOR: " + Arrays.toString(vector)); + } + + private void retrieveVectorAsString(Connection connection) + throws SQLException { + PreparedStatement queryStatement = connection.prepareStatement(querySql); + System.out.println("SQL DML: " + querySql); + ResultSet resultSet = queryStatement.executeQuery(); + String vector = null; + while (resultSet.next()) { + vector = (String) resultSet.getObject(2); + } + System.out.println("Retrieved VECTOR: " + vector); + } + + private void retrieveVectorWithBoundVector(Connection connection) + throws SQLException { + PreparedStatement queryStatement = connection + .prepareStatement(querySqlWithBind); + float[] inputVector = {1.0f, 2.2f, 3.3f}; + System.out.println("SQL DML: " + querySqlWithBind); + System.out.println("Bound VECTOR: " + Arrays.toString(inputVector)); + queryStatement.setObject(1, inputVector, OracleType.VECTOR_FLOAT64); + ResultSet resultSet = queryStatement.executeQuery(); + resultSet.next(); + float[] outputVector = resultSet.getObject(2, float[].class); + System.out.println("Retrieved VECTOR: " + Arrays.toString(outputVector)); + } + + private Connection getConnectionFromPooledDataSource() throws SQLException { + // Create pool-enabled data source instance + PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource(); + // set connection properties on the data source + pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource"); + pds.setURL(URL); + pds.setUser(USERNAME); + pds.setPassword(PASSWORD); + // Configure pool properties with a Properties instance + Properties prop = new Properties(); + prop.setProperty("oracle.jdbc.vectorDefaultGetObjectType", "String"); + pds.setConnectionProperties(prop); + // Override any pool properties directly + pds.setInitialPoolSize(10); + // Get a database connection from the pool-enabled data source + Connection conn = pds.getConnection(); + return conn; + } + +}