DATACOUCH-603 Do not cast query parameters in N1qlQueryCreator.

For IN and NOT_IN - they can take varargs, an array or a JsonArray
Don't cast query criteria parameters, let parameter accessor handle that.
Fixed conversion of query criteria values to parameters
Cleaned up QueryCriteria

Co-authored-by: mikereiche <michael.reiche@couchbase.com>
This commit is contained in:
Michael Reiche
2020-10-09 14:59:16 -07:00
committed by GitHub
parent e72cdba6ee
commit 1b5c32150e
10 changed files with 354 additions and 178 deletions

View File

@@ -19,7 +19,18 @@ package org.springframework.data.couchbase.core.query;
import static org.junit.jupiter.api.Assertions.*;
import static org.springframework.data.couchbase.core.query.QueryCriteria.*;
import com.couchbase.client.java.json.JsonArray;
import com.couchbase.client.java.json.JsonObject;
import org.junit.jupiter.api.Test;
import org.springframework.data.couchbase.domain.User;
import org.springframework.data.couchbase.domain.UserRepository;
import org.springframework.data.couchbase.repository.query.N1qlQueryCreator;
import org.springframework.data.repository.query.parser.PartTree;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
class QueryCriteriaTests {
@@ -56,21 +67,25 @@ class QueryCriteriaTests {
@Test
void testNestedAndCriteria() {
QueryCriteria c = where("name").is("Bubba").and(where("age").gt(12).or("country").is("Austria"));
assertEquals("`name` = \"Bubba\" and (`age` > 12 or `country` = \"Austria\")", c.export());
JsonArray parameters = JsonArray.create();
assertEquals("`name` = $1 and (`age` > $2 or `country` = $3)", c.export(new int[1], parameters, null));
assertEquals("[\"Bubba\",12,\"Austria\"]", parameters.toString());
}
@Test
void testNestedOrCriteria() {
QueryCriteria c = where("name").is("Bubba").or(where("age").gt(12).or("country").is("Austria"));
assertEquals("`name` = \"Bubba\" or (`age` > 12 or `country` = \"Austria\")", c.export());
JsonArray parameters = JsonArray.create();
assertEquals("`name` = $1 or (`age` > $2 or `country` = $3)", c.export(new int[1], parameters, null));
assertEquals("[\"Bubba\",12,\"Austria\"]", parameters.toString());
}
@Test
void testNestedNotIn() {
QueryCriteria c = where("name").is("Bubba").or(where("age").gt(12).or("country").is("Austria")).and(
where("state").notIn(new String[] { "Alabama", "Florida" }));
QueryCriteria c = where("name").is("Bubba").or(where("age").gt(12).or("country").is("Austria"))
.and(where("state").notIn(new String[] { "Alabama", "Florida" }));
assertEquals("`name` = \"Bubba\" or (`age` > 12 or `country` = \"Austria\") and "
+ "(not( (`state` in ( [ \"Alabama\", \"Florida\" ] )) ))", c.export());
+ "(not( (`state` in ( [\"Alabama\",\"Florida\"] )) ))", c.export());
}
@Test
@@ -110,13 +125,13 @@ class QueryCriteriaTests {
}
/* cannot do this properly yet because in arg to when() in
* startingWith() cannot be a QueryCriteria
* startingWith() cannot be a QueryCriteria
@Test
void testStartingWithExpr() {
QueryCriteria c = where("name").startingWith(where("name").plus("xxx"));
assertEquals("`name` like (((`name` || "xxx") || ""%""))", c.export());
}
*/
*/
@Test
void testEndingWith() {
@@ -204,14 +219,22 @@ class QueryCriteriaTests {
@Test
void testIn() {
QueryCriteria c = where("name").in(new String[] { "gump", "davis" });
assertEquals("`name` in ( [ \"gump\", \"davis\" ] )", c.export());
String[] args = new String[] { "gump", "davis" };
QueryCriteria c = where("name").in(args);
assertEquals("`name` in ( [\"gump\",\"davis\"] )", c.export());
JsonArray parameters = JsonArray.create();
assertEquals("`name` in ( $1 )", c.export(new int[1], parameters, null));
assertEquals(arrayToString(args), parameters.get(0).toString());
}
@Test
void testNotIn() {
QueryCriteria c = where("name").notIn(new String[] { "gump", "davis" });
assertEquals("not( (`name` in ( [ \"gump\", \"davis\" ] )) )", c.export());
String[] args = new String[] { "gump", "davis" };
QueryCriteria c = where("name").notIn(args);
assertEquals("not( (`name` in ( [\"gump\",\"davis\"] )) )", c.export());
JsonArray parameters = JsonArray.create();
assertEquals("not( (`name` in ( $1 )) )", c.export(new int[1], parameters, null));
assertEquals(arrayToString(args), parameters.get(0).toString());
}
@Test
@@ -225,4 +248,28 @@ class QueryCriteriaTests {
QueryCriteria c = where("name").FALSE();
assertEquals("not( (`name`) )", c.export());
}
private String arrayToString(Object[] array) {
StringBuilder sb = new StringBuilder();
if (array != null) {
sb.append("[");
boolean first = true;
for (Object e : array) {
if (!first) {
sb.append(",");
}
first = false;
if (e instanceof Number)
sb.append(e);
else {
sb.append("\"");
sb.append(e);
sb.append("\"");
}
}
sb.append("]");
}
return sb.toString();
}
}

View File

@@ -18,6 +18,7 @@ package org.springframework.data.couchbase.domain;
import java.util.List;
import com.couchbase.client.java.json.JsonArray;
import org.springframework.data.couchbase.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
@@ -34,6 +35,10 @@ public interface UserRepository extends PagingAndSortingRepository<User, String>
List<User> findByFirstname(String firstname);
List<User> findByFirstnameIn(String... firstnames);
List<User> findByFirstnameIn(JsonArray firstnames);
List<User> findByFirstnameAndLastname(String firstname, String lastname);
@Query("#{#n1ql.selectEntity} where #{#n1ql.filter} and firstname = $1 and lastname = $2")

View File

@@ -19,7 +19,13 @@ import static org.junit.jupiter.api.Assertions.*;
import static org.springframework.data.couchbase.core.query.QueryCriteria.*;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import com.couchbase.client.java.json.JsonArray;
import com.couchbase.client.java.json.JsonObject;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
@@ -62,6 +68,69 @@ class N1qlQueryCreatorTests {
assertEquals(query.export(), " WHERE " + where("firstname").is("Oliver").export());
}
@Test
void queryParametersArray() throws Exception {
String input = "findByFirstnameIn";
PartTree tree = new PartTree(input, User.class);
Method method = UserRepository.class.getMethod(input, String[].class);
Query expected = (new Query()).addCriteria(where("firstname").in("Oliver", "Charles"));
N1qlQueryCreator creator = new N1qlQueryCreator(tree,
getAccessor(getParameters(method), new Object[] { new Object[] { "Oliver", "Charles" } }), null, converter);
Query query = creator.createQuery();
// Query expected = (new Query()).addCriteria(where("firstname").in("Oliver", "Charles"));
assertEquals(expected.export(new int[1]), query.export(new int[1]));
JsonObject expectedOptions = JsonObject.create();
expected.buildQueryOptions(null).build().injectParams(expectedOptions);
JsonObject actualOptions = JsonObject.create();
expected.buildQueryOptions(null).build().injectParams(actualOptions);
assertEquals(expectedOptions.removeKey("client_context_id"), actualOptions.removeKey("client_context_id"));
}
@Test
void queryParametersJsonArray() throws Exception {
String input = "findByFirstnameIn";
PartTree tree = new PartTree(input, User.class);
Method method = UserRepository.class.getMethod(input, JsonArray.class);
JsonArray jsonArray = JsonArray.create();
jsonArray.add("Oliver");
jsonArray.add("Charles");
N1qlQueryCreator creator = new N1qlQueryCreator(tree, getAccessor(getParameters(method), jsonArray), null,
converter);
Query query = creator.createQuery();
Query expected = (new Query()).addCriteria(where("firstname").in("Oliver", "Charles"));
assertEquals(expected.export(new int[1]), query.export(new int[1]));
JsonObject expectedOptions = JsonObject.create();
expected.buildQueryOptions(null).build().injectParams(expectedOptions);
JsonObject actualOptions = JsonObject.create();
expected.buildQueryOptions(null).build().injectParams(actualOptions);
assertEquals(expectedOptions.removeKey("client_context_id"), actualOptions.removeKey("client_context_id"));
}
@Test
void queryParametersList() throws Exception {
String input = "findByFirstnameIn";
PartTree tree = new PartTree(input, User.class);
Method method = UserRepository.class.getMethod(input, String[].class);
List<String> list = new LinkedList<>();
list.add("Oliver");
list.add("Charles");
N1qlQueryCreator creator = new N1qlQueryCreator(tree, getAccessor(getParameters(method), new Object[] { list }),
null, converter);
Query query = creator.createQuery();
Query expected = (new Query()).addCriteria(where("firstname").in("Oliver", "Charles"));
assertEquals(expected.export(new int[1]), query.export(new int[1]));
JsonObject expectedOptions = JsonObject.create();
expected.buildQueryOptions(null).build().injectParams(expectedOptions);
JsonObject actualOptions = JsonObject.create();
expected.buildQueryOptions(null).build().injectParams(actualOptions);
assertEquals(expectedOptions.removeKey("client_context_id"), actualOptions.removeKey("client_context_id"));
}
@Test
void createsAndQueryCorrectly() throws Exception {
String input = "findByFirstnameAndLastname";
@@ -71,7 +140,7 @@ class N1qlQueryCreatorTests {
converter);
Query query = creator.createQuery();
assertEquals(query.export(), " WHERE " + where("firstname").is("John").and("lastname").is("Doe").export());
assertEquals(" WHERE " + where("firstname").is("John").and("lastname").is("Doe").export(), query.export());
}
private ParameterAccessor getAccessor(Parameters<?, ?> params, Object... values) {