expressions now support in-queries
This commit is contained in:
@@ -10,10 +10,16 @@ expression
|
|||||||
: LPAREN expression RPAREN #parenExpression
|
: LPAREN expression RPAREN #parenExpression
|
||||||
| NOT expression #notExpression
|
| NOT expression #notExpression
|
||||||
| prop=identifier eq=equal value=propValue #propertyExpression
|
| prop=identifier eq=equal value=propValue #propertyExpression
|
||||||
|
| prop=identifier in=inExpr LPAREN listOfProperties=listOfPropValues RPAREN #inExpression
|
||||||
| left=expression AND right=expression #binaryAndExpression
|
| left=expression AND right=expression #binaryAndExpression
|
||||||
| left=expression OR right=expression #binaryOrExpression
|
| left=expression OR right=expression #binaryOrExpression
|
||||||
;
|
;
|
||||||
|
|
||||||
|
listOfPropValues
|
||||||
|
: value=propValue
|
||||||
|
| leftValue=propValue COMMA listOfProperties=listOfPropValues
|
||||||
|
;
|
||||||
|
|
||||||
identifier
|
identifier
|
||||||
: IDENTIFIER #identifierExpression
|
: IDENTIFIER #identifierExpression
|
||||||
;
|
;
|
||||||
@@ -22,13 +28,16 @@ propValue
|
|||||||
;
|
;
|
||||||
|
|
||||||
equal : EQUAL ;
|
equal : EQUAL ;
|
||||||
|
inExpr : IN ;
|
||||||
|
|
||||||
AND : 'and' ;
|
AND : 'and' ;
|
||||||
OR : 'or' ;
|
OR : 'or' ;
|
||||||
NOT : '!';
|
NOT : '!';
|
||||||
EQUAL : '=' ;
|
EQUAL : '=' ;
|
||||||
|
IN : 'in' ;
|
||||||
LPAREN : '(' ;
|
LPAREN : '(' ;
|
||||||
RPAREN : ')' ;
|
RPAREN : ')' ;
|
||||||
|
COMMA : ',' ;
|
||||||
IDENTIFIER
|
IDENTIFIER
|
||||||
: JavaLetter JavaLetterOrDigit*
|
: JavaLetter JavaLetterOrDigit*
|
||||||
;
|
;
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
package org.lucares.pdb.datastore.lang;
|
package org.lucares.pdb.datastore.lang;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.lucares.utils.CollectionUtils;
|
||||||
|
|
||||||
abstract public class Expression {
|
abstract public class Expression {
|
||||||
|
|
||||||
public <T> T visit(final ExpressionVisitor<T> visitor) {
|
public <T> T visit(final ExpressionVisitor<T> visitor) {
|
||||||
@@ -434,4 +439,84 @@ abstract public class Expression {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class ListOfPropertyValues extends Expression {
|
||||||
|
private final List<Terminal> propertyValues = new ArrayList<>();
|
||||||
|
|
||||||
|
public ListOfPropertyValues(final Terminal propertyValue) {
|
||||||
|
propertyValues.add(propertyValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListOfPropertyValues(final Terminal propertyValue, final ListOfPropertyValues listOfPropertyValues) {
|
||||||
|
propertyValues.addAll(listOfPropertyValues.propertyValues);
|
||||||
|
propertyValues.add(propertyValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getValues() {
|
||||||
|
return CollectionUtils.map(propertyValues, Terminal::getValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "(" + String.join(", ", getValues()) + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class InExpression extends Expression {
|
||||||
|
private final String property;
|
||||||
|
private final List<String> values;
|
||||||
|
|
||||||
|
public InExpression(final String property, final List<String> values) {
|
||||||
|
this.property = property;
|
||||||
|
this.values = values;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return property + " in (" + String.join(", ", values) + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T visit(final ExpressionVisitor<T> visitor) {
|
||||||
|
return visitor.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProperty() {
|
||||||
|
return property;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getValues() {
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((property == null) ? 0 : property.hashCode());
|
||||||
|
result = prime * result + ((values == null) ? 0 : values.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
final InExpression other = (InExpression) obj;
|
||||||
|
if (property == null) {
|
||||||
|
if (other.property != null)
|
||||||
|
return false;
|
||||||
|
} else if (!property.equals(other.property))
|
||||||
|
return false;
|
||||||
|
if (values == null) {
|
||||||
|
if (other.values != null)
|
||||||
|
return false;
|
||||||
|
} else if (!values.equals(other.values))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ public class ExpressionToDocIdVisitor extends ExpressionVisitor<IntList> {
|
|||||||
final IntList rightFiles = right.visit(this);
|
final IntList rightFiles = right.visit(this);
|
||||||
|
|
||||||
final long start = System.nanoTime();
|
final long start = System.nanoTime();
|
||||||
final IntList result = IntList.intersection(leftFiles,rightFiles);
|
final IntList result = IntList.intersection(leftFiles, rightFiles);
|
||||||
LOGGER.trace("{} took {} ms results={}", expression, (System.nanoTime() - start) / 1_000_000.0, result.size());
|
LOGGER.trace("{} took {} ms results={}", expression, (System.nanoTime() - start) / 1_000_000.0, result.size());
|
||||||
assert result.isSorted();
|
assert result.isSorted();
|
||||||
|
|
||||||
@@ -126,7 +126,40 @@ public class ExpressionToDocIdVisitor extends ExpressionVisitor<IntList> {
|
|||||||
@Override
|
@Override
|
||||||
public IntList visit(final Expression.MatchAll expression) {
|
public IntList visit(final Expression.MatchAll expression) {
|
||||||
final long start = System.nanoTime();
|
final long start = System.nanoTime();
|
||||||
IntList result = getAllDocIds();
|
final IntList result = getAllDocIds();
|
||||||
|
LOGGER.trace("{} took {} ms results={}", expression, (System.nanoTime() - start) / 1_000_000.0, result.size());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IntList visit(final Expression.InExpression expression) {
|
||||||
|
final long start = System.nanoTime();
|
||||||
|
|
||||||
|
final String propertyName = expression.getProperty();
|
||||||
|
final List<String> values = expression.getValues();
|
||||||
|
|
||||||
|
IntList result = new IntList();
|
||||||
|
|
||||||
|
for (final String value : values) {
|
||||||
|
if (isMatchAll(value)) {
|
||||||
|
|
||||||
|
final Map<String, IntList> allValuesForKey = keyToValueToDocId.getOrDefault(propertyName, EMPTY_VALUES);
|
||||||
|
|
||||||
|
result = merge(allValuesForKey.values());
|
||||||
|
break;
|
||||||
|
} else if (containsWildcard(value)) {
|
||||||
|
|
||||||
|
final Collection<IntList> docIds = filterByWildcard(propertyName, globToRegex(value));
|
||||||
|
final IntList mergedDocIds = merge(docIds);
|
||||||
|
result = IntList.union(result, mergedDocIds);
|
||||||
|
} else {
|
||||||
|
final IntList docIds = keyToValueToDocId.//
|
||||||
|
getOrDefault(propertyName, EMPTY_VALUES).//
|
||||||
|
getOrDefault(value, EMPTY_DOC_IDS);
|
||||||
|
result = IntList.union(result, docIds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LOGGER.trace("{} took {} ms results={}", expression, (System.nanoTime() - start) / 1_000_000.0, result.size());
|
LOGGER.trace("{} took {} ms results={}", expression, (System.nanoTime() - start) / 1_000_000.0, result.size());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ public abstract class ExpressionVisitor<T> {
|
|||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public T visit(final Expression.InExpression expression) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
public T visit(final Expression.Parentheses parentheses) {
|
public T visit(final Expression.Parentheses parentheses) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,24 @@ public class QueryCompletionPdbLangParser extends PdbLangParser {
|
|||||||
final int end = node.getSymbol().getStopIndex();
|
final int end = node.getSymbol().getStopIndex();
|
||||||
|
|
||||||
if (_ctx instanceof PropertyTerminalExpressionContext) {
|
if (_ctx instanceof PropertyTerminalExpressionContext) {
|
||||||
final String propertyKey = _ctx.getParent().children.get(0).getText();
|
|
||||||
|
final String postfixAfterInsertedTerminal;
|
||||||
|
final String propertyKey;
|
||||||
|
if (_ctx.getParent() instanceof ListOfPropValuesContext) {
|
||||||
|
// for in-expressions, e.g. key in (val)
|
||||||
|
ParserRuleContext parent = _ctx.getParent();
|
||||||
|
while (parent instanceof ListOfPropValuesContext) {
|
||||||
|
parent = parent.getParent();
|
||||||
|
}
|
||||||
|
|
||||||
|
propertyKey = parent.children.get(0).getText();
|
||||||
|
postfixAfterInsertedTerminal = "";
|
||||||
|
} else {
|
||||||
|
// for property-expressions, e.g. key = val
|
||||||
|
propertyKey = _ctx.getParent().children.get(0).getText();
|
||||||
|
postfixAfterInsertedTerminal = " ";
|
||||||
|
}
|
||||||
|
|
||||||
String propertyValuePrefix = node.getText().substring(0, caretPosition - start);
|
String propertyValuePrefix = node.getText().substring(0, caretPosition - start);
|
||||||
propertyValuePrefix = propertyValuePrefix.replace(Proposer.PREFIX_MARKER, "");
|
propertyValuePrefix = propertyValuePrefix.replace(Proposer.PREFIX_MARKER, "");
|
||||||
final SortedSet<String> proposedValues = getPropertyValuesByPrefix(propertyKey,
|
final SortedSet<String> proposedValues = getPropertyValuesByPrefix(propertyKey,
|
||||||
@@ -64,10 +81,12 @@ public class QueryCompletionPdbLangParser extends PdbLangParser {
|
|||||||
proposedValues.stream()//
|
proposedValues.stream()//
|
||||||
.map(v -> {
|
.map(v -> {
|
||||||
final StringBuilder newQuery = new StringBuilder(query);
|
final StringBuilder newQuery = new StringBuilder(query);
|
||||||
newQuery.replace(start, end + 1, v + " "); // insert the terminal into the query
|
newQuery.replace(start, end + 1, v + postfixAfterInsertedTerminal); // insert the
|
||||||
|
// terminal into the
|
||||||
|
// query
|
||||||
|
|
||||||
return new Proposal(v, newQuery.toString(), false, newQuery.toString(),
|
return new Proposal(v, newQuery.toString(), false, newQuery.toString(),
|
||||||
start + v.length() + 1);
|
start + v.length() + postfixAfterInsertedTerminal.length());
|
||||||
}).map(p -> {
|
}).map(p -> {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
try {
|
try {
|
||||||
@@ -255,10 +274,26 @@ public class QueryCompletionPdbLangParser extends PdbLangParser {
|
|||||||
public void enterEqual(final EqualContext ctx) {
|
public void enterEqual(final EqualContext ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enterInExpr(final InExprContext ctx) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enterInExpression(final InExpressionContext ctx) {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void exitEqual(final EqualContext ctx) {
|
public void exitEqual(final EqualContext ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exitInExpr(final InExprContext ctx) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exitInExpression(final InExpressionContext ctx) {
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isEOF(final TerminalNode node) {
|
private boolean isEOF(final TerminalNode node) {
|
||||||
return node.getSymbol().getType() < 0;
|
return node.getSymbol().getType() < 0;
|
||||||
}
|
}
|
||||||
@@ -283,6 +318,14 @@ public class QueryCompletionPdbLangParser extends PdbLangParser {
|
|||||||
public void reportContextSensitivity(final Parser recognizer, final DFA dfa, final int startIndex,
|
public void reportContextSensitivity(final Parser recognizer, final DFA dfa, final int startIndex,
|
||||||
final int stopIndex, final int prediction, final ATNConfigSet configs) {
|
final int stopIndex, final int prediction, final ATNConfigSet configs) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enterListOfPropValues(final ListOfPropValuesContext ctx) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exitListOfPropValues(final ListOfPropValuesContext ctx) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public QueryCompletionPdbLangParser(final TokenStream input) {
|
public QueryCompletionPdbLangParser(final TokenStream input) {
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import org.antlr.v4.runtime.tree.ParseTree;
|
|||||||
import org.antlr.v4.runtime.tree.ParseTreeListener;
|
import org.antlr.v4.runtime.tree.ParseTreeListener;
|
||||||
import org.antlr.v4.runtime.tree.ParseTreeWalker;
|
import org.antlr.v4.runtime.tree.ParseTreeWalker;
|
||||||
import org.lucares.pdb.datastore.lang.Expression.AndTemporary;
|
import org.lucares.pdb.datastore.lang.Expression.AndTemporary;
|
||||||
|
import org.lucares.pdb.datastore.lang.Expression.InExpression;
|
||||||
|
import org.lucares.pdb.datastore.lang.Expression.ListOfPropertyValues;
|
||||||
import org.lucares.pdb.datastore.lang.Expression.Not;
|
import org.lucares.pdb.datastore.lang.Expression.Not;
|
||||||
import org.lucares.pdb.datastore.lang.Expression.OrTemporary;
|
import org.lucares.pdb.datastore.lang.Expression.OrTemporary;
|
||||||
import org.lucares.pdb.datastore.lang.Expression.Property;
|
import org.lucares.pdb.datastore.lang.Expression.Property;
|
||||||
@@ -17,6 +19,8 @@ import org.lucares.pdb.datastore.lang.Expression.Terminal;
|
|||||||
import org.lucares.pdb.datastore.lang.PdbLangParser.BinaryAndExpressionContext;
|
import org.lucares.pdb.datastore.lang.PdbLangParser.BinaryAndExpressionContext;
|
||||||
import org.lucares.pdb.datastore.lang.PdbLangParser.BinaryOrExpressionContext;
|
import org.lucares.pdb.datastore.lang.PdbLangParser.BinaryOrExpressionContext;
|
||||||
import org.lucares.pdb.datastore.lang.PdbLangParser.IdentifierExpressionContext;
|
import org.lucares.pdb.datastore.lang.PdbLangParser.IdentifierExpressionContext;
|
||||||
|
import org.lucares.pdb.datastore.lang.PdbLangParser.InExpressionContext;
|
||||||
|
import org.lucares.pdb.datastore.lang.PdbLangParser.ListOfPropValuesContext;
|
||||||
import org.lucares.pdb.datastore.lang.PdbLangParser.NotExpressionContext;
|
import org.lucares.pdb.datastore.lang.PdbLangParser.NotExpressionContext;
|
||||||
import org.lucares.pdb.datastore.lang.PdbLangParser.PropertyExpressionContext;
|
import org.lucares.pdb.datastore.lang.PdbLangParser.PropertyExpressionContext;
|
||||||
import org.lucares.pdb.datastore.lang.PdbLangParser.PropertyTerminalExpressionContext;
|
import org.lucares.pdb.datastore.lang.PdbLangParser.PropertyTerminalExpressionContext;
|
||||||
@@ -105,6 +109,39 @@ public class QueryLanguage {
|
|||||||
|
|
||||||
stack.push(operation.toExpression(left, right));
|
stack.push(operation.toExpression(left, right));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exitListOfPropValues(final ListOfPropValuesContext ctx) {
|
||||||
|
final Expression topStackElement = stack.pop();
|
||||||
|
|
||||||
|
if (topStackElement instanceof ListOfPropertyValues) {
|
||||||
|
// there are at least two property values in the query
|
||||||
|
// e.g. in the expression "bird in (eagle, pigeon)"
|
||||||
|
final ListOfPropertyValues existingList = (ListOfPropertyValues) topStackElement;
|
||||||
|
final Terminal nextPropertyValue = (Terminal) stack.pop();
|
||||||
|
|
||||||
|
final ListOfPropertyValues newListOfPropertyValues = new ListOfPropertyValues(nextPropertyValue,
|
||||||
|
existingList);
|
||||||
|
stack.push(newListOfPropertyValues);
|
||||||
|
} else {
|
||||||
|
// this is the first or the only value in this list of property values
|
||||||
|
// e.g. in the expression "bird in (eagle)"
|
||||||
|
final Terminal propertyValue = (Terminal) topStackElement;
|
||||||
|
|
||||||
|
final ListOfPropertyValues newListOfPropertyValues = new ListOfPropertyValues(propertyValue);
|
||||||
|
stack.push(newListOfPropertyValues);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exitInExpression(final InExpressionContext ctx) {
|
||||||
|
|
||||||
|
final ListOfPropertyValues propertyValues = (ListOfPropertyValues) stack.pop();
|
||||||
|
final Terminal propertyName = (Terminal) stack.pop();
|
||||||
|
|
||||||
|
final InExpression inExpression = new InExpression(propertyName.getValue(), propertyValues.getValues());
|
||||||
|
stack.push(inExpression);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Specify our entry point
|
// Specify our entry point
|
||||||
|
|||||||
@@ -99,6 +99,13 @@ public class DataStoreTest {
|
|||||||
assertSearch("dog=lab*dor", labradorJenny, labradorTim);
|
assertSearch("dog=lab*dor", labradorJenny, labradorTim);
|
||||||
assertSearch("dog=*lab*dor*", labradorJenny, labradorTim);
|
assertSearch("dog=*lab*dor*", labradorJenny, labradorTim);
|
||||||
|
|
||||||
|
// 'in' queries
|
||||||
|
assertSearch("bird in (eagle, pigeon, flamingo)", eagleTim, pigeonJennifer, flamingoJennifer);
|
||||||
|
assertSearch("dog in (labrador) and name in (Tim, Jennifer)", labradorTim);
|
||||||
|
assertSearch("name in (Jenn*)", pigeonJennifer, flamingoJennifer, labradorJenny);
|
||||||
|
assertSearch("name in (*) and dog=labrador", labradorJenny, labradorTim);
|
||||||
|
assertSearch("name in (XYZ, *) and dog=labrador", labradorJenny, labradorTim);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testGetByTags() throws IOException {
|
public void testGetByTags() throws IOException {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import java.util.Map;
|
|||||||
import org.lucares.pdb.api.Tags;
|
import org.lucares.pdb.api.Tags;
|
||||||
import org.lucares.pdb.datastore.PdbDB;
|
import org.lucares.pdb.datastore.PdbDB;
|
||||||
import org.lucares.pdb.datastore.Proposal;
|
import org.lucares.pdb.datastore.Proposal;
|
||||||
|
import org.lucares.utils.CollectionUtils;
|
||||||
import org.lucares.utils.file.FileUtils;
|
import org.lucares.utils.file.FileUtils;
|
||||||
import org.testng.Assert;
|
import org.testng.Assert;
|
||||||
import org.testng.annotations.AfterClass;
|
import org.testng.annotations.AfterClass;
|
||||||
@@ -104,6 +105,25 @@ public class ProposerTest {
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testInExpressions() throws Exception {
|
||||||
|
assertProposals("name in (Timothy,)", 17, //
|
||||||
|
new Proposal("Jennifer", "name in (Timothy,Jennifer)", true, "name in (Timothy,Jennifer)", 25), //
|
||||||
|
new Proposal("Jenny", "name in (Timothy,Jenny)", true, "name in (Timothy,Jenny)", 22), //
|
||||||
|
new Proposal("Tim", "name in (Timothy,Tim)", true, "name in (Timothy,Tim)", 20), //
|
||||||
|
new Proposal("Timothy", "name in (Timothy,Timothy)", true, "name in (Timothy,Timothy)", 24)//
|
||||||
|
);
|
||||||
|
|
||||||
|
assertProposals("name in (Timothy, J)", 19, //
|
||||||
|
new Proposal("Jennifer", "name in (Timothy, Jennifer)", true, "name in (Timothy, Jennifer)", 26), //
|
||||||
|
new Proposal("Jenny", "name in (Timothy, Jenny)", true, "name in (Timothy, Jenny)", 23));
|
||||||
|
|
||||||
|
assertProposals("name in (Tim)", 12, //
|
||||||
|
new Proposal("Timothy", "name in (Timothy)", true, "name in (Timothy)", 16));
|
||||||
|
|
||||||
|
/*
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
public void testProposalOnEmptyValuePrefix() throws Exception {
|
public void testProposalOnEmptyValuePrefix() throws Exception {
|
||||||
assertProposals("name=", 5, //
|
assertProposals("name=", 5, //
|
||||||
new Proposal("Jennifer", "name=Jennifer ", true, "name=Jennifer ", 14), //
|
new Proposal("Jennifer", "name=Jennifer ", true, "name=Jennifer ", 14), //
|
||||||
@@ -129,8 +149,8 @@ public class ProposerTest {
|
|||||||
Collections.sort(expectedList);
|
Collections.sort(expectedList);
|
||||||
|
|
||||||
System.out.println("\n\n--- " + query + " ---");
|
System.out.println("\n\n--- " + query + " ---");
|
||||||
System.out.println("actual : " + actual);
|
System.out.println("actual : " + String.join("\n", CollectionUtils.map(actual, Proposal::toString)));
|
||||||
System.out.println("expected: " + expectedList);
|
System.out.println("expected: " + String.join("\n", CollectionUtils.map(expectedList, Proposal::toString)));
|
||||||
Assert.assertEquals(actual, expectedList);
|
Assert.assertEquals(actual, expectedList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user