29
29
import java .util .HashMap ;
30
30
import java .util .List ;
31
31
import java .util .Map ;
32
+ import java .util .concurrent .locks .Lock ;
33
+ import java .util .concurrent .locks .ReentrantLock ;
32
34
import java .util .stream .Collectors ;
33
35
import java .util .stream .Stream ;
34
36
import javax .annotation .Nonnull ;
37
+ import javax .annotation .concurrent .GuardedBy ;
35
38
import javax .sql .DataSource ;
36
39
import org .slf4j .Logger ;
37
40
import org .slf4j .LoggerFactory ;
@@ -54,6 +57,8 @@ public class JDBCVectorStoreQueryProvider
54
57
private final String collectionsTable ;
55
58
private final String prefixForCollectionTables ;
56
59
60
+ private final Object dbCreationLock = new Object ();
61
+
57
62
@ SuppressFBWarnings ("EI_EXPOSE_REP2" ) // DataSource is not exposed
58
63
protected JDBCVectorStoreQueryProvider (
59
64
@ Nonnull DataSource dataSource ,
@@ -89,12 +94,13 @@ protected JDBCVectorStoreQueryProvider(
89
94
90
95
/**
91
96
* Creates a new instance of the JDBCVectorStoreQueryProvider class.
92
- * @param dataSource the data source
93
- * @param collectionsTable the collections table
97
+ *
98
+ * @param dataSource the data source
99
+ * @param collectionsTable the collections table
94
100
* @param prefixForCollectionTables the prefix for collection tables
95
- * @param supportedKeyTypes the supported key types
96
- * @param supportedDataTypes the supported data types
97
- * @param supportedVectorTypes the supported vector types
101
+ * @param supportedKeyTypes the supported key types
102
+ * @param supportedDataTypes the supported data types
103
+ * @param supportedVectorTypes the supported vector types
98
104
*/
99
105
public JDBCVectorStoreQueryProvider (
100
106
@ SuppressFBWarnings ("EI_EXPOSE_REP2" ) @ Nonnull DataSource dataSource ,
@@ -276,48 +282,57 @@ public boolean collectionExists(String collectionName) {
276
282
*/
277
283
@ Override
278
284
@ SuppressFBWarnings ("SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING" )
285
+ @ GuardedBy ("dbCreationLock" )
279
286
// SQL query is generated dynamically with valid identifiers
280
287
public void createCollection (String collectionName ,
281
288
VectorStoreRecordDefinition recordDefinition ) {
282
289
283
- // No approximate search is supported in JDBCVectorStoreQueryProvider
284
- if (recordDefinition .getVectorFields ().stream ()
285
- .anyMatch (
286
- field -> field .getIndexKind () != null && field .getIndexKind () != IndexKind .FLAT
287
- && field .getIndexKind () != IndexKind .UNDEFINED )) {
288
- LOGGER
289
- .warn (String .format ("Indexes are not supported in %s. Ignoring indexKind property." ,
290
- this .getClass ().getName ()));
291
- }
292
-
293
- String createStorageTable = formatQuery ("CREATE TABLE IF NOT EXISTS %s ("
294
- + "%s VARCHAR(255) PRIMARY KEY, "
295
- + "%s, "
296
- + "%s);" ,
297
- getCollectionTableName (collectionName ),
298
- getKeyColumnName (recordDefinition .getKeyField ()),
299
- getColumnNamesAndTypes (new ArrayList <>(recordDefinition .getDataFields ()),
300
- getSupportedDataTypes ()),
301
- getColumnNamesAndTypes (new ArrayList <>(recordDefinition .getVectorFields ()),
302
- getSupportedVectorTypes ()));
290
+ synchronized (dbCreationLock ) {
291
+ // No approximate search is supported in JDBCVectorStoreQueryProvider
292
+ if (recordDefinition .getVectorFields ().stream ()
293
+ .anyMatch (
294
+ field -> field .getIndexKind () != null && field .getIndexKind () != IndexKind .FLAT
295
+ && field .getIndexKind () != IndexKind .UNDEFINED )) {
296
+ LOGGER
297
+ .warn (String .format (
298
+ "Indexes are not supported in %s. Ignoring indexKind property." ,
299
+ this .getClass ().getName ()));
300
+ }
303
301
304
- String insertCollectionQuery = formatQuery ("INSERT INTO %s (collectionId) VALUES (?)" ,
305
- validateSQLidentifier (collectionsTable ));
302
+ String createStorageTable = formatQuery ("CREATE TABLE IF NOT EXISTS %s ("
303
+ + "%s VARCHAR(255) PRIMARY KEY, "
304
+ + "%s, "
305
+ + "%s);" ,
306
+ getCollectionTableName (collectionName ),
307
+ getKeyColumnName (recordDefinition .getKeyField ()),
308
+ getColumnNamesAndTypes (new ArrayList <>(recordDefinition .getDataFields ()),
309
+ getSupportedDataTypes ()),
310
+ getColumnNamesAndTypes (new ArrayList <>(recordDefinition .getVectorFields ()),
311
+ getSupportedVectorTypes ()));
312
+
313
+ String insertCollectionQuery = this .getInsertCollectionQuery (collectionsTable );
314
+
315
+ try (Connection connection = dataSource .getConnection ();
316
+ PreparedStatement createTable = connection .prepareStatement (createStorageTable )) {
317
+ createTable .execute ();
318
+ } catch (SQLException e ) {
319
+ throw new SKException ("Failed to create collection" , e );
320
+ }
306
321
307
- try (Connection connection = dataSource .getConnection ();
308
- PreparedStatement createTable = connection .prepareStatement (createStorageTable )) {
309
- createTable .execute ();
310
- } catch (SQLException e ) {
311
- throw new SKException ("Failed to create collection" , e );
322
+ try (Connection connection = dataSource .getConnection ();
323
+ PreparedStatement insert = connection .prepareStatement (insertCollectionQuery )) {
324
+ insert .setObject (1 , collectionName );
325
+ insert .execute ();
326
+ } catch (SQLException e ) {
327
+ throw new SKException ("Failed to insert collection" , e );
328
+ }
312
329
}
330
+ }
313
331
314
- try (Connection connection = dataSource .getConnection ();
315
- PreparedStatement insert = connection .prepareStatement (insertCollectionQuery )) {
316
- insert .setObject (1 , collectionName );
317
- insert .execute ();
318
- } catch (SQLException e ) {
319
- throw new SKException ("Failed to insert collection" , e );
320
- }
332
+ protected String getInsertCollectionQuery (String collectionsTable ) {
333
+ return formatQuery (
334
+ "INSERT IGNORE INTO %s (collectionId) VALUES (?)" ,
335
+ validateSQLidentifier (collectionsTable ));
321
336
}
322
337
323
338
/**
@@ -327,26 +342,29 @@ public void createCollection(String collectionName,
327
342
* @throws SKException if an error occurs while deleting the collection
328
343
*/
329
344
@ Override
345
+ @ GuardedBy ("dbCreationLock" )
330
346
public void deleteCollection (String collectionName ) {
331
- String deleteCollectionOperation = formatQuery ("DELETE FROM %s WHERE collectionId = ?" ,
332
- validateSQLidentifier (collectionsTable ));
333
- String dropTableOperation = formatQuery ("DROP TABLE %s" ,
334
- getCollectionTableName (collectionName ));
335
-
336
- try (Connection connection = dataSource .getConnection ();
337
- PreparedStatement deleteCollection = connection
338
- .prepareStatement (deleteCollectionOperation )) {
339
- deleteCollection .setObject (1 , collectionName );
340
- deleteCollection .execute ();
341
- } catch (SQLException e ) {
342
- throw new SKException ("Failed to delete collection" , e );
343
- }
347
+ synchronized (dbCreationLock ) {
348
+ String deleteCollectionOperation = formatQuery ("DELETE FROM %s WHERE collectionId = ?" ,
349
+ validateSQLidentifier (collectionsTable ));
350
+ String dropTableOperation = formatQuery ("DROP TABLE %s" ,
351
+ getCollectionTableName (collectionName ));
352
+
353
+ try (Connection connection = dataSource .getConnection ();
354
+ PreparedStatement deleteCollection = connection
355
+ .prepareStatement (deleteCollectionOperation )) {
356
+ deleteCollection .setObject (1 , collectionName );
357
+ deleteCollection .execute ();
358
+ } catch (SQLException e ) {
359
+ throw new SKException ("Failed to delete collection" , e );
360
+ }
344
361
345
- try (Connection connection = dataSource .getConnection ();
346
- PreparedStatement dropTable = connection .prepareStatement (dropTableOperation )) {
347
- dropTable .execute ();
348
- } catch (SQLException e ) {
349
- throw new SKException ("Failed to drop table" , e );
362
+ try (Connection connection = dataSource .getConnection ();
363
+ PreparedStatement dropTable = connection .prepareStatement (dropTableOperation )) {
364
+ dropTable .execute ();
365
+ } catch (SQLException e ) {
366
+ throw new SKException ("Failed to drop table" , e );
367
+ }
350
368
}
351
369
}
352
370
@@ -518,8 +536,8 @@ protected <Record> List<Record> getRecordsWithFilter(String collectionName,
518
536
*
519
537
* @param <Record> the record type
520
538
* @param collectionName the collection name
521
- * @param vector the vector to search with
522
- * @param options the search options
539
+ * @param vector the vector to search with
540
+ * @param options the search options
523
541
* @param recordDefinition the record definition
524
542
* @param mapper the mapper, responsible for mapping the result set to the record
525
543
* type.
@@ -622,8 +640,8 @@ public String getFilter(VectorSearchFilter filter,
622
640
}
623
641
624
642
/**
625
- * Gets the filter parameters for the given vector search filter to associate with the filter string
626
- * generated by the getFilter method.
643
+ * Gets the filter parameters for the given vector search filter to associate with the filter
644
+ * string generated by the getFilter method.
627
645
*
628
646
* @param filter The filter to get the filter parameters for.
629
647
* @return The filter parameters.
0 commit comments