@@ -138,6 +138,13 @@ export class PrismaVectorStore<
138138
139139 protected columnTypes ?: ColumnTypeConfig ;
140140
141+ /**
142+ * When true, addDocuments uses INSERT statements to create new records.
143+ * When false (default), addDocuments uses UPDATE statements to update existing records by ID.
144+ * Set to true when using with ParentDocumentRetriever or when documents don't pre-exist in the database.
145+ */
146+ protected useInsert : boolean ;
147+
141148 static IdColumn : typeof IdColumnSymbol = IdColumnSymbol ;
142149
143150 static ContentColumn : typeof ContentColumnSymbol = ContentColumnSymbol ;
@@ -160,6 +167,12 @@ export class PrismaVectorStore<
160167 columns : TSelectModel ;
161168 filter ?: TFilterModel ;
162169 columnTypes ?: ColumnTypeConfig ;
170+ /**
171+ * When true, addDocuments uses INSERT statements to create new records.
172+ * When false (default), addDocuments uses UPDATE statements to update existing records by ID.
173+ * Set to true when using with ParentDocumentRetriever or when documents don't pre-exist in the database.
174+ */
175+ useInsert ?: boolean ;
163176 }
164177 ) {
165178 super ( embeddings , { } ) ;
@@ -182,6 +195,7 @@ export class PrismaVectorStore<
182195 this . tableName = config . tableName ;
183196 this . vectorColumnName = config . vectorColumnName ;
184197 this . columnTypes = config . columnTypes ;
198+ this . useInsert = config . useInsert ?? false ;
185199
186200 this . selectColumns = entries
187201 . map ( ( [ key , alias ] ) => ( alias && key ) || null )
@@ -211,6 +225,7 @@ export class PrismaVectorStore<
211225 columns : TColumns ;
212226 filter ?: TFilters ;
213227 columnTypes ?: ColumnTypeConfig ;
228+ useInsert ?: boolean ;
214229 }
215230 ) {
216231 type ModelName = keyof TPrisma [ "ModelName" ] & string ;
@@ -233,6 +248,7 @@ export class PrismaVectorStore<
233248 vectorColumnName : string ;
234249 columns : TColumns ;
235250 columnTypes ?: ColumnTypeConfig ;
251+ useInsert ?: boolean ;
236252 }
237253 ) {
238254 const docs : Document [ ] = [ ] ;
@@ -264,6 +280,7 @@ export class PrismaVectorStore<
264280 vectorColumnName : string ;
265281 columns : TColumns ;
266282 columnTypes ?: ColumnTypeConfig ;
283+ useInsert ?: boolean ;
267284 }
268285 ) {
269286 type ModelName = keyof TPrisma [ "ModelName" ] & string ;
@@ -303,10 +320,12 @@ export class PrismaVectorStore<
303320 */
304321 async addDocuments ( documents : Document < TModel > [ ] ) {
305322 const texts = documents . map ( ( { pageContent } ) => pageContent ) ;
306- return this . addVectors (
307- await this . embeddings . embedDocuments ( texts ) ,
308- documents
309- ) ;
323+ const vectors = await this . embeddings . embedDocuments ( texts ) ;
324+
325+ if ( this . useInsert ) {
326+ return this . addDocumentsWithVectors ( vectors , documents ) ;
327+ }
328+ return this . addVectors ( vectors , documents ) ;
310329 }
311330
312331 /**
@@ -350,6 +369,58 @@ export class PrismaVectorStore<
350369 ) ;
351370 }
352371
372+ /**
373+ * Adds documents with their corresponding vectors to the store using INSERT statements.
374+ * This method ensures documents are created if they don't exist, making it compatible
375+ * with ParentDocumentRetriever which creates new child documents.
376+ * @param vectors The vectors to add.
377+ * @param documents The documents associated with the vectors.
378+ * @returns A promise that resolves when the documents have been added.
379+ */
380+ async addDocumentsWithVectors (
381+ vectors : number [ ] [ ] ,
382+ documents : Document < TModel > [ ]
383+ ) {
384+ // table name, column name cannot be parametrised
385+ // these fields are thus not escaped by Prisma and can be dangerous if user input is used
386+ const tableNameRaw = this . Prisma . raw ( `"${ this . tableName } "` ) ;
387+ const vectorColumnRaw = this . Prisma . raw ( `"${ this . vectorColumnName } "` ) ;
388+
389+ // Build column names for INSERT statement
390+ const columnNames = this . selectColumns . map ( ( col ) =>
391+ this . Prisma . raw ( `"${ col } "` )
392+ ) ;
393+ const allColumns = [ ...columnNames , vectorColumnRaw ] ;
394+
395+ await this . db . $transaction (
396+ vectors . map ( ( vector , idx ) => {
397+ const document = documents [ idx ] ;
398+ const vectorString = `[${ vector . join ( "," ) } ]` ;
399+
400+ // Build values for each column
401+ const columnValues = this . selectColumns . map ( ( col ) => {
402+ if ( col === this . contentColumn ) {
403+ return document . pageContent ;
404+ }
405+ return document . metadata [ col ] ;
406+ } ) ;
407+
408+ // Add vector as the last value
409+ const allValues = [
410+ ...columnValues ,
411+ this . Prisma . sql `${ vectorString } ::vector` ,
412+ ] ;
413+
414+ return this . db . $executeRaw (
415+ this . Prisma . sql `
416+ INSERT INTO ${ tableNameRaw } (${ this . Prisma . join ( allColumns , ", " ) } )
417+ VALUES (${ this . Prisma . join ( allValues , ", " ) } )
418+ `
419+ ) ;
420+ } )
421+ ) ;
422+ }
423+
353424 /**
354425 * Performs a similarity search with the specified query.
355426 * @param query The query to use for the similarity search.
@@ -572,6 +643,7 @@ export class PrismaVectorStore<
572643 vectorColumnName : string ;
573644 columns : ModelColumns < Record < string , unknown > > ;
574645 columnTypes ?: ColumnTypeConfig ;
646+ useInsert ?: boolean ;
575647 }
576648 ) : Promise < DefaultPrismaVectorStore > {
577649 const docs : Document [ ] = [ ] ;
@@ -604,6 +676,7 @@ export class PrismaVectorStore<
604676 vectorColumnName : string ;
605677 columns : ModelColumns < Record < string , unknown > > ;
606678 columnTypes ?: ColumnTypeConfig ;
679+ useInsert ?: boolean ;
607680 }
608681 ) : Promise < DefaultPrismaVectorStore > {
609682 const instance = new PrismaVectorStore ( embeddings , dbConfig ) ;
0 commit comments