diff --git a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/postgres/PostgresDocStoreTest.java b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/postgres/PostgresDocStoreTest.java index 5d11dd84..cf3c2d1b 100644 --- a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/postgres/PostgresDocStoreTest.java +++ b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/postgres/PostgresDocStoreTest.java @@ -28,6 +28,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertThrows; + public class PostgresDocStoreTest { public static final String ID = "id"; @@ -304,22 +306,98 @@ public void testIgnoreCaseLikeQuery() throws IOException { } @Test - public void testSearch() throws IOException { + public void testInQuery() throws IOException { Collection collection = datastore.getCollection(COLLECTION_NAME); - String documentString = "{\"attributes\":{\"trace_id\":{\"value\":{\"string\":\"00000000000000005e194fdf9fbf5101\"}},\"span_id\":{\"value\":{\"string\":\"6449f1f720c93a67\"}},\"service_type\":{\"value\":{\"string\":\"JAEGER_SERVICE\"}},\"FQN\":{\"value\":{\"string\":\"driver\"}}},\"createdTime\":1605692185945,\"entityId\":\"e3ffc6f0-fc92-3a9c-9fa0-26269184d1aa\",\"entityName\":\"driver\",\"entityType\":\"SERVICE\",\"identifyingAttributes\":{\"FQN\":{\"value\":{\"string\":\"driver\"}}},\"tenantId\":\"__default\"}"; - Document document = new JSONDocument(documentString); - SingleValueKey key = new SingleValueKey("default", "testKey1"); - collection.upsert(key, document); + collection.upsert(new SingleValueKey("default", "testKey1"), createDocument("name", "Bob")); + collection.upsert(new SingleValueKey("default", "testKey2"), createDocument("name", "Alice")); + collection.upsert(new SingleValueKey("default", "testKey3"), createDocument("name", "Halo")); + + List inArray = new ArrayList<>(); + inArray.add("Bob"); + inArray.add("Alice"); - // Search _id field in the document Query query = new Query(); - query.setFilter(new Filter(Filter.Op.EQ, DOCUMENT_ID, key.toString())); + query.setFilter(new Filter(Filter.Op.IN, "name", inArray)); Iterator results = collection.search(query); List documents = new ArrayList<>(); while (results.hasNext()) { documents.add(results.next()); } - Assertions.assertEquals(documents.size(), 1); + Assertions.assertEquals(documents.size(), 2); + } + + @Test + public void testSearch() throws IOException { + Collection collection = datastore.getCollection(COLLECTION_NAME); + String docStr1 = "{\"amount\":1234.5,\"attributes\":{\"trace_id\":{\"value\":{\"string\":\"00000000000000005e194fdf9fbf5101\"}},\"span_id\":{\"value\":{\"string\":\"6449f1f720c93a67\"}},\"service_type\":{\"value\":{\"string\":\"JAEGER_SERVICE\"}},\"FQN\":{\"value\":{\"string\":\"driver\"}}},\"createdTime\":1605692185945,\"entityId\":\"e3ffc6f0-fc92-3a9c-9fa0-26269184d1aa\",\"entityName\":\"driver\",\"entityType\":\"SERVICE\",\"identifyingAttributes\":{\"FQN\":{\"value\":{\"string\":\"driver\"}}},\"tenantId\":\"__default\"}"; + Document document1 = new JSONDocument(docStr1); + SingleValueKey key1 = new SingleValueKey("default", "testKey1"); + collection.upsert(key1, document1); + + String docStr2 = "{\"amount\":1234,\"attributes\":{\"trace_id\":{\"value\":{\"string\":\"00000000000000005e194fdf9fbf5101\"}},\"span_id\":{\"value\":{\"string\":\"6449f1f720c93a67\"}},\"service_type\":{\"value\":{\"string\":\"JAEGER_SERVICE\"}},\"FQN\":{\"value\":{\"string\":\"driver\"}}},\"createdTime\":1605692185945,\"entityId\":\"e3ffc6f0-fc92-3a9c-9fa0-26269184d1aa\",\"entityName\":\"driver\",\"entityType\":\"SERVICE\",\"identifyingAttributes\":{\"FQN\":{\"value\":{\"string\":\"driver\"}}},\"tenantId\":\"__default\"}"; + Document document2 = new JSONDocument(docStr2); + SingleValueKey key2 = new SingleValueKey("default", "testKey2"); + collection.upsert(key2, document2); + + // Search integer field + { + Query query = new Query(); + query.setFilter(new Filter(Filter.Op.EQ, "amount", 1234)); + Iterator results = collection.search(query); + List documents = new ArrayList<>(); + for (; results.hasNext(); ) { + documents.add(results.next()); + } + Assertions.assertEquals(documents.size(), 1); + } + + // Search float field + { + Query query = new Query(); + query.setFilter(new Filter(Filter.Op.EQ, "amount", 1234.5)); + Iterator results = collection.search(query); + List documents = new ArrayList<>(); + for (; results.hasNext(); ) { + documents.add(results.next()); + } + Assertions.assertEquals(documents.size(), 1); + } + + // Search integer and float field + { + Query query = new Query(); + query.setFilter(new Filter(Filter.Op.GTE, "amount", 123)); + Iterator results = collection.search(query); + List documents = new ArrayList<>(); + for (; results.hasNext(); ) { + documents.add(results.next()); + } + Assertions.assertEquals(documents.size(), 2); + } + + // Search _id field in the document + { + Query query = new Query(); + query.setFilter(new Filter(Filter.Op.EQ, DOCUMENT_ID, key1.toString())); + Iterator results = collection.search(query); + List documents = new ArrayList<>(); + for (; results.hasNext(); ) { + documents.add(results.next()); + } + Assertions.assertEquals(documents.size(), 1); + } + + // Unsupported Object Type in Filter, should throw an UnsupportedOperationException + { + Query query = new Query(); + query.setFilter(new Filter(Filter.Op.EQ, "amount", new Filter())); + String expected = "Un-supported object types in filter"; + Exception exception = assertThrows(UnsupportedOperationException.class, + () -> collection.search(query)); + String actualMessage = exception.getMessage(); + Assertions.assertTrue(actualMessage.contains(expected)); + } + } @Test diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/Params.java b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/Params.java new file mode 100644 index 00000000..b3480b49 --- /dev/null +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/Params.java @@ -0,0 +1,55 @@ +package org.hypertrace.core.documentstore.postgres; + +import java.util.HashMap; +import java.util.Map; + +/** + * Holds the params that need to be set in the PreparedStatement for constructing the final SQL + * query + */ +public class Params { + + // Map of index to the corresponding param value + private final Map objectParams; + + private Params( + Map objectParams) { + this.objectParams = objectParams; + } + + @Override + public String toString() { + return "Params{" + + "objectParams=" + objectParams + + '}'; + } + + public Map getObjectParams() { + return objectParams; + } + + public static Builder newBuilder() { + return new Builder(); + } + + public static class Builder { + + private int nextIndex; + private final Map objectParams; + + private Builder() { + nextIndex = 1; + objectParams = new HashMap<>(); + } + + public Builder addObjectParam(Object paramValue) { + objectParams.put(nextIndex++, paramValue); + return this; + } + + public Params build() { + return new Params(objectParams); + } + } +} + diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresCollection.java b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresCollection.java index ebe40b0c..8b6ef78a 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresCollection.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresCollection.java @@ -48,6 +48,8 @@ public class PostgresCollection implements Collection { }}; private static final ObjectMapper MAPPER = new ObjectMapper(); private static final String DOC_PATH_SEPARATOR = "\\."; + private static String QUESTION_MARK = "?"; + private final Connection client; private final String collectionName; @@ -147,13 +149,13 @@ public boolean updateSubDoc(Key key, String subDocPath, Document subDocument) { @Override public Iterator search(Query query) { String filters = null; - String space = " "; - StringBuilder searchSQLBuilder = new StringBuilder("SELECT * FROM") - .append(space).append(collectionName); + StringBuilder sqlBuilder = new StringBuilder("SELECT * FROM ") + .append(collectionName); + Params.Builder paramsBuilder = Params.newBuilder(); // If there is a filter in the query, parse it fully. if (query.getFilter() != null) { - filters = parseQuery(query.getFilter()); + filters = parseFilter(query.getFilter(), paramsBuilder); } LOGGER.debug( @@ -162,27 +164,27 @@ public Iterator search(Query query) { filters); if (filters != null) { - searchSQLBuilder + sqlBuilder .append(" WHERE ").append(filters); } if (!query.getOrderBys().isEmpty()) { String orderBySQL = parseOrderByQuery(query.getOrderBys()); - searchSQLBuilder.append(" ORDER BY ").append(orderBySQL); + sqlBuilder.append(" ORDER BY ").append(orderBySQL); } Integer limit = query.getLimit(); if (limit != null && limit >= 0) { - searchSQLBuilder.append(" LIMIT ").append(limit); + sqlBuilder.append(" LIMIT ").append(limit); } Integer offset = query.getOffset(); if (offset != null && offset >= 0) { - searchSQLBuilder.append(" OFFSET ").append(offset); + sqlBuilder.append(" OFFSET ").append(offset); } try { - PreparedStatement preparedStatement = client.prepareStatement(searchSQLBuilder.toString()); + PreparedStatement preparedStatement = buildPreparedStatement(sqlBuilder.toString(), paramsBuilder.build()); ResultSet resultSet = preparedStatement.executeQuery(); return new PostgresResultIterator(resultSet); } catch (SQLException e) { @@ -193,16 +195,16 @@ public Iterator search(Query query) { } @VisibleForTesting - protected String parseQuery(Filter filter) { + protected String parseFilter(Filter filter, Params.Builder paramsBuilder) { if (filter.isComposite()) { - return parseQueryForCompositeFilter(filter); + return parseCompositeFilter(filter, paramsBuilder); } else { - return parseQueryForNonCompositeFilter(filter); + return parseNonCompositeFilter(filter, paramsBuilder); } } @VisibleForTesting - protected String parseQueryForNonCompositeFilter(Filter filter) { + protected String parseNonCompositeFilter(Filter filter, Params.Builder paramsBuilder) { Filter.Op op = filter.getOp(); Object value = filter.getValue(); String fieldName = filter.getFieldName(); @@ -234,7 +236,10 @@ protected String parseQueryForNonCompositeFilter(Filter filter) { List values = (List) value; String collect = values .stream() - .map(val -> "'" + val + "'") + .map(val -> { + paramsBuilder.addObjectParam(val); + return QUESTION_MARK; + }) .collect(Collectors.joining(", ")); return filterString.append("(").append(collect).append(")").toString(); case CONTAINS: @@ -244,22 +249,23 @@ protected String parseQueryForNonCompositeFilter(Filter filter) { case NOT_EXISTS: // TODO: Checks if key does not exist case NEQ: - throw new UnsupportedOperationException("Only Equality predicate is supported"); default: throw new UnsupportedOperationException( String.format("Query operation:%s not supported", op)); } - return filterString.append("'").append(value).append("'").toString(); + String filters = filterString.append(QUESTION_MARK).toString(); + paramsBuilder.addObjectParam(value); + return filters; } @VisibleForTesting - protected String parseQueryForCompositeFilter(Filter filter) { + protected String parseCompositeFilter(Filter filter, Params.Builder paramsBuilder) { Filter.Op op = filter.getOp(); switch (op) { case OR: { String childList = Arrays.stream(filter.getChildFilters()) - .map(this::parseQuery) + .map(childFilter -> parseFilter(childFilter, paramsBuilder)) .filter(str -> !StringUtils.isEmpty(str)) .map(str -> "(" + str + ")") .collect(Collectors.joining(" OR ")); @@ -268,7 +274,7 @@ protected String parseQueryForCompositeFilter(Filter filter) { case AND: { String childList = Arrays.stream(filter.getChildFilters()) - .map(this::parseQuery) + .map(childFilter -> parseFilter(childFilter, paramsBuilder)) .filter(str -> !StringUtils.isEmpty(str)) .map(str -> "(" + str + ")") .collect(Collectors.joining(" AND ")); @@ -276,10 +282,38 @@ protected String parseQueryForCompositeFilter(Filter filter) { } default: throw new UnsupportedOperationException( - String.format("Boolean operation:%s not supported", op)); + String.format("Query operation:%s not supported", op)); } } + @VisibleForTesting + protected PreparedStatement buildPreparedStatement(String sqlQuery, Params params) throws SQLException, RuntimeException { + PreparedStatement preparedStatement = client.prepareStatement(sqlQuery); + params.getObjectParams().forEach((k, v) -> { + try { + if (isValidType(v)) { + preparedStatement.setString(k, String.valueOf(v)); + } else { + throw new UnsupportedOperationException("Un-supported object types in filter"); + } + } catch (SQLException e) { + LOGGER.error("SQLException setting Param. key: {}, value: {}", k, v); + } + }); + return preparedStatement; + } + + private boolean isValidType(Object v) { + Set> validClassez = new HashSet<>() {{ + add(Double.class); + add(Float.class); + add(Integer.class); + add(Long.class); + add(String.class); + }}; + return validClassez.contains(v.getClass()); + } + @VisibleForTesting private String getJsonSubDocPath(String subDocPath) { return "{" + subDocPath.replaceAll(DOC_PATH_SEPARATOR, ",") + "}"; @@ -379,10 +413,12 @@ public long count() { public long total(Query query) { StringBuilder totalSQLBuilder = new StringBuilder("SELECT COUNT(*) FROM ") .append(collectionName); + Params.Builder paramsBuilder = Params.newBuilder(); + long count = -1; // on any in-correct filter input, it will return total without filtering if (query.getFilter() != null) { - String parsedQuery = parseQuery(query.getFilter()); + String parsedQuery = parseFilter(query.getFilter(), paramsBuilder); if (parsedQuery != null) { totalSQLBuilder.append(" WHERE ").append(parsedQuery); } diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/ParamsTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/ParamsTest.java new file mode 100644 index 00000000..6757bbd6 --- /dev/null +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/ParamsTest.java @@ -0,0 +1,33 @@ +package org.hypertrace.core.documentstore.postgres; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class ParamsTest { + + Params.Builder paramBuilder; + + @BeforeEach + void setUp() { + paramBuilder = Params.newBuilder(); + } + + @Test + public void testAllParamsAndIndex() { + paramBuilder.addObjectParam(1); + paramBuilder.addObjectParam(2L); + paramBuilder.addObjectParam("Alice"); + paramBuilder.addObjectParam(3L); + paramBuilder.addObjectParam(4L); + Params params = paramBuilder.build(); + int index = 1; + Assertions.assertEquals(params.getObjectParams().get(index++), 1); + Assertions.assertEquals(params.getObjectParams().get(index++), 2L); + Assertions.assertEquals(params.getObjectParams().get(index++), "Alice"); + Assertions.assertEquals(params.getObjectParams().get(index++), 3L); + Assertions.assertEquals(params.getObjectParams().get(index), 4L); + Assertions.assertEquals(index, 5); + } + +} diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java index 50e618d6..6de2eb55 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java @@ -25,132 +25,136 @@ public void setUp() { } @Test - public void testParseQueryForNonCompositeFilter() { + public void testParseNonCompositeFilter() { { Filter filter = new Filter(Filter.Op.EQ, ID, "val1"); - String query = collection.parseQueryForNonCompositeFilter(filter); - Assertions.assertEquals(ID + " = 'val1'", query); + String query = collection.parseNonCompositeFilter(filter, initParams()); + Assertions.assertEquals(ID + " = ?", query); } { Filter filter = new Filter(Filter.Op.GT, ID, 5); - String query = collection.parseQueryForNonCompositeFilter(filter); - Assertions.assertEquals(ID + " > '5'", query); + String query = collection.parseNonCompositeFilter(filter, initParams()); + Assertions.assertEquals(ID + " > ?", query); } { Filter filter = new Filter(Filter.Op.GTE, ID, 5); - String query = collection.parseQueryForNonCompositeFilter(filter); - Assertions.assertEquals(ID + " >= '5'", query); + String query = collection.parseNonCompositeFilter(filter, initParams()); + Assertions.assertEquals(ID + " >= ?", query); } { Filter filter = new Filter(Filter.Op.LT, ID, 5); - String query = collection.parseQueryForNonCompositeFilter(filter); - Assertions.assertEquals(ID + " < '5'", query); + String query = collection.parseNonCompositeFilter(filter, initParams()); + Assertions.assertEquals(ID + " < ?", query); } { Filter filter = new Filter(Filter.Op.LTE, ID, 5); - String query = collection.parseQueryForNonCompositeFilter(filter); - Assertions.assertEquals(ID + " <= '5'", query); + String query = collection.parseNonCompositeFilter(filter, initParams()); + Assertions.assertEquals(ID + " <= ?", query); } { Filter filter = new Filter(Filter.Op.LIKE, ID, "abc"); - String query = collection.parseQueryForNonCompositeFilter(filter); - Assertions.assertEquals(ID + " ILIKE '%abc%'", query); + String query = collection.parseNonCompositeFilter(filter, initParams()); + Assertions.assertEquals(ID + " ILIKE ?", query); } { Filter filter = new Filter(Filter.Op.IN, ID, List.of("abc", "xyz")); - String query = collection.parseQueryForNonCompositeFilter(filter); - Assertions.assertEquals(ID + " IN ('abc', 'xyz')", query); + String query = collection.parseNonCompositeFilter(filter, initParams()); + Assertions.assertEquals(ID + " IN (?, ?)", query); } } @Test - public void testParseQueryForNonCompositeFilterForJsonField() { + public void testParseNonCompositeFilterForJsonField() { { Filter filter = new Filter(Filter.Op.EQ, "key1", "val1"); - String query = collection.parseQueryForNonCompositeFilter(filter); - Assertions.assertEquals("document->>'key1' = 'val1'", query); + String query = collection.parseNonCompositeFilter(filter, initParams()); + Assertions.assertEquals("document->>'key1' = ?", query); } { Filter filter = new Filter(Filter.Op.GT, "key1", 5); - String query = collection.parseQueryForNonCompositeFilter(filter); - Assertions.assertEquals("document->>'key1' > '5'", query); + String query = collection.parseNonCompositeFilter(filter, initParams()); + Assertions.assertEquals("document->>'key1' > ?", query); } { Filter filter = new Filter(Filter.Op.GTE, "key1", 5); - String query = collection.parseQueryForNonCompositeFilter(filter); - Assertions.assertEquals("document->>'key1' >= '5'", query); + String query = collection.parseNonCompositeFilter(filter, initParams()); + Assertions.assertEquals("document->>'key1' >= ?", query); } { Filter filter = new Filter(Filter.Op.LT, "key1", 5); - String query = collection.parseQueryForNonCompositeFilter(filter); - Assertions.assertEquals("document->>'key1' < '5'", query); + String query = collection.parseNonCompositeFilter(filter, initParams()); + Assertions.assertEquals("document->>'key1' < ?", query); } { Filter filter = new Filter(Filter.Op.LTE, "key1", 5); - String query = collection.parseQueryForNonCompositeFilter(filter); - Assertions.assertEquals("document->>'key1' <= '5'", query); + String query = collection.parseNonCompositeFilter(filter, initParams()); + Assertions.assertEquals("document->>'key1' <= ?", query); } { Filter filter = new Filter(Filter.Op.LIKE, "key1", "abc"); - String query = collection.parseQueryForNonCompositeFilter(filter); - Assertions.assertEquals("document->>'key1' ILIKE '%abc%'", query); + String query = collection.parseNonCompositeFilter(filter, initParams()); + Assertions.assertEquals("document->>'key1' ILIKE ?", query); } { Filter filter = new Filter(Filter.Op.IN, "key1", List.of("abc", "xyz")); - String query = collection.parseQueryForNonCompositeFilter(filter); - Assertions.assertEquals("document->>'key1' IN ('abc', 'xyz')", query); + String query = collection.parseNonCompositeFilter(filter, initParams()); + Assertions.assertEquals("document->>'key1' IN (?, ?)", query); } { Filter filter = new Filter(Filter.Op.EQ, DOCUMENT_ID, "k1:k2"); - String query = collection.parseQueryForNonCompositeFilter(filter); - Assertions.assertEquals("document->>'_id' = 'k1:k2'", query); + String query = collection.parseNonCompositeFilter(filter, initParams()); + Assertions.assertEquals("document->>'_id' = ?", query); } } @Test - public void testParseQueryForNonCompositeFilterUnsupportedException() { - String expectedMessage = "Only Equality predicate is supported"; + public void testNonCompositeFilterUnsupportedException() { + String expectedMessage = "Query operation:%s not supported"; { Filter filter = new Filter(Filter.Op.EXISTS, "key1", null); + String expected = String.format(expectedMessage, Filter.Op.EXISTS); Exception exception = assertThrows(UnsupportedOperationException.class, - () -> collection.parseQueryForNonCompositeFilter(filter)); + () -> collection.parseNonCompositeFilter(filter, initParams())); String actualMessage = exception.getMessage(); - Assertions.assertTrue(actualMessage.contains(expectedMessage)); + Assertions.assertTrue(actualMessage.contains(expected)); } { Filter filter = new Filter(Filter.Op.NOT_EXISTS, "key1", null); + String expected = String.format(expectedMessage, Filter.Op.NOT_EXISTS); Exception exception = assertThrows(UnsupportedOperationException.class, - () -> collection.parseQueryForNonCompositeFilter(filter)); + () -> collection.parseNonCompositeFilter(filter, initParams())); String actualMessage = exception.getMessage(); - Assertions.assertTrue(actualMessage.contains(expectedMessage)); + Assertions.assertTrue(actualMessage.contains(expected)); } { Filter filter = new Filter(Filter.Op.NEQ, "key1", null); + String expected = String.format(expectedMessage, Filter.Op.NEQ); Exception exception = assertThrows(UnsupportedOperationException.class, - () -> collection.parseQueryForNonCompositeFilter(filter)); + () -> collection.parseNonCompositeFilter(filter, initParams())); String actualMessage = exception.getMessage(); - Assertions.assertTrue(actualMessage.contains(expectedMessage)); + Assertions.assertTrue(actualMessage.contains(expected)); } { Filter filter = new Filter(Filter.Op.CONTAINS, "key1", null); + String expected = String.format(expectedMessage, Filter.Op.CONTAINS); Exception exception = assertThrows(UnsupportedOperationException.class, - () -> collection.parseQueryForNonCompositeFilter(filter)); + () -> collection.parseNonCompositeFilter(filter, initParams())); String actualMessage = exception.getMessage(); - Assertions.assertTrue(actualMessage.contains(expectedMessage)); + Assertions.assertTrue(actualMessage.contains(expected)); } } @@ -158,11 +162,11 @@ public void testParseQueryForNonCompositeFilterUnsupportedException() { public void testParseQueryForCompositeFilterWithNullConditions() { { Filter filter = new Filter(Filter.Op.AND, null, null); - Assertions.assertNull(collection.parseQuery(filter)); + Assertions.assertNull(collection.parseFilter(filter, initParams())); } { Filter filter = new Filter(Filter.Op.OR, null, null); - Assertions.assertNull(collection.parseQuery(filter)); + Assertions.assertNull(collection.parseFilter(filter, initParams())); } } @@ -171,18 +175,18 @@ public void testParseQueryForCompositeFilter() { { Filter filter = new Filter(Filter.Op.EQ, ID, "val1").and(new Filter(Filter.Op.EQ, CREATED_AT, "val2")); - String query = collection.parseQueryForCompositeFilter(filter); + String query = collection.parseCompositeFilter(filter, initParams()); Assertions - .assertEquals(String.format("(%s = 'val1') AND (%s = 'val2')", ID, CREATED_AT), query); + .assertEquals(String.format("(%s = ?) AND (%s = ?)", ID, CREATED_AT), query); } { Filter filter = new Filter(Filter.Op.EQ, ID, "val1").or(new Filter(Filter.Op.EQ, CREATED_AT, "val2")); - String query = collection.parseQueryForCompositeFilter(filter); + String query = collection.parseCompositeFilter(filter, initParams()); Assertions - .assertEquals(String.format("(%s = 'val1') OR (%s = 'val2')", ID, CREATED_AT), query); + .assertEquals(String.format("(%s = ?) OR (%s = ?)", ID, CREATED_AT), query); } } @@ -191,18 +195,18 @@ public void testParseQueryForCompositeFilterForJsonField() { { Filter filter = new Filter(Filter.Op.EQ, "key1", "val1").and(new Filter(Filter.Op.EQ, "key2", "val2")); - String query = collection.parseQueryForCompositeFilter(filter); + String query = collection.parseCompositeFilter(filter, initParams()); Assertions - .assertEquals("(document->>'key1' = 'val1') AND (document->>'key2' = 'val2')", query); + .assertEquals("(document->>'key1' = ?) AND (document->>'key2' = ?)", query); } { Filter filter = new Filter(Filter.Op.EQ, "key1", "val1").or(new Filter(Filter.Op.EQ, "key2", "val2")); - String query = collection.parseQueryForCompositeFilter(filter); + String query = collection.parseCompositeFilter(filter, initParams()); Assertions - .assertEquals("(document->>'key1' = 'val1') OR (document->>'key2' = 'val2')", query); + .assertEquals("(document->>'key1' = ?) OR (document->>'key2' = ?)", query); } } @@ -215,9 +219,9 @@ public void testParseNestedQuery() { new Filter(Filter.Op.EQ, ID, "val3").and(new Filter(Filter.Op.EQ, "key4", "val4")); Filter filter = filter1.or(filter2); - String query = collection.parseQuery(filter); - Assertions.assertEquals(String.format("((%s = 'val1') AND (document->>'key2' = 'val2')) " + - "OR ((%s = 'val3') AND (document->>'key4' = 'val4'))", ID, ID), query); + String query = collection.parseFilter(filter, initParams()); + Assertions.assertEquals(String.format("((%s = ?) AND (document->>'key2' = ?)) " + + "OR ((%s = ?) AND (document->>'key4' = ?))", ID, ID), query); } @@ -230,10 +234,14 @@ public void testJSONFieldParseNestedQuery() { new Filter(Filter.Op.EQ, "key3", "val3").and(new Filter(Filter.Op.EQ, "key4", "val4")); Filter filter = filter1.or(filter2); - String query = collection.parseQuery(filter); - Assertions.assertEquals("((document->>'key1' = 'val1') AND (document->>'key2' = 'val2')) " + - "OR ((document->>'key3' = 'val3') AND (document->>'key4' = 'val4'))", query); + String query = collection.parseFilter(filter, initParams()); + Assertions.assertEquals("((document->>'key1' = ?) AND (document->>'key2' = ?)) " + + "OR ((document->>'key3' = ?) AND (document->>'key4' = ?))", query); } + private Params.Builder initParams() { + return Params.newBuilder(); + } + }