Skip to content

Commit d6f63a5

Browse files
authored
Merge pull request #7 from psilberk/add-oracle-store
Code clean-up, copyright and license.
2 parents e87089e + d20908a commit d6f63a5

File tree

16 files changed

+1913
-584
lines changed

16 files changed

+1913
-584
lines changed

data/semantickernel-data-jdbc/src/main/java/com/microsoft/semantickernel/data/jdbc/JDBCVectorStoreRecordCollection.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ protected String getKeyFromRecord(Record data) {
195195
Field keyField = data.getClass()
196196
.getDeclaredField(recordDefinition.getKeyField().getName());
197197
keyField.setAccessible(true);
198-
return (String) keyField.get(data);
198+
return keyField.get(data).toString();
199199
} catch (NoSuchFieldException | IllegalAccessException e) {
200200
throw new SKException("Failed to get key from record", e);
201201
}

data/semantickernel-data-oracle/pom.xml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,17 @@
4343
<artifactId>jackson-core</artifactId>
4444
<scope>compile</scope>
4545
</dependency>
46+
<dependency>
47+
<groupId>com.fasterxml.jackson.datatype</groupId>
48+
<artifactId>jackson-datatype-jsr310</artifactId>
49+
<version>2.18.0</version>
50+
</dependency>
51+
<!--
52+
<dependency>
53+
<groupId>com.github.spotbugs</groupId>
54+
<artifactId>spotbugs-annotations</artifactId>
55+
</dependency>
56+
-->
4657
<dependency>
4758
<groupId>com.oracle.database.jdbc</groupId>
4859
<artifactId>ojdbc11</artifactId>
@@ -53,6 +64,7 @@
5364
<artifactId>ojdbc-provider-jackson-oson</artifactId>
5465
<version>1.0.4</version>
5566
</dependency>
67+
<!-- Tests use TestContainers to create an Oracle Database -->
5668
<dependency>
5769
<groupId>org.junit.jupiter</groupId>
5870
<artifactId>junit-jupiter</artifactId>
@@ -63,7 +75,6 @@
6375
<artifactId>junit-jupiter-api</artifactId>
6476
<scope>test</scope>
6577
</dependency>
66-
<!-- Tests use TestContainers to create an Oracle Database -->
6778
<dependency>
6879
<groupId>org.testcontainers</groupId>
6980
<artifactId>testcontainers</artifactId>
Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,74 @@
1+
/*
2+
** Semantic Kernel Oracle connector version 1.0.
3+
**
4+
** Copyright (c) 2025 Oracle and/or its affiliates.
5+
** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
6+
*/
17
package com.microsoft.semantickernel.data.jdbc.oracle;
28

39
/**
4-
* Defines oracle database type constants for supported field types.
10+
* Defines oracle database type constants for supported java types.
511
*/
612
public class OracleDataTypesMapping {
7-
public static final String STRING_VARCHAR = "NVARCHAR2(%s)";
13+
14+
/**
15+
* Oracle database type used when strings are mapped to VARCHAR
16+
*/
17+
public static final String STRING_VARCHAR = "VARCHAR2(%s)";
18+
/**
19+
* Oracle database type used when strings are mapped to CLOB
20+
*/
821
public static final String STRING_CLOB = "CLOB";
22+
/**
23+
* Oracle database type used to map booleans
24+
*/
925
public static final String BOOLEAN = "BOOLEAN";
26+
/**
27+
* Oracle database type used to map bytes
28+
*/
1029
public static final String BYTE = "NUMBER(3)";
30+
/**
31+
* Oracle database type used to map byte arrays
32+
*/
1133
public static final String BYTE_ARRAY = "RAW(2000)";
34+
/**
35+
* Oracle database type used to map shorts
36+
*/
1237
public static final String SHORT = "NUMBER(5)";
38+
/**
39+
* Oracle database type used to map ints
40+
*/
1341
public static final String INTEGER = "NUMBER(10)";
42+
/**
43+
* Oracle database type used to map longs
44+
*/
1445
public static final String LONG = "NUMBER(19)";
46+
/**
47+
* Oracle database type used to map float
48+
*/
1549
public static final String FLOAT = "BINARY_FLOAT";
50+
/**
51+
* Oracle database type used to map double
52+
*/
1653
public static final String DOUBLE = "BINARY_DOUBLE";
54+
/**
55+
* Oracle database type used to map BigDecimal
56+
*/
1757
public static final String DECIMAL = "NUMBER";
58+
/**
59+
* Oracle database type used to map offset date time
60+
*/
1861
public static final String OFFSET_DATE_TIME = "TIMESTAMP(7) WITH TIME ZONE";
19-
public static final String UUID = "RAW(16)";
62+
/**
63+
* Oracle database type used to map UUID
64+
*/
65+
public static final String UUID = "VARCHAR2(36)";
66+
/**
67+
* Oracle database type used to map lists
68+
*/
2069
public static final String JSON = "JSON";
70+
/**
71+
* Oracle database type used to map vectors (the parameter is the dimension of the vector)
72+
*/
2173
public static final String VECTOR_FLOAT = "VECTOR(%s, FLOAT32)";
2274
}
Lines changed: 68 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1+
/*
2+
** Semantic Kernel Oracle connector version 1.0.
3+
**
4+
** Copyright (c) 2025 Oracle and/or its affiliates.
5+
** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
6+
*/
17
package com.microsoft.semantickernel.data.jdbc.oracle;
28

39
import com.microsoft.semantickernel.data.jdbc.oracle.OracleVectorStoreQueryProvider.StringTypeMapping;
410
import com.microsoft.semantickernel.data.vectorstorage.definition.VectorStoreRecordDataField;
11+
import com.microsoft.semantickernel.data.vectorstorage.definition.VectorStoreRecordField;
512
import com.microsoft.semantickernel.data.vectorstorage.definition.VectorStoreRecordKeyField;
613
import com.microsoft.semantickernel.data.vectorstorage.definition.VectorStoreRecordVectorField;
714
import oracle.jdbc.OracleTypes;
@@ -16,24 +23,22 @@
1623
import java.util.stream.Collectors;
1724

1825
/**
19-
* Helper class for field operations.
26+
* Helper class for field operations. Handles mapping between field java types to DB types and
27+
* generating SQL statement to create field indexes.
2028
*/
21-
public class OracleVectorStoreFieldHelper {
22-
private static final Logger LOGGER = Logger.getLogger(OracleVectorStoreQueryProvider.class.getName());
29+
class OracleVectorStoreFieldHelper {
30+
31+
/**
32+
* The logger
33+
*/
34+
private static final Logger LOGGER = Logger.getLogger(OracleVectorStoreFieldHelper.class.getName());
2335

2436
/**
2537
* Maps supported key java classes to Oracle database types
2638
*/
2739
private static final HashMap<Class<?>, String> supportedKeyTypes = new HashMap() {
2840
{
2941
put(String.class, String.format(OracleDataTypesMapping.STRING_VARCHAR, 255));
30-
put(short.class, OracleDataTypesMapping.SHORT);
31-
put(Short.class, OracleDataTypesMapping.SHORT);
32-
put(int.class, OracleDataTypesMapping.INTEGER);
33-
put(Integer.class, OracleDataTypesMapping.INTEGER);
34-
put(long.class, OracleDataTypesMapping.LONG);
35-
put(Long.class, OracleDataTypesMapping.LONG);
36-
put(UUID .class, OracleDataTypesMapping.UUID);
3742
}
3843
};
3944

@@ -47,14 +52,6 @@ public class OracleVectorStoreFieldHelper {
4752
put(Collection.class, OracleDataTypesMapping.VECTOR_FLOAT);
4853
put(float[].class, OracleDataTypesMapping.VECTOR_FLOAT);
4954
put(Float[].class, OracleDataTypesMapping.VECTOR_FLOAT);
50-
/*
51-
put(byte[].class,"VECTOR(%s, INT8)");
52-
put(Byte[].class,"VECTOR(%s, INT8)");
53-
put(double[].class,"VECTOR(%s, FLOAT64)");
54-
put(Double[].class,"VECTOR(%s, FLOAT64)");
55-
put(boolean[].class,"VECTOR(%s, BINARY)");
56-
put(Boolean[].class,"VECTOR(%s, BINARY)");
57-
*/
5855
}
5956
};
6057

@@ -83,26 +80,12 @@ public class OracleVectorStoreFieldHelper {
8380
put(byte[].class, OracleDataTypesMapping.BYTE_ARRAY);
8481
put(List.class, OracleDataTypesMapping.JSON);
8582
}
86-
8783
};
8884

8985
/**
90-
* Maps vector type to OracleTypes. Only needed if types other than FLOAT_32 are supported.
86+
* Suffix added to the effective column name to generate the index name for a vector column.
9187
*/
92-
private static final Map<Class<?>, Integer> mapOracleTypeToVector = new HashMap() {
93-
{
94-
put(float[].class, OracleTypes.VECTOR_FLOAT32);
95-
put(Float[].class, OracleTypes.VECTOR_FLOAT32);
96-
/*
97-
put(byte[].class, OracleTypes.VECTOR_INT8);
98-
put(Byte[].class, OracleTypes.VECTOR_INT8);
99-
put(Double[].class, OracleTypes.VECTOR_FLOAT64);
100-
put(double[].class, OracleTypes.VECTOR_FLOAT64);
101-
put(Boolean[].class, OracleTypes.VECTOR_BINARY);
102-
put(boolean[].class, OracleTypes.VECTOR_BINARY);
103-
*/
104-
}
105-
};
88+
public static final String VECTOR_INDEX_SUFFIX = "_VECTOR_INDEX";
10689

10790
/**
10891
* Gets the mapping between the supported Java key types and the Oracle database type.
@@ -120,12 +103,11 @@ public static HashMap<Class<?>, String> getSupportedKeyTypes() {
120103
*/
121104
public static Map<Class<?>, String> getSupportedDataTypes(
122105
StringTypeMapping stringTypeMapping, int defaultVarCharLength) {
123-
124-
if (stringTypeMapping.equals(StringTypeMapping.USE_VARCHAR)) {
125-
supportedDataTypes.put(String.class, String.format(OracleDataTypesMapping.STRING_VARCHAR, defaultVarCharLength));
126-
} else {
127-
supportedDataTypes.put(String.class, OracleDataTypesMapping.STRING_CLOB);
128-
}
106+
String stringType = stringTypeMapping.equals(StringTypeMapping.USE_VARCHAR)
107+
? String.format(OracleDataTypesMapping.STRING_VARCHAR, defaultVarCharLength)
108+
: OracleDataTypesMapping.STRING_CLOB;
109+
supportedDataTypes.put(String.class, stringType);
110+
LOGGER.finest("Mapping String columns to " + stringType);
129111
return supportedDataTypes;
130112
}
131113

@@ -176,14 +158,14 @@ public static String getCreateVectorIndexStatement(VectorStoreRecordVectorField
176158
*/
177159
public static String createIndexForDataField(String collectionTableName, VectorStoreRecordDataField dataField, Map<Class<?>, String> supportedDataTypes) {
178160
if (supportedDataTypes.get(dataField.getFieldType()) == "JSON") {
179-
String dataFieldIndex = "CREATE MULTIVALUE INDEX %s ON %s t (t.%s.%s)";
161+
String dataFieldIndex = "CREATE MULTIVALUE INDEX IF NOT EXISTS %s ON %s t (t.%s.%s)";
180162
return String.format(dataFieldIndex,
181163
collectionTableName + "_" + dataField.getEffectiveStorageName(),
182164
collectionTableName,
183165
dataField.getEffectiveStorageName(),
184166
getFunctionForType(supportedDataTypes.get(dataField.getFieldSubType())));
185167
} else {
186-
String dataFieldIndex = "CREATE INDEX %s ON %s (%s ASC)";
168+
String dataFieldIndex = "CREATE INDEX IF NOT EXISTS %s ON %s (%s ASC)";
187169
return String.format(dataFieldIndex,
188170
collectionTableName + "_" + dataField.getEffectiveStorageName(),
189171
collectionTableName,
@@ -192,6 +174,50 @@ public static String createIndexForDataField(String collectionTableName, VectorS
192174
}
193175
}
194176

177+
/**
178+
* Returns vector columns names and types for CREATE TABLE statement
179+
* @param fields list of vector record fields.
180+
* @return comma separated list of columns and types for CREATE TABLE statement.
181+
*/
182+
public static String getVectorColumnNamesAndTypes(List<VectorStoreRecordVectorField> fields) {
183+
List<String> columns = fields.stream()
184+
.map(field -> field.getEffectiveStorageName() + " " +
185+
OracleVectorStoreFieldHelper.getTypeForVectorField(field)
186+
).collect(Collectors.toList());
187+
188+
return String.join(", ", columns);
189+
}
190+
191+
/**
192+
* Returns key column names and type for key column for CREATE TABLE statement
193+
* @param field the key field.
194+
* @return column name and type of the key field for CREATE TABLE statement.
195+
*/
196+
public static String getKeyColumnNameAndType(VectorStoreRecordKeyField field) {
197+
return field.getEffectiveStorageName() + " " + supportedKeyTypes.get(field.getFieldType());
198+
}
199+
200+
201+
/**
202+
* Generates the index name given the field name. by suffixing "_VECTOR_INDEX" to the field name.
203+
* @param effectiveStorageName the field name.
204+
* @return the index name.
205+
*/
206+
private static String getIndexName(String effectiveStorageName) {
207+
return effectiveStorageName + VECTOR_INDEX_SUFFIX;
208+
}
209+
210+
/**
211+
* Gets the type of the vector given the field definition. This method is not needed if only
212+
*
213+
* @param field the vector field definition.
214+
* @return returns the type of vector for the given field type.
215+
*/
216+
private static String getTypeForVectorField(VectorStoreRecordVectorField field) {
217+
String dimension = field.getDimensions() > 0 ? String.valueOf(field.getDimensions()) : "*";
218+
return String.format(supportedVectorTypes.get(field.getFieldType()), dimension);
219+
}
220+
195221
/**
196222
* Gets the function that allows to return the function that converts the JSON value to the
197223
* data type.
@@ -218,90 +244,4 @@ private static String getFunctionForType(String jdbcType) {
218244
}
219245
}
220246

221-
/**
222-
* Gets the type of the vector given the field definition. This method is not needed if only
223-
*
224-
* @param field the vector field definition.
225-
* @return returns the type of vector for the given field type.
226-
*/
227-
public static String getTypeForVectorField(VectorStoreRecordVectorField field) {
228-
String dimension = field.getDimensions() > 0 ? String.valueOf(field.getDimensions()) : "*";
229-
return String.format(supportedVectorTypes.get(field.getFieldType()), dimension);
230-
/* Not needed since all types are FLOAT32
231-
if (field.getFieldSubType() != null) {
232-
String vectorType;
233-
switch (field.getFieldSubType().getName()) {
234-
case "java.lang.Double":
235-
vectorType = "FLOAT64";
236-
break;
237-
case "java.lang.Byte":
238-
vectorType = "INT8";
239-
break;
240-
case "java.lang.Boolean":
241-
vectorType = "BINARY";
242-
break;
243-
default:
244-
vectorType = "FLOAT32";
245-
}
246-
return String.format(supportedVectorTypes.get(field.getFieldType()), dimension, vectorType);
247-
} else {
248-
return String.format(supportedVectorTypes.get(field.getFieldType()), dimension);
249-
}
250-
*/
251-
}
252-
253-
/**
254-
* Gets the JDBC oracle of the vector field definition.
255-
* @param field the vector field definition.
256-
* @return the JDBC oracle type.
257-
*/
258-
public static int getOracleTypeForField(VectorStoreRecordVectorField field) {
259-
if (field.getFieldSubType() == null) {
260-
return mapOracleTypeToVector.get(field.getFieldType()).intValue();
261-
} else {
262-
switch (field.getFieldSubType().getName()) {
263-
case "java.lang.Double":
264-
return OracleTypes.VECTOR_FLOAT64;
265-
case "java.lang.Byte":
266-
return OracleTypes.VECTOR_INT8;
267-
case "java.lang.Boolean":
268-
return OracleTypes.VECTOR_BINARY;
269-
default:
270-
return OracleTypes.VECTOR_FLOAT32;
271-
}
272-
}
273-
}
274-
275-
/**
276-
* Generates the index name given the field name. by suffixing "_VECTOR_INDEX" to the field name.
277-
* @param effectiveStorageName the field name.
278-
* @return the index name.
279-
*/
280-
private static String getIndexName(String effectiveStorageName) {
281-
return effectiveStorageName + "_VECTOR_INDEX";
282-
}
283-
284-
/**
285-
* Returns vector columns names and types for CREATE TABLE statement
286-
* @param fields list of vector record fields.
287-
* @return comma separated list of columns and types for CREATE TABLE statement.
288-
*/
289-
public static String getVectorColumnNamesAndTypes(List<VectorStoreRecordVectorField> fields) {
290-
List<String> columns = fields.stream()
291-
.map(field -> field.getEffectiveStorageName() + " " +
292-
OracleVectorStoreFieldHelper.getTypeForVectorField(field)
293-
).collect(Collectors.toList());
294-
295-
return String.join(", ", columns);
296-
}
297-
298-
/**
299-
* Returns key column names and type for key column for CREATE TABLE statement
300-
* @param field the key field.
301-
* @return column name and type of the key field for CREATE TABLE statement.
302-
*/
303-
public static String getKeyColumnNameAndType(VectorStoreRecordKeyField field) {
304-
return field.getEffectiveStorageName() + " " + supportedKeyTypes.get(field.getFieldType());
305-
}
306-
307247
}

0 commit comments

Comments
 (0)