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:
Vesa Martilla 2010-05-17 13:58:33 +00:00
parent 18235ec5de
commit 0fdb8d63fe
3 changed files with 385 additions and 111 deletions

View File

@ -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;
}
}

View File

@ -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());

View File

@ -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 {