Skip to content

Commit

Permalink
add tests for generating OQL expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
jmoghisi authored and asereda-gs committed Feb 12, 2020
1 parent f30ef44 commit 0c5e5e3
Show file tree
Hide file tree
Showing 4 changed files with 197 additions and 43 deletions.
Expand Up @@ -126,7 +126,7 @@ private Oql binaryOperator(Call call) {
if (op == Operators.EQUAL || op == Operators.NOT_EQUAL) {
operator = op == Operators.EQUAL ? "=" : "!=";
} else if (op == Operators.IN || op == Operators.NOT_IN) {
operator = op == Operators.IN ? "in" : "not in";
operator = op == Operators.IN ? "IN" : "NOT IN";
} else if (op == ComparableOperators.GREATER_THAN) {
operator = ">";
} else if (op == ComparableOperators.GREATER_THAN_OR_EQUAL) {
Expand Down
Expand Up @@ -17,10 +17,7 @@
package org.immutables.criteria.geode;

import org.immutables.criteria.backend.PathNaming;
import org.immutables.criteria.expression.AggregationCall;
import org.immutables.criteria.expression.Expression;
import org.immutables.criteria.expression.Path;
import org.immutables.criteria.expression.Query;
import org.immutables.criteria.expression.*;
import org.immutables.value.Value;

import java.util.ArrayList;
Expand Down Expand Up @@ -63,7 +60,7 @@ Oql generate(Query query) {

final StringBuilder oql = new StringBuilder("SELECT");
if (query.distinct()) {
oql.append(" DISTINCT ");
oql.append(" DISTINCT");
}

if (query.hasProjections()) {
Expand All @@ -74,13 +71,12 @@ Oql generate(Query query) {
String projections = query.count() && !addOuterCountQuery ? " COUNT(*) " : String.join(", ", paths);
oql.append(" ");
oql.append(projections);
oql.append(" ");
} else {
// no projections
oql.append(query.count() && !addOuterCountQuery ? " COUNT(*) " : " * ");
oql.append(query.count() && !addOuterCountQuery ? " COUNT(*)" : " *");
}

oql.append("FROM ").append(regionName());
oql.append(" FROM ").append(regionName());
final List<Object> variables = new ArrayList<>();
if (query.filter().isPresent()) {
Oql withVars = Geodes.converter(useBindVariables(), pathNaming()).convert(query.filter().get());
Expand Down
@@ -0,0 +1,116 @@
package org.immutables.criteria.geode;

import static org.immutables.check.Checkers.check;
import static org.immutables.criteria.matcher.Matchers.toExpression;
import static org.immutables.criteria.personmodel.PersonCriteria.person;

import org.immutables.criteria.backend.PathNaming;
import org.immutables.criteria.personmodel.PersonCriteria;
import org.junit.jupiter.api.Test;

class GeodeQueryVisitorTest {

@Test
void filter() {
check(toOql(person.age.is(18))).is("age = 18");
check(toOql(person.age.isNot(18))).is("age != 18");

check(toOql(person.age.atMost(18))).is("age <= 18");
check(toOql(person.age.lessThan(18))).is("age < 18");

check(toOql(person.age.atLeast(18))).is("age >= 18");
check(toOql(person.age.greaterThan(18))).is("age > 18");

check(toOql(person.age.between(18, 21))).is("(age >= 18) AND (age <= 21)");

check(toOql(person.address.isPresent())).is("address != null");
check(toOql(person.address.isAbsent())).is("address = null");
}

@Test
void filterWithBindParams() {
check(toOqlWithBindParams(person.age.is(18))).is("age = $1");
check(toOqlWithBindParams(person.age.isNot(18))).is("age != $1");

check(toOqlWithBindParams(person.age.atMost(18))).is("age <= $1");
check(toOqlWithBindParams(person.age.lessThan(18))).is("age < $1");

check(toOqlWithBindParams(person.age.atLeast(18))).is("age >= $1");
check(toOqlWithBindParams(person.age.greaterThan(18))).is("age > $1");

check(toOqlWithBindParams(person.age.between(18, 21))).is("(age >= $1) AND (age <= $2)");

check(toOqlWithBindParams(person.address.isPresent())).is("address != null");
check(toOqlWithBindParams(person.address.isAbsent())).is("address = null");

check(toOqlWithBindParams(person.fullName.isEmpty())).is("fullName = $1");
check(toOqlWithBindParams(person.fullName.notEmpty())).is("fullName != $1");
}

@Test
void filterNested() {
check(toOql(person.address.value().city.is("London")))
.is("address.city = 'London'");
}

@Test
void filterNestedWithBindParams() {
check(toOqlWithBindParams(person.address.value().city.is("London")))
.is("address.city = $1");
}

@Test
void filterIn() {
check(toOql(person.age.in(18, 19, 20, 21))).is("age IN SET(18, 19, 20, 21)");

check(toOql(person.age.notIn(18, 19, 20, 21))).is("NOT (age IN SET(18, 19, 20, 21))");
}

@Test
void filterInWithBindParams() {
check(toOqlWithBindParams(person.age.in(18, 19, 20, 21))).is("age IN $1");

check(toOqlWithBindParams(person.age.notIn(18, 19, 20, 21))).is("NOT (age IN $1)");
}

@Test
void filterNegation() {
check(toOqlWithBindParams(person.age.not(self -> self.greaterThan(18)))).is("NOT (age > $1)");
}

@Test
void filterConjunction() {
check(toOqlWithBindParams(person.age.greaterThan(18)
.address.value().city.is("London")
.and(person.isActive.isTrue())))
.is("((age > $1) AND (address.city = $2)) AND (isActive = $3)");
}

@Test
void filterDisjunction() {
check(toOqlWithBindParams(person.age.greaterThan(18)
.or(person.isActive.isTrue()))).is("(age > $1) OR (isActive = $2)");
}

@Test
void filterConjunctionWithDisjunction() {
check(toOqlWithBindParams(person.age.greaterThan(18)
.address.value().city.is("London")
.or(person.isActive.isTrue()))).is("((age > $1) AND (address.city = $2)) OR (isActive = $3)");
}

private static String toOqlWithBindParams(PersonCriteria personCriteria) {
return toOql(personCriteria, true);
}

private static String toOql(PersonCriteria personCriteria) {
return toOql(personCriteria, false);
}

private static String toOql(PersonCriteria personCriteria, boolean useBindVariables) {
final PathNaming pathNaming = ReservedWordNaming.of(PathNaming.defaultNaming());
final GeodeQueryVisitor queryVisitor = new GeodeQueryVisitor(useBindVariables, pathNaming);
return toExpression(personCriteria).accept(queryVisitor).oql();
}

}
Expand Up @@ -18,19 +18,17 @@

import org.immutables.criteria.Criteria;
import org.immutables.criteria.backend.PathNaming;
import org.immutables.criteria.expression.Collation;
import org.immutables.criteria.expression.Expression;
import org.immutables.criteria.expression.Expressions;
import org.immutables.criteria.expression.Operators;
import org.immutables.criteria.expression.Query;
import org.immutables.criteria.matcher.Matchers;
import org.immutables.criteria.typemodel.TypeHolder;
import org.immutables.criteria.expression.*;
import org.immutables.criteria.personmodel.Person;
import org.immutables.value.Value;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.util.Collections;

import static org.immutables.check.Checkers.check;
import static org.immutables.criteria.matcher.Matchers.toExpression;
import static org.immutables.criteria.personmodel.PersonCriteria.person;

/**
* Check generated OQL out of {@link Query}
Expand All @@ -39,48 +37,88 @@ class OqlGeneratorTest {

private final ReservedWordsCriteria reservedWords = ReservedWordsCriteria.reservedWords;

private OqlGenerator generator;

@BeforeEach
void setUp() {
generator = OqlGenerator.of("/myRegion", PathNaming.defaultNaming());
}

@Test
void basic() {
OqlGenerator generator = OqlGenerator.of("/myRegion", PathNaming.defaultNaming());
check(generator.generate(Query.of(TypeHolder.StringHolder.class)).oql()).is("SELECT * FROM /myRegion");
check(generator.generate(Query.of(TypeHolder.StringHolder.class).withLimit(1)).oql()).is("SELECT * FROM /myRegion LIMIT 1");
final ImmutableQuery query = Query.of(Person.class);
check(generate(query)).is("SELECT * FROM /myRegion");
check(generate(query.withDistinct(true))).is("SELECT DISTINCT * FROM /myRegion");

Expression proj1 = Matchers.toExpression(reservedWords.value);
check(generator.generate(Query.of(TypeHolder.StringHolder.class).addProjections(proj1))
.oql()).is("SELECT value FROM /myRegion");
Expression proj1 = toExpression(reservedWords.value);
check(generate(Query.of(Person.class).addProjections(proj1)))
.is("SELECT value FROM /myRegion");

Expression proj2 = Matchers.toExpression(reservedWords.nullable);
check(generator.generate(Query.of(TypeHolder.StringHolder.class).addProjections(proj1, proj2))
.oql()).is("SELECT value, nullable FROM /myRegion");
Expression proj2 = toExpression(reservedWords.nullable);
check(generate(Query.of(Person.class).addProjections(proj1, proj2)))
.is("SELECT value, nullable FROM /myRegion");
}

@Test
void filter() {
check(generate(Query.of(Person.class).withFilter(toExpression(person.age.greaterThan(18)))))
.is("SELECT * FROM /myRegion WHERE age > $1");
}

@Test
void orderBy() {
final ImmutableQuery query = Query.of(Person.class);
check(generate(query.withCollations((Collation) person.fullName.asc())))
.is("SELECT * FROM /myRegion ORDER BY fullName");

check(generate(query.withCollations((Collation) person.age.desc(), (Collation) person.fullName.asc())))
.is("SELECT * FROM /myRegion ORDER BY age DESC, fullName");

check(generate(query.withCollations((Collation) person.fullName.desc()).withLimit(1).withOffset(10)))
.is("SELECT * FROM /myRegion ORDER BY fullName DESC LIMIT 1 OFFSET 10");
}

@Test
void projections() {
final ImmutableQuery query = Query.of(Person.class);
check(generate(query.addProjections(toExpression(person.fullName))))
.is("SELECT fullName FROM /myRegion");

check(generate(query.addProjections(
toExpression(person.fullName), toExpression(person.age)
))).is("SELECT fullName, age FROM /myRegion");
}

@Test
void countAll() {
OqlGenerator generator = OqlGenerator.of("/myRegion", PathNaming.defaultNaming());
check(generator.generate(Query.of(TypeHolder.StringHolder.class).withCount(true)).oql()).is("SELECT COUNT(*) FROM /myRegion");
check(generate(Query.of(Person.class).withCount(true)))
.is("SELECT COUNT(*) FROM /myRegion");

check(generate(Query.of(Person.class).withDistinct(true).withCount(true)))
.is("SELECT DISTINCT COUNT(*) FROM /myRegion");
}

@Test
void pathNaming() {
OqlGenerator generator = OqlGenerator.of("/myRegion", path -> path.toStringPath() + "1");
Expression type = Matchers.toExpression(reservedWords.type);
Expression value = Matchers.toExpression(reservedWords.value);
Expression select = Matchers.toExpression(reservedWords.select);
check(generator.generate(Query.of(TypeHolder.StringHolder.class).addProjections(type))
Expression type = toExpression(reservedWords.type);
Expression value = toExpression(reservedWords.value);
Expression select = toExpression(reservedWords.select);
check(generator.generate(Query.of(Person.class).addProjections(type))
.oql()).is("SELECT type1 FROM /myRegion");

check(generator.generate(Query.of(TypeHolder.StringHolder.class).addProjections(type, value))
check(generator.generate(Query.of(Person.class).addProjections(type, value))
.oql()).is("SELECT type1, value1 FROM /myRegion");

check(generator.withoutBindVariables().generate(Query.of(TypeHolder.StringHolder.class)
check(generator.withoutBindVariables().generate(Query.of(Person.class)
.addProjections(type, value)
.withFilter(Expressions.call(Operators.EQUAL, select, Expressions.constant(42))))
.oql()).is("SELECT type1, value1 FROM /myRegion WHERE select1 = 42");

check(generator.generate(Query.of(TypeHolder.StringHolder.class).addProjections(type, value).addGroupBy(type))
check(generator.generate(Query.of(Person.class).addProjections(type, value).addGroupBy(type))
.oql()).is("SELECT type1, value1 FROM /myRegion GROUP BY type1");

check(generator.generate(Query.of(TypeHolder.StringHolder.class)
check(generator.generate(Query.of(Person.class)
.addGroupBy(type)
.addCollations(Collections.singleton(Collation.of(type)))
.addProjections(type, value))
Expand All @@ -96,26 +134,30 @@ void pathNaming() {
void reservedWords() {
OqlGenerator generator = OqlGenerator.of("/myRegion", ReservedWordNaming.of(PathNaming.defaultNaming()));

Expression type = Matchers.toExpression(reservedWords.type);
Expression order = Matchers.toExpression(reservedWords.order);
Expression date = Matchers.toExpression(reservedWords.date);
Expression select = Matchers.toExpression(reservedWords.select);
check(generator.generate(Query.of(TypeHolder.StringHolder.class).addProjections(type))
Expression type = toExpression(reservedWords.type);
Expression order = toExpression(reservedWords.order);
Expression date = toExpression(reservedWords.date);
Expression select = toExpression(reservedWords.select);
check(generator.generate(Query.of(Person.class).addProjections(type))
.oql()).is("SELECT \"type\" FROM /myRegion");

check(generator.generate(Query.of(TypeHolder.StringHolder.class).addProjections(type, order))
check(generator.generate(Query.of(Person.class).addProjections(type, order))
.oql()).is("SELECT \"type\", \"order\" FROM /myRegion");

check(generator.generate(Query.of(TypeHolder.StringHolder.class).addProjections(type, order, date))
check(generator.generate(Query.of(Person.class).addProjections(type, order, date))
.oql()).is("SELECT \"type\", \"order\", \"date\" FROM /myRegion");

check(generator.generate(Query.of(TypeHolder.StringHolder.class)
check(generator.generate(Query.of(Person.class)
.addGroupBy(type)
.addCollations(Collections.singleton(Collation.of(type)))
.addProjections(type, order, date, select))
.oql()).is("SELECT \"type\", \"order\", \"date\", \"select\" FROM /myRegion GROUP BY \"type\" ORDER BY \"type\"");
}

private String generate(Query query) {
return generator.generate(query).oql();
}

/**
* Some of the reserved words in Geode.
* For a full list see <a href="https://geode.apache.org/docs/guide/19/developing/querying_basics/reserved_words.html">reserved words in Geode</a>
Expand Down

0 comments on commit 0c5e5e3

Please sign in to comment.