@@ -10,6 +10,7 @@ import { knex, Knex } from 'knex';
1010import nullthrows from 'nullthrows' ;
1111import { setTimeout } from 'timers/promises' ;
1212
13+ import { SearchStrategy } from '../AuthorizationResultBasedKnexEntityLoader' ;
1314import { OrderByOrdering } from '../BasePostgresEntityDatabaseAdapter' ;
1415import { raw , sql , SQLFragment , SQLFragmentHelpers } from '../SQLOperator' ;
1516import { PostgresTestEntity } from '../__testfixtures__/PostgresTestEntity' ;
@@ -1913,5 +1914,182 @@ describe('postgres entity integration', () => {
19131914 expect ( emptyBackwardPageWithTotal . pageInfo . startCursor ) . toBeNull ( ) ;
19141915 expect ( emptyBackwardPageWithTotal . pageInfo . endCursor ) . toBeNull ( ) ;
19151916 } ) ;
1917+
1918+ it ( 'supports search with ILIKE strategy' , async ( ) => {
1919+ const vc = new ViewerContext ( createKnexIntegrationTestEntityCompanionProvider ( knexInstance ) ) ;
1920+
1921+ await PostgresTestEntity . dropPostgresTableAsync ( knexInstance ) ;
1922+ await PostgresTestEntity . createOrTruncatePostgresTableAsync ( knexInstance ) ;
1923+
1924+ // Create test data with searchable names
1925+ const names = [
1926+ 'Alice Johnson' ,
1927+ 'Bob Smith' ,
1928+ 'Charlie Brown' ,
1929+ 'David Smith' ,
1930+ 'Eve Johnson' ,
1931+ 'Frank Miller' ,
1932+ ] ;
1933+ for ( let i = 0 ; i < names . length ; i ++ ) {
1934+ await PostgresTestEntity . creator ( vc )
1935+ . setField ( 'name' , names [ i ] ! )
1936+ . setField ( 'hasACat' , i % 2 === 0 )
1937+ . createAsync ( ) ;
1938+ }
1939+
1940+ // Search for names containing "Johnson"
1941+ const searchResults = await PostgresTestEntity . knexLoader ( vc ) . loadPageBySQLAsync ( {
1942+ first : 10 ,
1943+ search : {
1944+ strategy : SearchStrategy . ILIKE ,
1945+ term : 'Johnson' ,
1946+ fields : [ 'name' ] ,
1947+ } ,
1948+ orderBy : [ { fieldName : 'name' , order : OrderByOrdering . ASCENDING } ] ,
1949+ } ) ;
1950+
1951+ expect ( searchResults . edges ) . toHaveLength ( 2 ) ;
1952+ expect ( searchResults . edges [ 0 ] ?. node . getField ( 'name' ) ) . toBe ( 'Alice Johnson' ) ;
1953+ expect ( searchResults . edges [ 1 ] ?. node . getField ( 'name' ) ) . toBe ( 'Eve Johnson' ) ;
1954+
1955+ // Search for names containing "Smith" with pagination
1956+ const smithPage1 = await PostgresTestEntity . knexLoader ( vc ) . loadPageBySQLAsync ( {
1957+ first : 1 ,
1958+ search : {
1959+ strategy : SearchStrategy . ILIKE ,
1960+ term : 'Smith' ,
1961+ fields : [ 'name' ] ,
1962+ } ,
1963+ orderBy : [ { fieldName : 'name' , order : OrderByOrdering . ASCENDING } ] ,
1964+ } ) ;
1965+
1966+ expect ( smithPage1 . edges ) . toHaveLength ( 1 ) ;
1967+ expect ( smithPage1 . edges [ 0 ] ?. node . getField ( 'name' ) ) . toBe ( 'Bob Smith' ) ;
1968+ expect ( smithPage1 . pageInfo . hasNextPage ) . toBe ( true ) ;
1969+
1970+ // Get next page
1971+ const smithPage2 = await PostgresTestEntity . knexLoader ( vc ) . loadPageBySQLAsync ( {
1972+ first : 1 ,
1973+ after : smithPage1 . pageInfo . endCursor ! ,
1974+ search : {
1975+ strategy : SearchStrategy . ILIKE ,
1976+ term : 'Smith' ,
1977+ fields : [ 'name' ] ,
1978+ } ,
1979+ orderBy : [ { fieldName : 'name' , order : OrderByOrdering . ASCENDING } ] ,
1980+ } ) ;
1981+
1982+ expect ( smithPage2 . edges ) . toHaveLength ( 1 ) ;
1983+ expect ( smithPage2 . edges [ 0 ] ?. node . getField ( 'name' ) ) . toBe ( 'David Smith' ) ;
1984+ expect ( smithPage2 . pageInfo . hasNextPage ) . toBe ( false ) ;
1985+
1986+ // Test partial match (case insensitive)
1987+ const partialMatch = await PostgresTestEntity . knexLoader ( vc ) . loadPageBySQLAsync ( {
1988+ first : 10 ,
1989+ search : {
1990+ strategy : SearchStrategy . ILIKE ,
1991+ term : 'john' ,
1992+ fields : [ 'name' ] ,
1993+ } ,
1994+ orderBy : [ { fieldName : 'name' , order : OrderByOrdering . ASCENDING } ] ,
1995+ } ) ;
1996+
1997+ expect ( partialMatch . edges ) . toHaveLength ( 2 ) ;
1998+ expect ( partialMatch . edges [ 0 ] ?. node . getField ( 'name' ) ) . toBe ( 'Alice Johnson' ) ;
1999+ expect ( partialMatch . edges [ 1 ] ?. node . getField ( 'name' ) ) . toBe ( 'Eve Johnson' ) ;
2000+
2001+ // Test search with WHERE clause
2002+ const combinedFilter = await PostgresTestEntity . knexLoader ( vc ) . loadPageBySQLAsync ( {
2003+ first : 10 ,
2004+ where : sql `has_a_cat = ${ true } ` ,
2005+ search : {
2006+ strategy : SearchStrategy . ILIKE ,
2007+ term : 'Johnson' ,
2008+ fields : [ 'name' ] ,
2009+ } ,
2010+ orderBy : [ { fieldName : 'name' , order : OrderByOrdering . ASCENDING } ] ,
2011+ } ) ;
2012+
2013+ // Both Alice Johnson (index 0) and Eve Johnson (index 4) have cats
2014+ expect ( combinedFilter . edges ) . toHaveLength ( 2 ) ;
2015+ expect ( combinedFilter . edges [ 0 ] ?. node . getField ( 'name' ) ) . toBe ( 'Alice Johnson' ) ;
2016+ expect ( combinedFilter . edges [ 0 ] ?. node . getField ( 'hasACat' ) ) . toBe ( true ) ;
2017+ expect ( combinedFilter . edges [ 1 ] ?. node . getField ( 'name' ) ) . toBe ( 'Eve Johnson' ) ;
2018+ expect ( combinedFilter . edges [ 1 ] ?. node . getField ( 'hasACat' ) ) . toBe ( true ) ;
2019+
2020+ // Test search with includeTotal
2021+ const withTotal = await PostgresTestEntity . knexLoader ( vc ) . loadPageBySQLAsync ( {
2022+ first : 1 ,
2023+ search : {
2024+ strategy : SearchStrategy . ILIKE ,
2025+ term : 'Smith' ,
2026+ fields : [ 'name' ] ,
2027+ } ,
2028+ orderBy : [ { fieldName : 'name' , order : OrderByOrdering . ASCENDING } ] ,
2029+ includeTotal : true ,
2030+ } ) ;
2031+
2032+ expect ( withTotal . edges ) . toHaveLength ( 1 ) ;
2033+ expect ( withTotal . totalCount ) . toBe ( 2 ) ; // Bob Smith and David Smith
2034+ } ) ;
2035+
2036+ it ( 'supports trigram similarity search' , async ( ) => {
2037+ const vc = new ViewerContext ( createKnexIntegrationTestEntityCompanionProvider ( knexInstance ) ) ;
2038+
2039+ await PostgresTestEntity . dropPostgresTableAsync ( knexInstance ) ;
2040+ await PostgresTestEntity . createOrTruncatePostgresTableAsync ( knexInstance ) ;
2041+
2042+ // Enable pg_trgm extension for trigram similarity
2043+ await knexInstance . raw ( 'CREATE EXTENSION IF NOT EXISTS pg_trgm' ) ;
2044+
2045+ // Create test data with similar names
2046+ const names = [ 'Johnson' , 'Jonson' , 'Johnsen' , 'Smith' , 'Smyth' , 'Schmidt' ] ;
2047+ for ( let i = 0 ; i < names . length ; i ++ ) {
2048+ await PostgresTestEntity . creator ( vc )
2049+ . setField ( 'name' , names [ i ] ! )
2050+ . setField ( 'hasACat' , i < 3 ) // First 3 have cats
2051+ . createAsync ( ) ;
2052+ }
2053+
2054+ // Search for similar names to "Johnson" using trigram
2055+ const trigramSearch = await PostgresTestEntity . knexLoader ( vc ) . loadPageBySQLAsync ( {
2056+ first : 10 ,
2057+ search : {
2058+ strategy : SearchStrategy . TRIGRAM ,
2059+ term : 'Johnson' ,
2060+ fields : [ 'name' ] ,
2061+ threshold : 0.3 , // Similarity threshold
2062+ } ,
2063+ } ) ;
2064+
2065+ // Should find exact match and similar names, ordered by relevance
2066+ expect ( trigramSearch . edges . length ) . toBeGreaterThan ( 0 ) ;
2067+ // Exact match should come first due to ILIKE matching
2068+ expect ( trigramSearch . edges [ 0 ] ?. node . getField ( 'name' ) ) . toBe ( 'Johnson' ) ;
2069+
2070+ // The similar names (Jonson, Johnsen) should also be included
2071+ const foundNames = trigramSearch . edges . map ( ( e ) => e . node . getField ( 'name' ) ) ;
2072+ expect ( foundNames ) . toContain ( 'Jonson' ) ;
2073+ expect ( foundNames ) . toContain ( 'Johnsen' ) ;
2074+
2075+ // Test combining with WHERE clause
2076+ const filteredTrigram = await PostgresTestEntity . knexLoader ( vc ) . loadPageBySQLAsync ( {
2077+ first : 10 ,
2078+ where : sql `has_a_cat = ${ true } ` ,
2079+ search : {
2080+ strategy : SearchStrategy . TRIGRAM ,
2081+ term : 'Johnson' ,
2082+ fields : [ 'name' ] ,
2083+ threshold : 0.3 ,
2084+ } ,
2085+ } ) ;
2086+
2087+ // Only the Johnson-like names with cats
2088+ expect ( filteredTrigram . edges . length ) . toBeGreaterThan ( 0 ) ;
2089+ expect ( filteredTrigram . edges . length ) . toBeLessThanOrEqual ( 3 ) ;
2090+ filteredTrigram . edges . forEach ( ( edge ) => {
2091+ expect ( edge . node . getField ( 'hasACat' ) ) . toBe ( true ) ;
2092+ } ) ;
2093+ } ) ;
19162094 } ) ;
19172095} ) ;
0 commit comments