@@ -5972,6 +5972,332 @@ private static Collection getFlatCollection(String dataStoreName) {
59725972 return datastore .getCollectionForType (FLAT_COLLECTION_NAME , DocumentType .FLAT );
59735973 }
59745974
5975+ @ Nested
5976+ class FlatCollectionLegacySearchMethod {
5977+
5978+ @ ParameterizedTest
5979+ @ ArgumentsSource (PostgresProvider .class )
5980+ void testSearchWithNoFilter (String dataStoreName ) {
5981+ Collection flatCollection = getFlatCollection (dataStoreName );
5982+
5983+ // Test legacy search() method with no filter - should return all documents
5984+ org .hypertrace .core .documentstore .Query legacyQuery =
5985+ new org .hypertrace .core .documentstore .Query ();
5986+
5987+ Iterator <Document > results = flatCollection .search (legacyQuery );
5988+ int count = 0 ;
5989+ while (results .hasNext ()) {
5990+ results .next ();
5991+ count ++;
5992+ }
5993+ assertEquals (10 , count ); // All 10 documents in flat collection
5994+ }
5995+
5996+ @ ParameterizedTest
5997+ @ ArgumentsSource (PostgresProvider .class )
5998+ void testSearchWithEqFilter (String dataStoreName ) throws JsonProcessingException {
5999+ Collection flatCollection = getFlatCollection (dataStoreName );
6000+
6001+ // Test legacy search() with EQ filter on scalar column
6002+ org .hypertrace .core .documentstore .Query legacyQuery =
6003+ new org .hypertrace .core .documentstore .Query ()
6004+ .withFilter (
6005+ new org .hypertrace .core .documentstore .Filter (
6006+ org .hypertrace .core .documentstore .Filter .Op .EQ , "item" , "Soap" ));
6007+
6008+ Iterator <Document > results = flatCollection .search (legacyQuery );
6009+ int count = 0 ;
6010+ while (results .hasNext ()) {
6011+ Document doc = results .next ();
6012+ JsonNode json = new ObjectMapper ().readTree (doc .toJson ());
6013+ assertEquals ("Soap" , json .get ("item" ).asText ());
6014+ count ++;
6015+ }
6016+ assertEquals (3 , count ); // 3 Soap items (IDs 1, 5, 8)
6017+ }
6018+
6019+ @ ParameterizedTest
6020+ @ ArgumentsSource (PostgresProvider .class )
6021+ void testSearchWithInFilter (String dataStoreName ) {
6022+ Collection flatCollection = getFlatCollection (dataStoreName );
6023+
6024+ // Test legacy search() with IN filter
6025+ org .hypertrace .core .documentstore .Query legacyQuery =
6026+ new org .hypertrace .core .documentstore .Query ()
6027+ .withFilter (
6028+ new org .hypertrace .core .documentstore .Filter (
6029+ org .hypertrace .core .documentstore .Filter .Op .IN ,
6030+ "item" ,
6031+ List .of ("Soap" , "Mirror" )));
6032+
6033+ Iterator <Document > results = flatCollection .search (legacyQuery );
6034+ int count = 0 ;
6035+ while (results .hasNext ()) {
6036+ results .next ();
6037+ count ++;
6038+ }
6039+ assertEquals (4 , count ); // 3 Soap + 1 Mirror = 4
6040+ }
6041+
6042+ @ ParameterizedTest
6043+ @ ArgumentsSource (PostgresProvider .class )
6044+ void testSearchWithNumericFilter (String dataStoreName ) throws JsonProcessingException {
6045+ Collection flatCollection = getFlatCollection (dataStoreName );
6046+
6047+ // Test legacy search() with GT filter on integer column
6048+ org .hypertrace .core .documentstore .Query legacyQuery =
6049+ new org .hypertrace .core .documentstore .Query ()
6050+ .withFilter (
6051+ new org .hypertrace .core .documentstore .Filter (
6052+ org .hypertrace .core .documentstore .Filter .Op .GT , "price" , 15 ));
6053+
6054+ Iterator <Document > results = flatCollection .search (legacyQuery );
6055+ int count = 0 ;
6056+ while (results .hasNext ()) {
6057+ Document doc = results .next ();
6058+ JsonNode json = new ObjectMapper ().readTree (doc .toJson ());
6059+ assertTrue (json .get ("price" ).asInt () > 15 );
6060+ count ++;
6061+ }
6062+ assertTrue (count > 0 );
6063+ }
6064+
6065+ @ ParameterizedTest
6066+ @ ArgumentsSource (PostgresProvider .class )
6067+ void testSearchWithCompositeAndFilter (String dataStoreName ) throws JsonProcessingException {
6068+ Collection flatCollection = getFlatCollection (dataStoreName );
6069+
6070+ // Test legacy search() with composite AND filter
6071+ org .hypertrace .core .documentstore .Filter itemFilter =
6072+ new org .hypertrace .core .documentstore .Filter (
6073+ org .hypertrace .core .documentstore .Filter .Op .EQ , "item" , "Soap" );
6074+ org .hypertrace .core .documentstore .Filter priceFilter =
6075+ new org .hypertrace .core .documentstore .Filter (
6076+ org .hypertrace .core .documentstore .Filter .Op .GTE , "price" , 10 );
6077+
6078+ org .hypertrace .core .documentstore .Filter compositeFilter =
6079+ new org .hypertrace .core .documentstore .Filter ();
6080+ compositeFilter .setOp (org .hypertrace .core .documentstore .Filter .Op .AND );
6081+ compositeFilter .setChildFilters (
6082+ new org .hypertrace .core .documentstore .Filter [] {itemFilter , priceFilter });
6083+
6084+ org .hypertrace .core .documentstore .Query legacyQuery =
6085+ new org .hypertrace .core .documentstore .Query ().withFilter (compositeFilter );
6086+
6087+ Iterator <Document > results = flatCollection .search (legacyQuery );
6088+ int count = 0 ;
6089+ while (results .hasNext ()) {
6090+ Document doc = results .next ();
6091+ JsonNode json = new ObjectMapper ().readTree (doc .toJson ());
6092+ assertEquals ("Soap" , json .get ("item" ).asText ());
6093+ assertTrue (json .get ("price" ).asInt () >= 10 );
6094+ count ++;
6095+ }
6096+ assertTrue (count > 0 );
6097+ }
6098+
6099+ @ ParameterizedTest
6100+ @ ArgumentsSource (PostgresProvider .class )
6101+ void testSearchWithOrderBy (String dataStoreName ) throws JsonProcessingException {
6102+ Collection flatCollection = getFlatCollection (dataStoreName );
6103+
6104+ // Test legacy search() with ORDER BY
6105+ org .hypertrace .core .documentstore .Query legacyQuery =
6106+ new org .hypertrace .core .documentstore .Query ()
6107+ .withOrderBy (new OrderBy ("price" , true )); // ASC
6108+
6109+ Iterator <Document > results = flatCollection .search (legacyQuery );
6110+ int previousPrice = Integer .MIN_VALUE ;
6111+ int count = 0 ;
6112+ while (results .hasNext ()) {
6113+ Document doc = results .next ();
6114+ JsonNode json = new ObjectMapper ().readTree (doc .toJson ());
6115+ int currentPrice = json .get ("price" ).asInt ();
6116+ assertTrue (currentPrice >= previousPrice , "Results should be sorted by price ASC" );
6117+ previousPrice = currentPrice ;
6118+ count ++;
6119+ }
6120+ assertEquals (10 , count );
6121+ }
6122+
6123+ @ ParameterizedTest
6124+ @ ArgumentsSource (PostgresProvider .class )
6125+ void testSearchWithPagination (String dataStoreName ) {
6126+ Collection flatCollection = getFlatCollection (dataStoreName );
6127+
6128+ // Test legacy search() with LIMIT and OFFSET
6129+ org .hypertrace .core .documentstore .Query legacyQuery =
6130+ new org .hypertrace .core .documentstore .Query ().withLimit (3 ).withOffset (2 );
6131+
6132+ Iterator <Document > results = flatCollection .search (legacyQuery );
6133+ int count = 0 ;
6134+ while (results .hasNext ()) {
6135+ results .next ();
6136+ count ++;
6137+ }
6138+ assertEquals (3 , count ); // Should return exactly 3 documents
6139+ }
6140+
6141+ @ ParameterizedTest
6142+ @ ArgumentsSource (PostgresProvider .class )
6143+ void testSearchWithSelections (String dataStoreName ) throws JsonProcessingException {
6144+ Collection flatCollection = getFlatCollection (dataStoreName );
6145+
6146+ // Test legacy search() with selections
6147+ org .hypertrace .core .documentstore .Query legacyQuery =
6148+ new org .hypertrace .core .documentstore .Query ()
6149+ .withSelection ("item" )
6150+ .withSelection ("price" )
6151+ .withLimit (5 );
6152+
6153+ Iterator <Document > results = flatCollection .search (legacyQuery );
6154+ int count = 0 ;
6155+ while (results .hasNext ()) {
6156+ Document doc = results .next ();
6157+ JsonNode json = new ObjectMapper ().readTree (doc .toJson ());
6158+ // Should have item and price fields
6159+ assertNotNull (json .get ("item" ));
6160+ assertNotNull (json .get ("price" ));
6161+ count ++;
6162+ }
6163+ assertEquals (5 , count );
6164+ }
6165+
6166+ @ ParameterizedTest
6167+ @ ArgumentsSource (PostgresProvider .class )
6168+ void testSearchWithJsonbFilter (String dataStoreName ) throws JsonProcessingException {
6169+ Collection flatCollection = getFlatCollection (dataStoreName );
6170+
6171+ // Test legacy search() with filter on JSONB nested field (props.brand)
6172+ org .hypertrace .core .documentstore .Query legacyQuery =
6173+ new org .hypertrace .core .documentstore .Query ()
6174+ .withFilter (
6175+ new org .hypertrace .core .documentstore .Filter (
6176+ org .hypertrace .core .documentstore .Filter .Op .EQ , "props.brand" , "Dettol" ));
6177+
6178+ Iterator <Document > results = flatCollection .search (legacyQuery );
6179+ int count = 0 ;
6180+ while (results .hasNext ()) {
6181+ Document doc = results .next ();
6182+ JsonNode json = new ObjectMapper ().readTree (doc .toJson ());
6183+ JsonNode props = json .get ("props" );
6184+ assertNotNull (props );
6185+ assertEquals ("Dettol" , props .get ("brand" ).asText ());
6186+ count ++;
6187+ }
6188+ assertEquals (1 , count ); // Only 1 document with brand=Dettol
6189+ }
6190+
6191+ @ ParameterizedTest
6192+ @ ArgumentsSource (PostgresProvider .class )
6193+ void testSearchCompleteQuery (String dataStoreName ) throws JsonProcessingException {
6194+ Collection flatCollection = getFlatCollection (dataStoreName );
6195+
6196+ // Test legacy search() with filter, orderBy, selections, and pagination
6197+ org .hypertrace .core .documentstore .Query legacyQuery =
6198+ new org .hypertrace .core .documentstore .Query ()
6199+ .withFilter (
6200+ new org .hypertrace .core .documentstore .Filter (
6201+ org .hypertrace .core .documentstore .Filter .Op .GTE , "price" , 5 ))
6202+ .withSelection ("item" )
6203+ .withSelection ("price" )
6204+ .withOrderBy (new OrderBy ("price" , false )) // DESC
6205+ .withLimit (5 )
6206+ .withOffset (0 );
6207+
6208+ Iterator <Document > results = flatCollection .search (legacyQuery );
6209+ int previousPrice = Integer .MAX_VALUE ;
6210+ int count = 0 ;
6211+ while (results .hasNext ()) {
6212+ Document doc = results .next ();
6213+ JsonNode json = new ObjectMapper ().readTree (doc .toJson ());
6214+ int currentPrice = json .get ("price" ).asInt ();
6215+ assertTrue (currentPrice >= 5 , "Price should be >= 5" );
6216+ assertTrue (currentPrice <= previousPrice , "Results should be sorted by price DESC" );
6217+ previousPrice = currentPrice ;
6218+ count ++;
6219+ }
6220+ assertEquals (5 , count );
6221+ }
6222+
6223+ @ ParameterizedTest
6224+ @ ArgumentsSource (PostgresProvider .class )
6225+ void testSearchWithFilterSelectionsOrderByAndOffset (String dataStoreName )
6226+ throws JsonProcessingException {
6227+ Collection flatCollection = getFlatCollection (dataStoreName );
6228+
6229+ // Test covering: filter, selections, orderBy, and offset (pagination)
6230+ org .hypertrace .core .documentstore .Query legacyQuery =
6231+ new org .hypertrace .core .documentstore .Query ()
6232+ .withFilter (
6233+ new org .hypertrace .core .documentstore .Filter (
6234+ org .hypertrace .core .documentstore .Filter .Op .GTE , "price" , 5 ))
6235+ .withSelection ("item" )
6236+ .withSelection ("price" )
6237+ .withOrderBy (new OrderBy ("price" , true )) // ASC
6238+ .withLimit (3 )
6239+ .withOffset (1 ); // Skip first result
6240+
6241+ Iterator <Document > results = flatCollection .search (legacyQuery );
6242+ int count = 0 ;
6243+ int previousPrice = Integer .MIN_VALUE ;
6244+ while (results .hasNext ()) {
6245+ Document doc = results .next ();
6246+ JsonNode json = new ObjectMapper ().readTree (doc .toJson ());
6247+ assertTrue (json .has ("item" ));
6248+ assertTrue (json .has ("price" ));
6249+ int price = json .get ("price" ).asInt ();
6250+ assertTrue (price >= previousPrice ); // ASC order
6251+ previousPrice = price ;
6252+ count ++;
6253+ }
6254+ assertEquals (3 , count );
6255+ }
6256+
6257+ @ ParameterizedTest
6258+ @ ArgumentsSource (PostgresProvider .class )
6259+ void testSearchWithNullFilterEmptySelectionsNoOrderBy (String dataStoreName ) {
6260+ Collection flatCollection = getFlatCollection (dataStoreName );
6261+
6262+ // Test covering null/empty branches:
6263+ // - No filter (null filter path)
6264+ // - No selections (empty selections path)
6265+ // - No orderBy (empty orderBys path)
6266+ // - Limit without offset (offset defaults to 0)
6267+ org .hypertrace .core .documentstore .Query legacyQuery =
6268+ new org .hypertrace .core .documentstore .Query ().withLimit (5 );
6269+
6270+ Iterator <Document > results = flatCollection .search (legacyQuery );
6271+ int count = 0 ;
6272+ while (results .hasNext ()) {
6273+ results .next ();
6274+ count ++;
6275+ }
6276+ assertEquals (5 , count );
6277+ }
6278+
6279+ @ ParameterizedTest
6280+ @ ArgumentsSource (PostgresProvider .class )
6281+ void testSearchWithUnknownFieldFallback (String dataStoreName ) {
6282+ Collection flatCollection = getFlatCollection (dataStoreName );
6283+
6284+ org .hypertrace .core .documentstore .Query legacyQuery =
6285+ new org .hypertrace .core .documentstore .Query ()
6286+ .withSelection ("nonexistent_field" )
6287+ .withOrderBy (new OrderBy ("another_unknown" , true ))
6288+ .withLimit (1 );
6289+
6290+ assertThrows (
6291+ Exception .class ,
6292+ () -> {
6293+ Iterator <Document > results = flatCollection .search (legacyQuery );
6294+ while (results .hasNext ()) {
6295+ results .next ();
6296+ }
6297+ });
6298+ }
6299+ }
6300+
59756301 @ Nested
59766302 class BulkUpdateTest {
59776303
0 commit comments