@@ -106,11 +106,11 @@ rating int
106106
107107 sql . ShouldBe (
108108 """
109- CREATE INDEX indexing_items_idx ON indexing_items USING bm25 (id, (description::pdb.ngram(3,3,'positions=true')), ((metadata ->> 'color')::pdb.literal('alias=metadata_color')), (rating::pdb.alias('my_rating_alias')), ((rating + 1)::pdb.alias('escape'' me'))) WITH (key_field = 'id', search_tokenizer = 'simple(lowercase=false)') WHERE rating > 0;
109+ CREATE INDEX indexing_items_idx ON indexing_items USING paradedb (id, (description::pdb.ngram(3,3,'positions=true')), ((metadata ->> 'color')::pdb.literal('alias=metadata_color')), (rating::pdb.alias('my_rating_alias')), ((rating + 1)::pdb.alias('escape'' me'))) WITH (key_field = 'id', search_tokenizer = 'simple(lowercase=false)') WHERE rating > 0;
110110
111111 """
112112 ) ;
113- await context . Database . ExecuteSqlRawAsync ( sql ) ;
113+ await ExecuteCreateIndexSqlAsync ( context , sql ) ;
114114 }
115115
116116 private sealed class SimpleIndexContext ( DbContextOptions < SimpleIndexContext > options )
@@ -155,11 +155,11 @@ rating int
155155
156156 sql . ShouldBe (
157157 """
158- CREATE INDEX CONCURRENTLY indexing_items_idx ON indexing_items USING bm25 (id, description, metadata) WITH (key_field = 'id');
158+ CREATE INDEX CONCURRENTLY indexing_items_idx ON indexing_items USING paradedb (id, description, metadata) WITH (key_field = 'id');
159159
160160 """
161161 ) ;
162- await context . Database . ExecuteSqlRawAsync ( sql ) ;
162+ await ExecuteCreateIndexSqlAsync ( context , sql ) ;
163163 }
164164
165165 private sealed class ArrayExpressionIndexContext (
@@ -215,11 +215,11 @@ tags varchar(255)[]
215215
216216 sql . ShouldBe (
217217 """
218- CREATE INDEX indexing_items_idx ON indexing_items USING bm25 (id, categories, (tags::pdb.literal), ((description || ' ' || category)::pdb.simple('alias=description_concat')), (description::pdb.literal), (description::pdb.simple('alias=description_simple'))) WITH (key_field = 'id');
218+ CREATE INDEX indexing_items_idx ON indexing_items USING paradedb (id, categories, (tags::pdb.literal), ((description || ' ' || category)::pdb.simple('alias=description_concat')), (description::pdb.literal), (description::pdb.simple('alias=description_simple'))) WITH (key_field = 'id');
219219
220220 """
221221 ) ;
222- await context . Database . ExecuteSqlRawAsync ( sql ) ;
222+ await ExecuteCreateIndexSqlAsync ( context , sql ) ;
223223 }
224224
225225 public static IEnumerable < (
@@ -317,11 +317,140 @@ description text
317317 var sql = GenerateSearchTokenizerCreateIndexSql ( tokenizer ) ;
318318
319319 sql . ShouldBe (
320- $ "CREATE INDEX indexing_items_idx ON indexing_items USING bm25 (id, description) WITH (key_field = 'id', search_tokenizer = '{ expectedSearchTokenizer } ');\n "
320+ $ "CREATE INDEX indexing_items_idx ON indexing_items USING paradedb (id, description) WITH (key_field = 'id', search_tokenizer = '{ expectedSearchTokenizer } ');\n "
321+ ) ;
322+ await ExecuteCreateIndexSqlAsync ( context , sql ) ;
323+ }
324+
325+ private sealed class Bm25MethodIndexContext ( DbContextOptions < Bm25MethodIndexContext > options )
326+ : DbContext ( options )
327+ {
328+ protected override void OnModelCreating ( ModelBuilder modelBuilder )
329+ {
330+ modelBuilder . Entity < IndexingItem > ( entity =>
331+ {
332+ entity . ToTable ( "indexing_items" ) ;
333+ entity . Property ( e => e . Id ) . HasColumnName ( "id" ) ;
334+ entity . Property ( e => e . Description ) . HasColumnName ( "description" ) ;
335+
336+ entity
337+ . HasBm25Index ( "indexing_items_idx" , e => e . Id )
338+ . HasMethod ( "bm25" )
339+ . HasField ( e => e . Description ) ;
340+ } ) ;
341+ }
342+ }
343+
344+ [ Test ]
345+ public async Task Bm25Index_WithExplicitBm25Method ( )
346+ {
347+ await using var context = DbFixture . CreateContext ( ) ;
348+ await context . Database . OpenConnectionAsync ( ) ;
349+ await context . Database . ExecuteSqlRawAsync (
350+ """
351+ CREATE TEMP TABLE indexing_items (
352+ id int PRIMARY KEY,
353+ description text
354+ );
355+ """
356+ ) ;
357+
358+ var sql = GenerateCreateIndexSql < Bm25MethodIndexContext , IndexingItem > ( ) ;
359+
360+ sql . ShouldBe (
361+ """
362+ CREATE INDEX indexing_items_idx ON indexing_items USING bm25 (id, description) WITH (key_field = 'id');
363+
364+ """
365+ ) ;
366+ await context . Database . ExecuteSqlRawAsync ( sql ) ;
367+ }
368+
369+ private sealed class ParadeDbMethodIndexContext (
370+ DbContextOptions < ParadeDbMethodIndexContext > options
371+ ) : DbContext ( options )
372+ {
373+ protected override void OnModelCreating ( ModelBuilder modelBuilder )
374+ {
375+ modelBuilder . Entity < IndexingItem > ( entity =>
376+ {
377+ entity . ToTable ( "indexing_items" ) ;
378+ entity . Property ( e => e . Id ) . HasColumnName ( "id" ) ;
379+ entity . Property ( e => e . Description ) . HasColumnName ( "description" ) ;
380+
381+ entity
382+ . HasBm25Index ( "indexing_items_idx" , e => e . Id )
383+ . HasMethod ( "paradedb" )
384+ . HasField ( e => e . Description ) ;
385+ } ) ;
386+ }
387+ }
388+
389+ [ Test ]
390+ public async Task Bm25Index_WithExplicitParadeDbMethod ( )
391+ {
392+ var sql = GenerateCreateIndexSql < ParadeDbMethodIndexContext , IndexingItem > ( ) ;
393+
394+ sql . ShouldBe (
395+ """
396+ CREATE INDEX indexing_items_idx ON indexing_items USING paradedb (id, description) WITH (key_field = 'id');
397+
398+ """
399+ ) ;
400+
401+ Skip . When (
402+ ! DbFixture . SupportsParadeDbAm ,
403+ "This ParadeDB version does not have the 'paradedb' index access method (requires pg_search 0.25.0+, unreleased)"
404+ ) ;
405+
406+ await using var context = DbFixture . CreateContext ( ) ;
407+ await context . Database . OpenConnectionAsync ( ) ;
408+ await context . Database . ExecuteSqlRawAsync (
409+ """
410+ CREATE TEMP TABLE indexing_items (
411+ id int PRIMARY KEY,
412+ description text
413+ );
414+ """
321415 ) ;
322416 await context . Database . ExecuteSqlRawAsync ( sql ) ;
323417 }
324418
419+ private sealed class InvalidMethodIndexContext (
420+ DbContextOptions < InvalidMethodIndexContext > options
421+ ) : DbContext ( options )
422+ {
423+ protected override void OnModelCreating ( ModelBuilder modelBuilder )
424+ {
425+ modelBuilder . Entity < IndexingItem > ( entity =>
426+ {
427+ entity . ToTable ( "indexing_items" ) ;
428+ entity . Property ( e => e . Id ) . HasColumnName ( "id" ) ;
429+ entity . Property ( e => e . Description ) . HasColumnName ( "description" ) ;
430+
431+ entity
432+ . HasBm25Index ( "indexing_items_idx" , e => e . Id )
433+ . HasMethod ( "gin" )
434+ . HasField ( e => e . Description ) ;
435+ } ) ;
436+ }
437+ }
438+
439+ [ Test ]
440+ public void Bm25Index_RejectsUnknownMethod ( )
441+ {
442+ var exception = Should . Throw < ArgumentException > ( ( ) =>
443+ GenerateCreateIndexSql < InvalidMethodIndexContext , IndexingItem > ( )
444+ ) ;
445+
446+ exception . Message . ShouldContain ( "'paradedb' or 'bm25'" ) ;
447+ }
448+
449+ private Task ExecuteCreateIndexSqlAsync ( Persistence . TestDbContext context , string sql ) =>
450+ context . Database . ExecuteSqlRawAsync (
451+ sql . Replace ( " USING paradedb " , $ " USING { DbFixture . AccessMethod } ")
452+ ) ;
453+
325454 private static string GenerateCreateIndexSql < TContext , TEntity > ( )
326455 where TContext : DbContext
327456 {
0 commit comments