Skip to content

Commit 2fe1f90

Browse files
authored
Implement Collection#search for FlatPostgresCollection (#285)
1 parent 080d894 commit 2fe1f90

File tree

5 files changed

+904
-93
lines changed

5 files changed

+904
-93
lines changed

document-store/src/integrationTest/java/org/hypertrace/core/documentstore/DocStoreQueryV1Test.java

Lines changed: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)