@@ -253,6 +253,72 @@ def test_expire_aggregate(env):
253253 # The result count is not accurate in aggregation, for now we compare res to the expected results with the wrong count
254254 env .assertEqual (res , [1 , ['t' , 'arr' ], ['t' , 'bar' ]])
255255
256+
257+ def expire_ft_hybrid_test (protocol ):
258+ env = Env (protocol = protocol )
259+ # Use "lazy" expire (expire only when key is accessed) on all shards
260+ env .cmd ('DEBUG' , 'SET-ACTIVE-EXPIRE' , '0' )
261+
262+ # Create index with text, vector, and numeric fields
263+ env .expect ('FT.CREATE' , 'idx' , 'SCHEMA' , 't' , 'TEXT' , 'n' , 'NUMERIC' , 'v' , 'VECTOR' , 'FLAT' , '6' , 'TYPE' , 'FLOAT32' , 'DIM' , '2' , 'DISTANCE_METRIC' , 'L2' ).ok ()
264+
265+ # Create test vectors (2-dimensional float32)
266+ import numpy as np
267+ query_vector = np .array ([0.5 , 0.5 ]).astype (np .float32 ).tobytes ()
268+
269+ # Use cluster-aware connection for data insertion
270+ with env .getClusterConnectionIfNeeded () as conn :
271+ # Create 1000 documents
272+ for i in range (1000 ):
273+ # Create a unique vector for each document
274+ vector = np .array ([float (i % 100 ) / 100.0 , float ((i + 1 ) % 100 ) / 100.0 ]).astype (np .float32 ).tobytes ()
275+ doc_key = f'doc{ i } '
276+ text_value = f'text{ i } '
277+ numeric_value = str (i )
278+
279+ conn .execute_command ('HSET' , doc_key , 't' , text_value , 'n' , numeric_value , 'v' , vector )
280+
281+ # Expire the first 990 documents (doc0 to doc989)
282+ if i < 990 :
283+ conn .execute_command ('PEXPIRE' , doc_key , 1 )
284+
285+ # Ensure expiration before query
286+ time .sleep (0.01 )
287+
288+ # Test FT.HYBRID requesting 1000 results but expecting only 10 (non-expired documents)
289+ hybrid_query = ['FT.HYBRID' , 'idx' , 'SEARCH' , '*' , 'VSIM' , '@v' , query_vector , 'LIMIT' , '0' , '1000' , 'COMBINE' , 'RRF' , '2' , 'CONSTANT' , '60' , 'LOAD' , '4' , '@__key' , '@__score' , '@t' , '@n' ]
290+
291+ # Execute query using cluster-aware command to get expected results
292+ actual_res = env .cmd (* hybrid_query )
293+ from common import get_results_from_hybrid_response
294+ actual_results_dict , actual_total_results = get_results_from_hybrid_response (actual_res )
295+
296+ # Validate that only 10 documents are returned (doc990 to doc999)
297+ env .assertEqual (actual_total_results , 10 )
298+
299+ # Verify that only non-expired documents are present
300+ expected_doc_keys = {f'doc{ i } ' for i in range (990 , 1000 )}
301+ actual_doc_keys = set (actual_results_dict .keys ())
302+ env .assertEqual (actual_doc_keys , expected_doc_keys )
303+
304+ # Verify that each returned document has the correct attributes
305+ for doc_key in actual_results_dict :
306+ doc_num = int (doc_key [3 :]) # Extract number from 'docXXX'
307+ env .assertTrue ('__key' in actual_results_dict [doc_key ])
308+ env .assertTrue ('__score' in actual_results_dict [doc_key ])
309+ env .assertTrue ('t' in actual_results_dict [doc_key ])
310+ env .assertTrue ('n' in actual_results_dict [doc_key ])
311+ env .assertEqual (actual_results_dict [doc_key ]['__key' ], doc_key )
312+ env .assertEqual (actual_results_dict [doc_key ]['t' ], f'text{ doc_num } ' )
313+ env .assertEqual (actual_results_dict [doc_key ]['n' ], str (doc_num ))
314+ env .assertTrue (float (actual_results_dict [doc_key ]['__score' ]) >= 0 )
315+
316+ def test_expire_ft_hybrid_resp2 ():
317+ expire_ft_hybrid_test (protocol = 2 )
318+
319+ def test_expire_ft_hybrid_resp3 ():
320+ expire_ft_hybrid_test (protocol = 3 )
321+
256322def createTextualSchema (field_to_additional_schema_keywords ):
257323 schema = []
258324 for field , additional_schema_words in field_to_additional_schema_keywords .items ():
0 commit comments