mirror of
https://github.com/querydsl/querydsl.git
synced 2026-06-24 21:07:26 +08:00
Implemented support for Long and Integer -based numeric range queries. I also added support for greater and smaller than and greater or equal and less or equal.
This commit is contained in:
parent
18235ec5de
commit
0fdb8d63fe
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (c) 2010 Mysema Ltd.
|
||||
* All rights reserved.
|
||||
*
|
||||
*
|
||||
*/
|
||||
package com.mysema.query.lucene;
|
||||
|
||||
@ -15,6 +15,7 @@ import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.queryParser.QueryParser;
|
||||
import org.apache.lucene.search.BooleanClause;
|
||||
import org.apache.lucene.search.BooleanQuery;
|
||||
import org.apache.lucene.search.NumericRangeQuery;
|
||||
import org.apache.lucene.search.PhraseQuery;
|
||||
import org.apache.lucene.search.PrefixQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
@ -24,6 +25,7 @@ import org.apache.lucene.search.TermQuery;
|
||||
import org.apache.lucene.search.TermRangeQuery;
|
||||
import org.apache.lucene.search.WildcardQuery;
|
||||
import org.apache.lucene.search.BooleanClause.Occur;
|
||||
import org.apache.lucene.util.NumericUtils;
|
||||
|
||||
import com.mysema.query.types.Constant;
|
||||
import com.mysema.query.types.Expr;
|
||||
@ -39,12 +41,13 @@ import com.mysema.query.types.Path;
|
||||
* @author vema
|
||||
*
|
||||
*/
|
||||
// TODO Add support for longs, floats etc.
|
||||
public class LuceneSerializer {
|
||||
|
||||
public static final LuceneSerializer DEFAULT = new LuceneSerializer(false,true);
|
||||
|
||||
|
||||
public static final LuceneSerializer DEFAULT = new LuceneSerializer(false, true);
|
||||
|
||||
private final boolean lowerCase;
|
||||
|
||||
|
||||
private final boolean splitTerms;
|
||||
|
||||
protected LuceneSerializer(boolean lowerCase, boolean splitTerms) {
|
||||
@ -73,11 +76,19 @@ public class LuceneSerializer {
|
||||
} else if (op == Ops.ENDS_WITH || op == Ops.ENDS_WITH_IC) {
|
||||
return endsWith(operation);
|
||||
} else if (op == Ops.STRING_CONTAINS || op == Ops.STRING_CONTAINS_IC) {
|
||||
return stringContains(operation);
|
||||
return stringContains(operation);
|
||||
} else if (op == Ops.BETWEEN) {
|
||||
return between(operation);
|
||||
} else if (op == Ops.IN){
|
||||
} else if (op == Ops.IN) {
|
||||
return in(operation);
|
||||
} else if (op == Ops.LT || op == Ops.BEFORE) {
|
||||
return lt(operation);
|
||||
} else if (op == Ops.GT || op == Ops.AFTER) {
|
||||
return gt(operation);
|
||||
} else if (op == Ops.LOE || op == Ops.BOE) {
|
||||
return le(operation);
|
||||
} else if (op == Ops.GOE || op == Ops.AOE) {
|
||||
return ge(operation);
|
||||
}
|
||||
throw new UnsupportedOperationException("Illegal operation " + operation);
|
||||
}
|
||||
@ -109,11 +120,17 @@ public class LuceneSerializer {
|
||||
private Query eq(Operation<?> operation) {
|
||||
verifyArguments(operation);
|
||||
String field = toField(operation.getArg(0));
|
||||
String[] terms = createTerms(operation.getArg(1));
|
||||
return eq(field, terms);
|
||||
if (operation.getArg(1).getType().equals(Integer.class)) {
|
||||
return new TermQuery(new Term(field, NumericUtils
|
||||
.intToPrefixCoded(((Constant<Integer>) operation.getArg(1)).getConstant())));
|
||||
} else if (operation.getArg(1).getType().equals(Double.class)) {
|
||||
return new TermQuery(new Term(field, NumericUtils
|
||||
.doubleToPrefixCoded(((Constant<Double>) operation.getArg(1)).getConstant())));
|
||||
}
|
||||
return eq(field, createTerms(operation.getArg(1)));
|
||||
}
|
||||
|
||||
private Query eq(String field, String[] terms){
|
||||
|
||||
private Query eq(String field, String[] terms) {
|
||||
if (terms.length > 1) {
|
||||
PhraseQuery pq = new PhraseQuery();
|
||||
for (String s : terms) {
|
||||
@ -121,21 +138,20 @@ public class LuceneSerializer {
|
||||
}
|
||||
return pq;
|
||||
}
|
||||
return new TermQuery(new Term(field, normalize(terms[0])));
|
||||
return new TermQuery(new Term(field, normalize(terms[0])));
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Query in(Operation<?> operation){
|
||||
private Query in(Operation<?> operation) {
|
||||
String field = toField(operation.getArg(0));
|
||||
Collection values = (Collection) ((Constant)operation.getArg(1)).getConstant();
|
||||
Collection values = (Collection) ((Constant) operation.getArg(1)).getConstant();
|
||||
BooleanQuery bq = new BooleanQuery();
|
||||
for (Object value : values){
|
||||
for (Object value : values) {
|
||||
bq.add(eq(field, StringUtils.split(value.toString())), Occur.SHOULD);
|
||||
}
|
||||
return bq;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private Query ne(Operation<?> operation) {
|
||||
BooleanQuery bq = new BooleanQuery();
|
||||
bq.add(new BooleanClause(eq(operation), Occur.MUST_NOT));
|
||||
@ -189,26 +205,75 @@ public class LuceneSerializer {
|
||||
private Query between(Operation<?> operation) {
|
||||
verifyArguments(operation);
|
||||
// TODO Phrase not properly supported
|
||||
String field = toField(operation.getArg(0));
|
||||
String[] lowerTerms = createTerms(operation.getArg(1));
|
||||
String[] upperTerms = createTerms(operation.getArg(2));
|
||||
return new TermRangeQuery(field, normalize(lowerTerms[0]), normalize(upperTerms[0]), true,
|
||||
true);
|
||||
return range(toField(operation.getArg(0)), operation.getArg(1), operation.getArg(2), true, true);
|
||||
}
|
||||
|
||||
private Query lt(Operation<?> operation) {
|
||||
verifyArguments(operation);
|
||||
return range(toField(operation.getArg(0)), null, operation.getArg(1), false, false);
|
||||
}
|
||||
|
||||
private Query gt(Operation<?> operation) {
|
||||
verifyArguments(operation);
|
||||
return range(toField(operation.getArg(0)), operation.getArg(1), null, false, false);
|
||||
}
|
||||
|
||||
private Query le(Operation<?> operation) {
|
||||
verifyArguments(operation);
|
||||
return range(toField(operation.getArg(0)), null, operation.getArg(1), true, true);
|
||||
}
|
||||
|
||||
private Query ge(Operation<?> operation) {
|
||||
verifyArguments(operation);
|
||||
return range(toField(operation.getArg(0)), operation.getArg(1), null, true, true);
|
||||
}
|
||||
|
||||
// TODO Simplify(?)
|
||||
// TODO Timo: Check if the the annotation is necessary, thanks! -vema
|
||||
@SuppressWarnings("unchecked")
|
||||
private Query range(String field, Expr<?> min, Expr<?> max, boolean minInc, boolean maxInc) {
|
||||
if (min != null && min.getType().equals(Integer.class) || max != null && max.getType().equals(Integer.class)) {
|
||||
return integerRange(field, min == null ? null : ((Constant<Integer>) min).getConstant(), max == null ? null : ((Constant<Integer>) max).getConstant(), minInc, maxInc);
|
||||
} else if (min != null && min.getType().equals(Double.class) || max != null && max.getType().equals(Double.class)) {
|
||||
return doubleRange(field, min == null ? null : ((Constant<Double>) min).getConstant(), max == null ? null : ((Constant<Double>) max).getConstant(), minInc, maxInc);
|
||||
} else {
|
||||
return stringRange(field, min, max, minInc, maxInc);
|
||||
}
|
||||
}
|
||||
|
||||
private Query integerRange(String field, Integer min, Integer max, boolean minInc, boolean maxInc) {
|
||||
return NumericRangeQuery.newIntRange(field, min, max, minInc, maxInc);
|
||||
}
|
||||
|
||||
private Query doubleRange(String field, Double min, Double max, boolean minInc, boolean maxInc) {
|
||||
return NumericRangeQuery.newDoubleRange(field, min, max, minInc, maxInc);
|
||||
}
|
||||
|
||||
private Query stringRange(String field, Expr<?> min, Expr<?> max, boolean minInc, boolean maxInc) {
|
||||
if (min == null) {
|
||||
return new TermRangeQuery(field, null, normalize(createTerms(max)[0]), minInc,
|
||||
maxInc);
|
||||
} else if (max == null) {
|
||||
return new TermRangeQuery(field, normalize(createTerms(min)[0]), null, minInc,
|
||||
maxInc);
|
||||
}
|
||||
return new TermRangeQuery(field, normalize(createTerms(min)[0]), normalize(createTerms(max)[0]), minInc,
|
||||
maxInc);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private String toField(Expr<?> expr){
|
||||
if (expr instanceof Path){
|
||||
return toField((Path<?>)expr);
|
||||
}else if (expr instanceof Operation){
|
||||
private String toField(Expr<?> expr) {
|
||||
if (expr instanceof Path) {
|
||||
return toField((Path<?>) expr);
|
||||
} else if (expr instanceof Operation) {
|
||||
Operation<?> operation = (Operation<?>) expr;
|
||||
if (operation.getOperator() == Ops.LOWER || operation.getOperator() == Ops.UPPER){
|
||||
if (operation.getOperator() == Ops.LOWER || operation.getOperator() == Ops.UPPER) {
|
||||
return toField(operation.getArg(0));
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unable to transform " + expr + " to field");
|
||||
}
|
||||
|
||||
|
||||
public String toField(Path<?> path) {
|
||||
return path.getMetadata().getExpression().toString();
|
||||
}
|
||||
@ -216,27 +281,27 @@ public class LuceneSerializer {
|
||||
private void verifyArguments(Operation<?> operation) {
|
||||
List<Expr<?>> arguments = operation.getArgs();
|
||||
for (int i = 1; i < arguments.size(); ++i) {
|
||||
if (!(arguments.get(i) instanceof Constant<?>) && !(arguments.get(i) instanceof PhraseElement)) {
|
||||
throw new IllegalArgumentException("operation argument was not of type Constant nor PhraseElement.");
|
||||
if (!(arguments.get(i) instanceof Constant<?>)
|
||||
&& !(arguments.get(i) instanceof PhraseElement)) {
|
||||
throw new IllegalArgumentException(
|
||||
"operation argument was not of type Constant nor PhraseElement.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String[] createTerms(Expr<?> expr) {
|
||||
if (splitTerms || expr instanceof PhraseElement){
|
||||
if (splitTerms || expr instanceof PhraseElement) {
|
||||
return StringUtils.split(expr.toString());
|
||||
}else{
|
||||
return new String[]{expr.toString()};
|
||||
}
|
||||
return new String[] { expr.toString() };
|
||||
}
|
||||
|
||||
private String[] createEscapedTerms(Expr<?> expr) {
|
||||
String escaped = QueryParser.escape(expr.toString());
|
||||
if (splitTerms || expr instanceof PhraseElement){
|
||||
if (splitTerms || expr instanceof PhraseElement) {
|
||||
return StringUtils.split(escaped);
|
||||
}else{
|
||||
return new String[]{escaped};
|
||||
}
|
||||
return new String[] { escaped };
|
||||
}
|
||||
|
||||
private String normalize(String s) {
|
||||
@ -246,25 +311,32 @@ public class LuceneSerializer {
|
||||
public Query toQuery(Expr<?> expr) {
|
||||
if (expr instanceof Operation<?>) {
|
||||
return toQuery((Operation<?>) expr);
|
||||
}else if (expr instanceof QueryElement){
|
||||
return ((QueryElement)expr).getQuery();
|
||||
} else if (expr instanceof QueryElement) {
|
||||
return ((QueryElement) expr).getQuery();
|
||||
}
|
||||
throw new IllegalArgumentException("expr was not of type Operation or QueryElement");
|
||||
}
|
||||
|
||||
public Sort toSort(List<OrderSpecifier<?>> orderBys){
|
||||
// TODO Add support for sorting floats, longs etc.
|
||||
public Sort toSort(List<OrderSpecifier<?>> orderBys) {
|
||||
List<SortField> sortFields = new ArrayList<SortField>(orderBys.size());
|
||||
for (OrderSpecifier<?> orderSpecifier : orderBys) {
|
||||
if (!(orderSpecifier.getTarget() instanceof Path<?>)) {
|
||||
throw new IllegalArgumentException("argument was not of type Path.");
|
||||
}
|
||||
sortFields.add(new SortField(toField((Path<?>)orderSpecifier.getTarget()),
|
||||
Locale.ENGLISH,
|
||||
!orderSpecifier.isAscending()));
|
||||
if (orderSpecifier.getTarget().getType().equals(Integer.class)) {
|
||||
sortFields.add(new SortField(toField((Path<?>) orderSpecifier.getTarget()),
|
||||
SortField.INT, !orderSpecifier.isAscending()));
|
||||
} else if (orderSpecifier.getTarget().getType().equals(Double.class)) {
|
||||
sortFields.add(new SortField(toField((Path<?>) orderSpecifier.getTarget()),
|
||||
SortField.DOUBLE, !orderSpecifier.isAscending()));
|
||||
} else {
|
||||
sortFields.add(new SortField(toField((Path<?>) orderSpecifier.getTarget()),
|
||||
Locale.ENGLISH, !orderSpecifier.isAscending()));
|
||||
}
|
||||
}
|
||||
Sort sort = new Sort();
|
||||
sort.setSort(sortFields.toArray(new SortField[sortFields.size()]));
|
||||
return sort;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -5,8 +5,8 @@
|
||||
*/
|
||||
package com.mysema.query.lucene;
|
||||
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.createMockBuilder;
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.replay;
|
||||
import static org.easymock.EasyMock.verify;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@ -19,6 +19,7 @@ import java.util.List;
|
||||
import org.apache.lucene.analysis.standard.StandardAnalyzer;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.document.NumericField;
|
||||
import org.apache.lucene.document.Field.Index;
|
||||
import org.apache.lucene.document.Field.Store;
|
||||
import org.apache.lucene.index.IndexWriter;
|
||||
@ -35,6 +36,7 @@ import com.mysema.query.QueryException;
|
||||
import com.mysema.query.QueryModifiers;
|
||||
import com.mysema.query.SearchResults;
|
||||
import com.mysema.query.types.path.PEntity;
|
||||
import com.mysema.query.types.path.PNumber;
|
||||
import com.mysema.query.types.path.PString;
|
||||
import com.mysema.query.types.path.PathMetadataFactory;
|
||||
|
||||
@ -46,7 +48,7 @@ import com.mysema.query.types.path.PathMetadataFactory;
|
||||
*/
|
||||
public class LuceneQueryTest {
|
||||
|
||||
public class QDocument extends PEntity<Document>{
|
||||
public class QDocument extends PEntity<Document> {
|
||||
|
||||
private static final long serialVersionUID = -4872833626508344081L;
|
||||
|
||||
@ -54,28 +56,29 @@ public class LuceneQueryTest {
|
||||
super(Document.class, PathMetadataFactory.forVariable(var));
|
||||
}
|
||||
|
||||
public final PString year = createString("year");
|
||||
|
||||
public final PNumber<Integer> year = createNumber("year", Integer.class);
|
||||
public final PString title = createString("title");
|
||||
|
||||
public final PNumber<Double> gross = createNumber("gross", Double.class);
|
||||
}
|
||||
|
||||
private LuceneQuery query;
|
||||
// private PathBuilder<Object> entityPath;
|
||||
private PString title;
|
||||
private PString year;
|
||||
private PNumber<Integer> year;
|
||||
private PNumber<Double> gross;
|
||||
|
||||
private RAMDirectory idx;
|
||||
private IndexWriter writer;
|
||||
private Searcher searcher;
|
||||
|
||||
private Document createDocument(String docTitle, String docAuthor, String docText, String docYear) {
|
||||
private Document createDocument(String docTitle, String docAuthor, String docText, int docYear,
|
||||
double docGross) {
|
||||
Document doc = new Document();
|
||||
|
||||
doc.add(new Field("title", docTitle, Store.YES, Index.ANALYZED));
|
||||
doc.add(new Field("author", docAuthor, Store.YES, Index.ANALYZED));
|
||||
doc.add(new Field("text", docText, Store.YES, Index.ANALYZED));
|
||||
doc.add(new Field("year", docYear, Store.YES, Index.ANALYZED));
|
||||
doc.add(new NumericField("year", Store.YES, true).setIntValue(docYear));
|
||||
doc.add(new NumericField("gross", Store.YES, true).setDoubleValue(docGross));
|
||||
|
||||
return doc;
|
||||
}
|
||||
@ -85,30 +88,31 @@ public class LuceneQueryTest {
|
||||
QDocument entityPath = new QDocument("doc");
|
||||
title = entityPath.title;
|
||||
year = entityPath.year;
|
||||
gross = entityPath.gross;
|
||||
|
||||
idx = new RAMDirectory();
|
||||
writer = new IndexWriter(idx, new StandardAnalyzer(Version.LUCENE_CURRENT), true, MaxFieldLength.UNLIMITED);
|
||||
writer = new IndexWriter(idx, new StandardAnalyzer(Version.LUCENE_CURRENT), true,
|
||||
MaxFieldLength.UNLIMITED);
|
||||
|
||||
writer.addDocument(createDocument("Jurassic Park", "Michael Crichton",
|
||||
"It's a UNIX system! I know this!", "1990"));
|
||||
"It's a UNIX system! I know this!", 1990, 90.00));
|
||||
writer.addDocument(createDocument("Nummisuutarit", "Aleksis Kivi",
|
||||
"ESKO. Ja iloitset ja riemuitset?", "1864"));
|
||||
writer.addDocument(createDocument(
|
||||
"ESKO. Ja iloitset ja riemuitset?", 1864, 10.00));
|
||||
writer
|
||||
.addDocument(createDocument(
|
||||
"The Lord of the Rings",
|
||||
"John R. R. Tolkien",
|
||||
"One Ring to rule them all, One Ring to find them, One Ring to bring them all and in the darkness bind them",
|
||||
"1954"));
|
||||
writer.addDocument(createDocument(
|
||||
"Introduction to Algorithms",
|
||||
1954, 89.00));
|
||||
writer.addDocument(createDocument("Introduction to Algorithms",
|
||||
"Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford Stein",
|
||||
"Bubble sort",
|
||||
"1990"));
|
||||
"Bubble sort", 1990, 30.50));
|
||||
|
||||
writer.optimize();
|
||||
writer.close();
|
||||
|
||||
searcher = new IndexSearcher(idx);
|
||||
query = new LuceneQuery(new LuceneSerializer(true,true), searcher);
|
||||
query = new LuceneQuery(new LuceneSerializer(true, true), searcher);
|
||||
}
|
||||
|
||||
@After
|
||||
@ -129,13 +133,13 @@ public class LuceneQueryTest {
|
||||
|
||||
@Test
|
||||
public void countDistinct() {
|
||||
query.where(year.like("19*").or(title.like("The Lord*")));
|
||||
query.where(year.between(1900, 3000));
|
||||
assertEquals(3, query.countDistinct());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void list_Sorted_By_Year_Ascending() {
|
||||
query.where(year.between("1800", "2000"));
|
||||
query.where(year.between(1800, 2000));
|
||||
query.orderBy(year.asc());
|
||||
List<Document> documents = query.list();
|
||||
assertFalse(documents.isEmpty());
|
||||
@ -144,7 +148,7 @@ public class LuceneQueryTest {
|
||||
|
||||
@Test
|
||||
public void list_Not_Sorted() {
|
||||
query.where(year.between("1800", "2000"));
|
||||
query.where(year.between(1800, 2000));
|
||||
List<Document> documents = query.list();
|
||||
assertFalse(documents.isEmpty());
|
||||
assertEquals(4, documents.size());
|
||||
@ -152,7 +156,7 @@ public class LuceneQueryTest {
|
||||
|
||||
@Test
|
||||
public void list_Not_Sorted_Limit_2() {
|
||||
query.where(year.between("1800", "2000"));
|
||||
query.where(year.between(1800, 2000));
|
||||
query.limit(2);
|
||||
List<Document> documents = query.list();
|
||||
assertFalse(documents.isEmpty());
|
||||
@ -161,7 +165,7 @@ public class LuceneQueryTest {
|
||||
|
||||
@Test
|
||||
public void list_Sorted_By_Year_Limit_1() {
|
||||
query.where(year.between("1800", "2000"));
|
||||
query.where(year.between(1800, 2000));
|
||||
query.limit(1);
|
||||
query.orderBy(year.asc());
|
||||
List<Document> documents = query.list();
|
||||
@ -171,7 +175,7 @@ public class LuceneQueryTest {
|
||||
|
||||
@Test
|
||||
public void list_Not_Sorted_Offset_2() {
|
||||
query.where(year.between("1800", "2000"));
|
||||
query.where(year.between(1800, 2000));
|
||||
query.offset(2);
|
||||
List<Document> documents = query.list();
|
||||
assertFalse(documents.isEmpty());
|
||||
@ -180,7 +184,7 @@ public class LuceneQueryTest {
|
||||
|
||||
@Test
|
||||
public void list_Sorted_Ascending_By_Year_Offset_2() {
|
||||
query.where(year.between("1800", "2000"));
|
||||
query.where(year.between(1800, 2000));
|
||||
query.offset(2);
|
||||
query.orderBy(year.asc());
|
||||
List<Document> documents = query.list();
|
||||
@ -192,7 +196,7 @@ public class LuceneQueryTest {
|
||||
|
||||
@Test
|
||||
public void list_Sorted_Ascending_By_Year_Restrict_Limit_2_Offset_1() {
|
||||
query.where(year.between("1800", "2000"));
|
||||
query.where(year.between(1800, 2000));
|
||||
query.restrict(new QueryModifiers(2l, 1l));
|
||||
query.orderBy(year.asc());
|
||||
List<Document> documents = query.list();
|
||||
@ -204,7 +208,7 @@ public class LuceneQueryTest {
|
||||
|
||||
@Test
|
||||
public void list_Sorted_Ascending_By_Year() {
|
||||
query.where(year.between("1800", "2000"));
|
||||
query.where(year.between(1800, 2000));
|
||||
query.orderBy(year.asc());
|
||||
List<Document> documents = query.list();
|
||||
assertFalse(documents.isEmpty());
|
||||
@ -217,7 +221,7 @@ public class LuceneQueryTest {
|
||||
|
||||
@Test
|
||||
public void list_Sorted_Descending_By_Year() {
|
||||
query.where(year.between("1800", "2000"));
|
||||
query.where(year.between(1800, 2000));
|
||||
query.orderBy(year.desc());
|
||||
List<Document> documents = query.list();
|
||||
assertFalse(documents.isEmpty());
|
||||
@ -228,9 +232,22 @@ public class LuceneQueryTest {
|
||||
assertEquals("1864", documents.get(3).get("year"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void list_Sorted_Descending_By_Gross() {
|
||||
query.where(gross.between(0.0, 1000.00));
|
||||
query.orderBy(gross.desc());
|
||||
List<Document> documents = query.list();
|
||||
assertFalse(documents.isEmpty());
|
||||
assertEquals(4, documents.size());
|
||||
assertEquals("90.0", documents.get(0).get("gross"));
|
||||
assertEquals("89.0", documents.get(1).get("gross"));
|
||||
assertEquals("30.5", documents.get(2).get("gross"));
|
||||
assertEquals("10.0", documents.get(3).get("gross"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void list_Sorted_Descending_By_Year_And_Ascending_By_Title() {
|
||||
query.where(year.between("1800", "2000"));
|
||||
query.where(year.between(1800, 2000));
|
||||
query.orderBy(year.desc());
|
||||
query.orderBy(title.asc());
|
||||
List<Document> documents = query.list();
|
||||
@ -244,7 +261,7 @@ public class LuceneQueryTest {
|
||||
|
||||
@Test
|
||||
public void list_Sorted_Descending_By_Year_And_Descending_By_Title() {
|
||||
query.where(year.between("1800", "2000"));
|
||||
query.where(year.between(1800, 2000));
|
||||
query.orderBy(year.desc());
|
||||
query.orderBy(title.desc());
|
||||
List<Document> documents = query.list();
|
||||
@ -265,39 +282,39 @@ public class LuceneQueryTest {
|
||||
|
||||
@Test(expected = QueryException.class)
|
||||
public void uniqueResult_Finds_More_Than_One_Result() {
|
||||
query.where(year.eq("1990"));
|
||||
query.where(year.eq(1990));
|
||||
query.uniqueResult();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void uniqueResult_Finds_No_Results() {
|
||||
query.where(year.eq("2200"));
|
||||
query.where(year.eq(2200));
|
||||
assertNull(query.uniqueResult());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void uniqueResult_Finds_No_Results_Because_No_Documents_In_Index() throws IOException {
|
||||
searcher = createMockBuilder(IndexSearcher.class).addMockedMethod("maxDoc").createMock();
|
||||
query = new LuceneQuery(new LuceneSerializer(true,true), searcher);
|
||||
query = new LuceneQuery(new LuceneSerializer(true, true), searcher);
|
||||
expect(searcher.maxDoc()).andReturn(0);
|
||||
replay(searcher);
|
||||
assertNull(query.where(year.eq("3000")).uniqueResult());
|
||||
assertNull(query.where(year.eq(3000)).uniqueResult());
|
||||
verify(searcher);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void count_Returns_0_Because_No_Documents_In_Index() throws IOException {
|
||||
searcher = createMockBuilder(IndexSearcher.class).addMockedMethod("maxDoc").createMock();
|
||||
query = new LuceneQuery(new LuceneSerializer(true,true), searcher);
|
||||
query = new LuceneQuery(new LuceneSerializer(true, true), searcher);
|
||||
expect(searcher.maxDoc()).andReturn(0);
|
||||
replay(searcher);
|
||||
assertEquals(0, query.where(year.eq("3000")).count());
|
||||
assertEquals(0, query.where(year.eq(3000)).count());
|
||||
verify(searcher);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listDistinct() {
|
||||
query.where(year.between("1900", "2000").or(title.startsWith("Jura")));
|
||||
query.where(year.between(1900, 2000).or(title.startsWith("Jura")));
|
||||
query.orderBy(year.asc());
|
||||
List<Document> documents = query.listDistinct();
|
||||
assertFalse(documents.isEmpty());
|
||||
@ -306,7 +323,7 @@ public class LuceneQueryTest {
|
||||
|
||||
@Test
|
||||
public void listResults() {
|
||||
query.where(year.between("1800", "2000"));
|
||||
query.where(year.between(1800, 2000));
|
||||
query.restrict(new QueryModifiers(2l, 1l));
|
||||
query.orderBy(year.asc());
|
||||
SearchResults<Document> results = query.listResults();
|
||||
@ -320,7 +337,7 @@ public class LuceneQueryTest {
|
||||
|
||||
@Test
|
||||
public void listDistinctResults() {
|
||||
query.where(year.between("1800", "2000").or(title.eq("The Lord of the Rings")));
|
||||
query.where(year.between(1800, 2000).or(title.eq("The Lord of the Rings")));
|
||||
query.restrict(new QueryModifiers(1l, 1l));
|
||||
query.orderBy(year.asc());
|
||||
SearchResults<Document> results = query.listDistinctResults();
|
||||
@ -333,13 +350,14 @@ public class LuceneQueryTest {
|
||||
|
||||
@Test
|
||||
public void list_All() {
|
||||
List<Document> results = query.where(title.like("*")).orderBy(title.asc(), year.desc()).list();
|
||||
List<Document> results = query.where(title.like("*")).orderBy(title.asc(), year.desc())
|
||||
.list();
|
||||
assertEquals(4, results.size());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void list_Sorted_Ascending_Limit_Negative() {
|
||||
query.where(year.between("1800", "2000"));
|
||||
query.where(year.between(1800, 2000));
|
||||
query.limit(-1);
|
||||
query.orderBy(year.asc());
|
||||
query.list();
|
||||
@ -347,14 +365,14 @@ public class LuceneQueryTest {
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void list_Not_Sorted_Limit_Negative() {
|
||||
query.where(year.between("1800", "2000"));
|
||||
query.where(year.between(1800, 2000));
|
||||
query.limit(-1);
|
||||
query.list();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void list_Sorted_Ascending_Limit_0() {
|
||||
query.where(year.between("1800", "2000"));
|
||||
query.where(year.between(1800, 2000));
|
||||
query.limit(0);
|
||||
query.orderBy(year.asc());
|
||||
query.list();
|
||||
@ -362,14 +380,14 @@ public class LuceneQueryTest {
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void list_Not_Sorted_Limit_0() {
|
||||
query.where(year.between("1800", "2000"));
|
||||
query.where(year.between(1800, 2000));
|
||||
query.limit(0);
|
||||
query.list();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void list_Sorted_Ascending_Offset_Negative() {
|
||||
query.where(year.between("1800", "2000"));
|
||||
query.where(year.between(1800, 2000));
|
||||
query.offset(-1);
|
||||
query.orderBy(year.asc());
|
||||
query.list();
|
||||
@ -377,14 +395,14 @@ public class LuceneQueryTest {
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void list_Not_Sorted_Offset_Negative() {
|
||||
query.where(year.between("1800", "2000"));
|
||||
query.where(year.between(1800, 2000));
|
||||
query.offset(-1);
|
||||
query.list();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void list_Sorted_Ascending_Offset_0() {
|
||||
query.where(year.between("1800", "2000"));
|
||||
query.where(year.between(1800, 2000));
|
||||
query.offset(0);
|
||||
query.orderBy(year.asc());
|
||||
List<Document> documents = query.list();
|
||||
@ -394,7 +412,7 @@ public class LuceneQueryTest {
|
||||
|
||||
@Test
|
||||
public void list_Not_Sorted_Offset_0() {
|
||||
query.where(year.between("1800", "2000"));
|
||||
query.where(year.between(1800, 2000));
|
||||
query.offset(0);
|
||||
List<Document> documents = query.list();
|
||||
assertFalse(documents.isEmpty());
|
||||
|
||||
@ -14,6 +14,8 @@ import java.util.Arrays;
|
||||
import org.apache.lucene.analysis.standard.StandardAnalyzer;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.document.NumericField;
|
||||
import org.apache.lucene.document.Field.Store;
|
||||
import org.apache.lucene.index.IndexWriter;
|
||||
import org.apache.lucene.index.IndexWriter.MaxFieldLength;
|
||||
import org.apache.lucene.search.IndexSearcher;
|
||||
@ -21,6 +23,7 @@ import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.Searcher;
|
||||
import org.apache.lucene.search.TopDocs;
|
||||
import org.apache.lucene.store.RAMDirectory;
|
||||
import org.apache.lucene.util.NumericUtils;
|
||||
import org.apache.lucene.util.Version;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
@ -33,6 +36,7 @@ import com.mysema.query.Target;
|
||||
import com.mysema.query.types.Expr;
|
||||
import com.mysema.query.types.expr.EBoolean;
|
||||
import com.mysema.query.types.expr.EStringConst;
|
||||
import com.mysema.query.types.path.PNumber;
|
||||
import com.mysema.query.types.path.PString;
|
||||
import com.mysema.query.types.path.PathBuilder;
|
||||
|
||||
@ -48,7 +52,12 @@ public class LuceneSerializerTest {
|
||||
private PString title;
|
||||
private PString author;
|
||||
private PString text;
|
||||
private PString year;
|
||||
private PString rating;
|
||||
private PNumber<Integer> year;
|
||||
private PNumber<Double> gross;
|
||||
|
||||
private static final String YEAR_PREFIX_CODED = NumericUtils.intToPrefixCoded(1990);
|
||||
private static final String GROSS_PREFIX_CODED = NumericUtils.doubleToPrefixCoded(900.00);
|
||||
|
||||
private RAMDirectory idx;
|
||||
private IndexWriter writer;
|
||||
@ -60,7 +69,9 @@ public class LuceneSerializerTest {
|
||||
doc.add(new Field("title", new StringReader("Jurassic Park")));
|
||||
doc.add(new Field("author", new StringReader("Michael Crichton")));
|
||||
doc.add(new Field("text", new StringReader("It's a UNIX system! I know this!")));
|
||||
doc.add(new Field("year", new StringReader("1990")));
|
||||
doc.add(new Field("rating", new StringReader("Good")));
|
||||
doc.add(new NumericField("year", Store.YES, true).setIntValue(1990));
|
||||
doc.add(new NumericField("gross", Store.YES, true).setDoubleValue(900.00));
|
||||
|
||||
return doc;
|
||||
}
|
||||
@ -73,7 +84,9 @@ public class LuceneSerializerTest {
|
||||
title = entityPath.getString("title");
|
||||
author = entityPath.getString("author");
|
||||
text = entityPath.getString("text");
|
||||
year = entityPath.getString("year");
|
||||
year = entityPath.getNumber("year", Integer.class);
|
||||
rating = entityPath.getString("rating");
|
||||
gross = entityPath.getNumber("gross", Double.class);
|
||||
|
||||
idx = new RAMDirectory();
|
||||
writer = new IndexWriter(idx, new StandardAnalyzer(Version.LUCENE_CURRENT), true, MaxFieldLength.UNLIMITED);
|
||||
@ -103,12 +116,12 @@ public class LuceneSerializerTest {
|
||||
assertEquals(expectedHits, docs.totalHits);
|
||||
assertEquals(expectedQuery, query.toString());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void queryElement() throws Exception{
|
||||
Query query1 = serializer.toQuery(author.like("Michael"));
|
||||
Query query2 = serializer.toQuery(text.like("Text"));
|
||||
|
||||
|
||||
EBoolean query = EBoolean.anyOf(
|
||||
new QueryElement(query1),
|
||||
new QueryElement(query2)
|
||||
@ -138,17 +151,27 @@ public class LuceneSerializerTest {
|
||||
|
||||
@Test
|
||||
public void like_or_like() throws Exception {
|
||||
testQuery(title.like("House").or(year.like("*99*")), "title:house year:*99*", 1);
|
||||
testQuery(title.like("House").or(author.like("*ichae*")), "title:house author:*ichae*", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void like_and_like() throws Exception {
|
||||
testQuery(title.like("*assic*").and(year.like("199?")), "+title:*assic* +year:199?", 1);
|
||||
testQuery(title.like("*assic*").and(rating.like("G?od")), "+title:*assic* +rating:g?od", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void eq() throws Exception {
|
||||
testQuery(year.eq("1990"), "year:1990", 1);
|
||||
testQuery(rating.eq("Good"), "rating:good", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void eq_Numeric_Integer() throws Exception {
|
||||
testQuery(year.eq(1990), "year:" + YEAR_PREFIX_CODED, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void eq_Numeric_Double() throws Exception {
|
||||
testQuery(gross.eq(900.00), "gross:" + GROSS_PREFIX_CODED, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -158,27 +181,27 @@ public class LuceneSerializerTest {
|
||||
|
||||
@Test
|
||||
public void eq_or_eq() throws Exception {
|
||||
testQuery(title.eq("House").or(year.eq("1990")), "title:house year:1990", 1);
|
||||
testQuery(title.eq("House").or(year.eq(1990)), "title:house year:" + YEAR_PREFIX_CODED, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void eq_and_eq() throws Exception {
|
||||
testQuery(title.eq("Jurassic Park").and(year.eq("1990")), "+title:\"jurassic park\" +year:1990", 1);
|
||||
testQuery(title.eq("Jurassic Park").and(year.eq(1990)), "+title:\"jurassic park\" +year:" + YEAR_PREFIX_CODED, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void eq_and_eq_and_eq() throws Exception {
|
||||
testQuery(title.eq("Jurassic Park").and(year.eq("1990")).and(author.eq("Michael Crichton")), "+(+title:\"jurassic park\" +year:1990) +author:\"michael crichton\"", 1);
|
||||
testQuery(title.eq("Jurassic Park").and(year.eq(1990)).and(author.eq("Michael Crichton")), "+(+title:\"jurassic park\" +year:" + YEAR_PREFIX_CODED + ") +author:\"michael crichton\"", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void eq_and_eq_or_eq() throws Exception {
|
||||
testQuery(title.eq("Jurassic Park").and(year.eq("190")).or(author.eq("Michael Crichton")), "(+title:\"jurassic park\" +year:190) author:\"michael crichton\"", 1);
|
||||
testQuery(title.eq("Jurassic Park").and(rating.eq("Bad")).or(author.eq("Michael Crichton")), "(+title:\"jurassic park\" +rating:bad) author:\"michael crichton\"", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void eq_or_eq_and_eq_Does_Not_Find_Results() throws Exception {
|
||||
testQuery(title.eq("Jeeves").or(year.eq("1915")).and(author.eq("Michael Crichton")), "+(title:jeeves year:1915) +author:\"michael crichton\"", 0);
|
||||
testQuery(title.eq("Jeeves").or(rating.eq("Superb")).and(author.eq("Michael Crichton")), "+(title:jeeves rating:superb) +author:\"michael crichton\"", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -203,7 +226,7 @@ public class LuceneSerializerTest {
|
||||
|
||||
@Test
|
||||
public void eq_not_or_eq() throws Exception {
|
||||
testQuery(title.eq("House").not().or(year.eq("1990")), "(-title:house) year:1990", 1);
|
||||
testQuery(title.eq("House").not().or(rating.eq("Good")), "(-title:house) rating:good", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -213,7 +236,7 @@ public class LuceneSerializerTest {
|
||||
|
||||
@Test
|
||||
public void eq_and_eq_not_Does_Not_Find_Results_Because_Second_Expression_Finds_Nothing() throws Exception {
|
||||
testQuery(year.eq("1990").and(title.eq("House").not()), "+year:1990 +(-title:house)", 0);
|
||||
testQuery(rating.eq("Superb").and(title.eq("House").not()), "+rating:superb +(-title:house)", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -228,7 +251,7 @@ public class LuceneSerializerTest {
|
||||
|
||||
@Test
|
||||
public void ne_or_eq() throws Exception {
|
||||
testQuery(title.ne("Jurassic Park").or(year.eq("1954")), "(-title:\"jurassic park\") year:1954", 0);
|
||||
testQuery(title.ne("Jurassic Park").or(rating.eq("Lousy")), "(-title:\"jurassic park\") rating:lousy", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -281,6 +304,16 @@ public class LuceneSerializerTest {
|
||||
testQuery(title.between("Indiana", "Kundun"), "title:[indiana TO kundun]", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void between_Numeric_Integer() throws Exception {
|
||||
testQuery(year.between(1980, 2000), "year:[1980 TO 2000]", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void between_Numeric_Double() throws Exception {
|
||||
testQuery(gross.between(10.00, 19030.00), "gross:[10.0 TO 19030.0]", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void between_Phrase() throws Exception {
|
||||
testQuery(title.between("Jurassic Park", "Kundun"), "title:[jurassic TO kundun]", 1);
|
||||
@ -308,12 +341,163 @@ public class LuceneSerializerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void in() throws Exception {
|
||||
public void in() throws Exception {
|
||||
testQuery(title.in(Arrays.asList("Jurassic","Park")), "title:jurassic title:park", 1);
|
||||
testQuery(title.in("Jurassic","Park"), "title:jurassic title:park", 1);
|
||||
testQuery(title.eq("Jurassic").or(title.eq("Park")), "title:jurassic title:park", 1);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void lt() throws Exception {
|
||||
testQuery(rating.lt("Superb"), "rating:{* TO superb}", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lt_Numeric_Integer() throws Exception {
|
||||
testQuery(year.lt(1991), "year:{* TO 1991}", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lt_Numeric_Double() throws Exception {
|
||||
testQuery(gross.lt(10000.0), "gross:{* TO 10000.0}", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lt_Not_In_Range_Because_Equal() throws Exception {
|
||||
testQuery(rating.lt("Good"), "rating:{* TO good}", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lt_Numeric_Integer_Not_In_Range_Because_Equal() throws Exception {
|
||||
testQuery(year.lt(1990), "year:{* TO 1990}", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lt_Numeric_Double_Not_In_Range_Because_Equal() throws Exception {
|
||||
testQuery(gross.lt(900.0), "gross:{* TO 900.0}", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loe() throws Exception {
|
||||
testQuery(rating.loe("Superb"), "rating:[* TO superb]", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loe_Numeric_Integer() throws Exception {
|
||||
testQuery(year.loe(1991), "year:[* TO 1991]", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loe_Numeric_Double() throws Exception {
|
||||
testQuery(gross.loe(903.0), "gross:[* TO 903.0]", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loe_Equal() throws Exception {
|
||||
testQuery(rating.loe("Good"), "rating:[* TO good]", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loe_Numeric_Integer_Equal() throws Exception {
|
||||
testQuery(year.loe(1990), "year:[* TO 1990]", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loe_Numeric_Double_Equal() throws Exception {
|
||||
testQuery(gross.loe(900.0), "gross:[* TO 900.0]", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loe_Not_Found() throws Exception {
|
||||
testQuery(rating.loe("Bad"), "rating:[* TO bad]", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loe_Numeric_Integer_Not_Found() throws Exception {
|
||||
testQuery(year.loe(1989), "year:[* TO 1989]", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loe_Numeric_Double_Not_Found() throws Exception {
|
||||
testQuery(gross.loe(899.9), "gross:[* TO 899.9]", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void gt() throws Exception {
|
||||
testQuery(rating.gt("Bad"), "rating:{bad TO *}", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void gt_Numeric_Integer() throws Exception {
|
||||
testQuery(year.gt(1989), "year:{1989 TO *}", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void gt_Numeric_Double() throws Exception {
|
||||
testQuery(gross.gt(100.00), "gross:{100.0 TO *}", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void gt_Not_In_Range_Because_Equal() throws Exception {
|
||||
testQuery(rating.gt("Good"), "rating:{good TO *}", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void gt_Numeric_Integer_Not_In_Range_Because_Equal() throws Exception {
|
||||
testQuery(year.gt(1990), "year:{1990 TO *}", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void gt_Numeric_Double_Not_In_Range_Because_Equal() throws Exception {
|
||||
testQuery(gross.gt(900.00), "gross:{900.0 TO *}", 0);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void ge() throws Exception {
|
||||
testQuery(rating.goe("Bad"), "rating:[bad TO *]", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void goe_Numeric_Integer() throws Exception {
|
||||
testQuery(year.goe(1989), "year:[1989 TO *]", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void goe_Numeric_Double() throws Exception {
|
||||
testQuery(gross.goe(320.50), "gross:[320.5 TO *]", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void goe_Equal() throws Exception {
|
||||
testQuery(rating.goe("Good"), "rating:[good TO *]", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void goe_Numeric_Integer_Equal() throws Exception {
|
||||
testQuery(year.goe(1990), "year:[1990 TO *]", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void goe_Numeric_Double_Equal() throws Exception {
|
||||
testQuery(gross.goe(900.00), "gross:[900.0 TO *]", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void goe_Not_Found() throws Exception {
|
||||
testQuery(rating.goe("Hood"), "rating:[hood TO *]", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void goe_Numeric_Integer_Not_Found() throws Exception {
|
||||
testQuery(year.goe(1991), "year:[1991 TO *]", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void goe_Numeric_Double_Not_Found() throws Exception {
|
||||
testQuery(gross.goe(900.10), "gross:[900.1 TO *]", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void fuzzy() throws Exception {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user